Closed louislewis2 closed 2 years ago
@louislewis2 it sounds like you probably want to specify which properties are modified, rather than using the "just mark everything as modified" behavior of Update
. EF will only send an UPDATE
statement for the properties that are modified. Here is how you do that:
context.Entry(entityToUpdate).Property("PropertyToMarkAsModified").IsModified = true;
Thanks @rowanmiller I managed to implement this shortly after I created the issue (work must go on :) ) I think though this as a feature would still be greatly loved by many. I would even settle for an attribute that performs in essence the same as the code above. Thanks for the reply and thanks to the team for a great new EF!
@louislewis2 there actually is a flag on our lower level metadata to setup what you want:
modelBuilder.Entity<ContactDetail>()
.Property(c => c.Account)
.Metadata.IsReadonlyAfterSave = true;
We may promote this to the top level Fluent API if feedback says folks want it often.
The name of the property from a South African language point of view is quite puzzling, at first glance is looks like it is saying that it will become read only after a save. Perhaps I am missing something but in my understanding having it look like this makes more sense (to me).
modelBuilder.Entity<ContactDetail>()
.Property(c => c.Account)
.Metadata.IsReadonly = true;
For a feature like the above, how many times can I vote? :)
@louislewis2 that is exactly what it means... when you initially create the entity you can assign a value to the property and have it persisted to the database. At that point EF assumes the value of the property will never change, and thus it will never be marked as updated. Is that what you want?
@rowanmiller Not quite, I am looking for always read only, as in EF will never try to persist a value. Imagine the following scenario.
public class AccountCore
{
public int Id { get; set; } //Primary Key (RO)
public string AccountCode { get; set;} // Uniquely identifies a record (RO)
public string AccountName {get; set;} // (RW)
public string AccountDescription { get; set; } //(RW)
public double Debits { get; set; } //(RO)
public double Credits { get; set; } //(RO)
}
To make it easy for customers via REST, we allows them to load a record for example GET /api/accounts/1 (Using Id) GET /api/accounts/acc100 (Using AccountCode)
On a post, the customer can edit the RW fields, few in the sample, but in practice could be many. So currently we have to use the api that you showed above by using the EntityEntry. A bit simpler when dealing only with Update vs UpdateRange which does not give back a EntityEntry, so one has to resort to manually calling Update over a foreach loop for example.
In the above example, we might be expecting a POST object like this
{ accountName: "account 1", accountDescription: "account for testing" }
From that object we simply new up an instance of the class, set the values and call Update.
Once the models start getting larger, the amount of times we need to set the property IsModified = false quickly gets out of hand and requires extremely careful testing, to ensure that no RO properties are being updated.
Working against a system that has more than 1300 database objects, tables and views, I think one can quickly see how having to manually remember to set each field IsModiifed = false quickly becomes a real world issue. Whilst it may be true that for so many models, that setting the IsModified to false, is hard either way around, having it dispersed throughout the code base, makes it that much harder to work with and track.
A lot of our work is integration based and very often the systems we integrate with have been around for many years, and have interesting database designs, it is not uncommon for us to see tables have either close to or more than 100 columns. So what often happens is that a EF model is creating to make sense of a smaller picture of the actual underlying data. 1 database table, may end up with 10 or more EF models, which in itself is interesting because we cant map them in the same context (future issue :) ), and most of our work lies in the financial sector, where certain fields must be read only.
so once it is defined as read only in the model (hopeful), there is no chance that EF will ever even generate TSQL code for the property / field when performing an update. I think read only and query only can be interchangeably used.
I hope this helps to understand the use case better.
@louislewis2 it sounds like there are two things you are asking about:
I think one can quickly see how having to manually remember to set each field IsModiifed = false quickly becomes a real world issue.
Could you just do it the other way around and mark IsModified = true
for the fields that are being updated by the current request? There is a weakly-typed string overload of Property()
, so you can write general purpose helper methods for it.
So once it is defined as read only in the model (hopeful), there is no chance that EF will ever even generate TSQL code for the property / field when performing an update.
This is exactly what the IsReadonlyAfterSave
flag does - it will only ever be included in the initial INSERT
statement and never in an UPDATE
. BTW if the value is generated in the database and does not need to be included in the initial INSERT
, then there is also the IsReadonlyBeforeSave
flag.
@rowanmiller Sorry for the delay, I will over the next few days be working specifically with the code that pertains to this discussion. I will try out your suggestions and get back to you here.
Is the Metadata going to get promoted to fluent API? Or is there anyway we can use it at the moment.
We also have a database field that we want to be able to query and never set, so read-only seems like the ideal solution.
@fatmanmclone90 there isn't a "nice" API for it, but you can set it using annotations on the Fluent API - http://docs.efproject.net/en/latest/modeling/backing-field.html.
@ajcvickers to make sure we have the right issues to track read only scenarios
Sometimes when creating models that represents a subset of a table, it feel normal to add in fields which we can use to query and identify a particular record. Consider in a example where in an accounting system the rest api provides two ways in which a ContactDetail object can be returned. One way is a primary key (int) and the other account (string). So in the model we add those two fields for querying.
Then when saving we don't want to load the entire record, so we create an instance based on the properties from the post, set the primary key and ask the ef context to update. This works where the values are updated as expected, but the account field is then updated to null.
what we are looking for is way to say ef, this is a read only property, don't create any sql for it when updating to the database.
The current workarounds involve making a call to the database before hand, getting the value of the account, setting that on the item we are about to update and the calling update. the other way is to load the item from the database, set the values we intend to change and then call update. But these workarounds all involve more trips to the database. Working on databases which generally average between 200 and 300 Gb, performance is key and getting creative with reads and updates is a must. If there is another way of achieving this, I would gladly be open to it.
(PS: Thanks for a great EF 7 so far :) )