What is the best way to prevent updating on specific fields in Entity Framework - entity-framework

Im writing an web application with MVC using Entity Framework for my backend logic. My problem is that I have an entity that has certain fields that should never be changed on an update. I am not really sure what the best way to solve this problem would be. There is going to be a lot of data processed in my application, so I cant afford to just hack up a solution.
Is it possible to just define the fields as readonly in the POCO entities ? Or should I write and entity framework extension class that validates all updates. Could it be done in the mapping files between EF and the actual database?
I am relatively new with EF, so I hope some of you might be able to give me some pointers!
Thanks!

If you are using .NET 4.5 and EF 5 (i.e. MVC 4), you can simply set IsModified = false on the individual properties in question. This has the benefit of sticking close to the default out-of-the-box MVC conventions.
For example, if you have a CreatedBy field that shouldn't be touched when the record is updated, use the following in your controller:
[HttpPost]
public ActionResult Edit(Response response)
{
if (ModelState.IsValid)
{
db.Entry(response).State = EntityState.Modified;
db.Entry(response).Property(p => p.CreatedBy).IsModified = false;
db.SaveChanges();
return RedirectToAction("Index");
}
return View(response);
}
Note that the IsModified line is the only change from the default controller action.
You MUST put this line AFTER setting .State = EntityState.Modified (which applies to the record as a whole and adds the record into the db context).
The effect is that EF will not include this column in the SQL UPDATE statement.
I am still (very) shocked that there are no [InsertOnly] or [UpdateOnly] attributes similar to [ReadOnly]. This seems like a major oversight by the MVC team. Am I missing something?
I'm not fully satisfied with this solution because it's a hack: You're telling EF that no change was made when what you really mean to say is "HANDS OFF". It also means that you have to use this code anyplace where the field could be updated. It would be better to have an attribute on the class property.
(Apologies for posting to an older thread, but I've not see this solution anywhere else. ViewModels are robust but a lot of work, and EF was supposed to make things easier, not harder...)

Well I would advice against ever using the EF classes in the View. You're best bet is to construct ViewModel classes and use Automapper to map them from the EF classes.
When you are updating records in the database though, you can control which fields in the ViewModel are used to update the existing fields in the EF class.
The normal process would be:
Use the Id to get the latest version of the existing object out of the database.
If you are using optimistic concurrency control then check that the object has not been updated since the ViewModel was created (so check timestamp for example).
Update this object with the required fields from your ViewModel object.
Persist the updated object back to the database.
Update to include Automapper examples:
Let's say your POCO is
public class MyObject
{
public int Id {get;set;}
public string Field1 {get;set;}
public string Field2 {get;set;}
}
and Field1 is the field you don't want updating.
You should declare a view model with the same properties:
public class MyObjectModel
{
public int Id {get;set;}
public string Field1 {get;set;}
public string Field2 {get;set;}
}
and Automap between them in the constructor of your Controller.
Mapper.CreateMap<MyObject, MyObjectModel>();
you can if you wish (although I prefer to do this manually, automap the other way too:
Mapper.CreateMap<MyObjectModel, MyObject>().ForMember(dest=>dest.Field1, opt=>opt.Ignore());
When you are sending date to your website you would use:
var myObjectModelInstance = Mapper.Map<MyObject, MyObjectModel>(myObjectInstance);
to create the viewModel.
When saving the data, you'd probably want something like:
public JsonResult SaveMyObject(MyObjectModel myModel)
{
var poco = Mapper.Map<MyObjectModel, MyObject>(myModel);
if(myModel.Id == 0 )
{
//New object
poco.Field1 = myModel.Field1 //set Field1 for new creates only
}
}
although I'd probably remove the exclusion of Field1 above and do something like:
public JsonResult SaveMyObject(MyObjectModel myModel)
{
var poco;
if(myModel.Id == 0)
{
poco = Mapper.Map<MyObjectModel, MyObject>(myModel);
}
else
{
poco = myDataLayer.GetMyObjectById(myModel.Id);
poco.Field2 = myModel.Field2;
}
myDataLayer.SaveMyObject(poco);
}
note I believe that best-practise would have you never Automap FROM the ViewModel, but to always do this manually, including for new items.

I just asked a very similar question, and I believe the answer to that one may help out a lot of folks who stumble across this one as well. The OP mentions that these are fields that should never change, and using PropertySaveBehavior.Ignore ensures this. With the existing answers to this question, you need to make custom save methods or introduce mapping where it might not make sense. By setting the AfterSave property behavior instead, you can prevent this from being possible in EF altogether.
In my project, I am generically accessing a property that is on an abstract class so I have to set it like this:
MyProperty.SetAfterSaveBehavior(PropertySaveBehavior.Ignore);
If you're accessing it directly on a known class, you'd use this:
...
.Property(e => e.YourProperty)
.Metadata.SetAfterSaveBehavior(PropertySaveBehavior.Ignore);

Related

MVC 5 Edit with ViewModel by reusing EF Generated Code

I have a Business Model and an EditBusinessViewModel.
In MVC 4 I would use code something like this to edit a record:
[HttpPost]
public ActionResult Edit(MainMenu mainmenu)
{
if (ModelState.IsValid)
{
db.MainMenus.Attach(mainmenu);
db.ObjectStateManager.ChangeObjectState(mainmenu, EntityState.Modified);
db.SaveChanges();
return RedirectToAction("Index");
}
return View(mainmenu);
}
Now the auto generated code in MVC 5 looks like this, I've modified this Action to only include fields from my EditBusinessViewModel and named it Edit2:
[HttpPost]
[ValidateAntiForgeryToken]
[ValidateInput(false)]
public ActionResult Edit2([Bind(Include = "ID,BusinessName,BusinessDescription,BusinessAddress,BusinessPhoneOne,BusinessPhoneTwo,BusinessWeb,BusinessEmail,BusinessMelRef")] EditBusinessViewModel business)
{
if (ModelState.IsValid)
{
db.Entry(business).State = EntityState.Modified;
db.SaveChanges();
return Redirect("~/Home/Index/" + business.ID);
}
return View(business);
}
I have the Get part working, my Model and View are working by returning:
return View(new EditBusinessViewModel(business));
But when I post back, I get an error on this line:
db.Entry(business).State = EntityState.Modified;
The entity type EditBusinessViewModel is not part of the model for the current context. Which it is not and the reason for the ViewModel, I guess?
What I would like to know is can I use this code or is there something else I should be doing?
Update
I've been thinking about this too deeply and a ViewModel is just that, a ViewModel so now I have:
[HttpPost]
[ValidateAntiForgeryToken]
[ValidateInput(false)]
public ActionResult Edit2([Bind(Include = "ID,BusinessDescription,BusinessAddress,BusinessPhoneOne,BusinessPhoneTwo,BusinessWeb,BusinessEmail,BusinessMelRef")] EditBusinessViewModel business)
{
if (ModelState.IsValid)
{
business.UserEmail = User.Identity.GetUserName();
Business newbus = db.Businesses.Find(business.ID);
{
newbus.BusinessDescription = business.BusinessDescription;
newbus.BusinessAddress = business.BusinessAddress;
};
db.Entry(newbus).State = EntityState.Modified;
db.SaveChanges();
return Redirect("~/Home/Index/" + business.ID);
}
return View(business);
}
This way I post back the data I need from the view in the View Model, find the entity in the database by its matching ID and update it with the EF scaffold code.
Is there a better way?
Well, you won't be able to use your current code, for reasons I believe you're pointing to in your question itself. You're working with two different types, one that is mapped from a DB table and one that you are using specifically for your Views and is not mapped. Your Entity Model, you don't say which version of EF, but with MVC 5 I assume it's 6 or 6.1.
So you have your entity POCO generated by EF text template, and you have you ViewModel. Even if the properties were identical, EF would not take your ViewModel type, because it has no mapping definition in the edmx, this is the reason it says it's not in the current context as you've already recognized.
There are some decent ways to work in this system though. IF you desire to use separate entities and ViewModels, which I personally do in most of my own code. You could :
It seems like you have an ID, if that ID points to a unique ID on the EF Model, you could do a look up for an entity with that ID and then update the values of the entity with the values from your ViewModel and then save the entity with StateModified instead of the ViewModel.
If the properties are exactly the same or very similar, between your Model and ViewModel, you could look at something like AutoMapper, https://github.com/AutoMapper/AutoMapper, which would enable you to map your ViewModel directly to an instance of your entity Model Type.
If you're Model and ViewModel are vastly different, you could build a static tranform, not sure how many people do this but I like them. Essentially you define two static methods that enable you to convert your Model to your ViewModel and vice versa. The benefit is anywhere you need to do this you can call one method, and if the structure of either type changes you just have to update this in one location.
You say autogenerated code in MVC 5, You could mean just the default example code that comes with EF 5 but I think you're talking about MVC 5 Scaffolding. http://www.asp.net/visual-studio/overview/2013/aspnet-scaffolding-overview; if so, the code for these should not need much alteration at least not in the Controller side, unless you have specialty domain logic which it doesn't appear like you do. If you wanted to use separate ViewModels I suppose you could, in conjunction with one of the recommendations above, but the point of Scaffolding is to remove much of the plumbing that you have to do when exposing DB Models for basic CRUD methods.
If I've missed the mark on what you're looking for please reply in a comment. Also, it's a bit hard to provide code examples for the above recommendations without seeing the class definitions for your two models. I think the descriptions should be enough to go off of, if you think one will fit your use case well? But, if you'd like some simple code examples update your answer with the code for those classes and I can provide some.
From your posted snippet:
return View(new EditBusinessViewModel(business));
Here, business is not your ViewModel, but a variable (presumably your entity from the db) that is used in your ViewModel's constructor. I can only assume it's passed with the intent of storing it in one of your ViewModel's properties.
public ActionResult Edit2([Bind(Include = "...")] EditBusinessViewModel business)
Here, business is your ViewModel. It has the type EditBusinessViewModel, as you can see. But in that method you make the following call:
db.Entry(business).State = EntityState.Modified;
EditBusinessViewModel is not a type known by EF, since it is your viewmodel. You are supposed to pass your entity to the database. The ViewModel should only be used in your MVC project.
I'm pretty sure that one of the properties of your EditBusinessViewModel is the entity you need. This is vaguely confirmed by the fact that you pass your entity in the EditBusinessViewModel constructor.
I don't know what the property is called, since you didn't post the ViewModel's class. Assuming it's called MyEntity, this should do the trick:
db.Entry(business.MyEntity).State = EntityState.Modified;
But for clarity, I'd suggest renaming that parameter to prevent any confusion between separate uses of a business variable. Change it to businessVM or something similar so you're always reminded that you're working with a ViewModel, not an entity.

Do you need to load contents of Entity Framework collection before you .Add() to it?

I have an entity model with
class Package
{
...
virtual ICollection<Owner> Owners { get; set; }
}
and am implementing an AddOwner() operation.
Does the entity framework require me to retrieve my Package object including owners with .Include(p => p.Owners) in order for me to call p.Add(newOwner); and save changes back to the database?
Secondary question : Intuition isn't guiding me to an answer here because I don't know whether to base my intuition on CLR collections or on some principle of Entity Framework; is there a principle of EF that helps me understand things better than thinking in terms of CLR collections?
As long as your Package object is attached to a context, the Owners property will be set to an EntityCollection<Owner>. Regardless of whether it's loaded, changes through that property will be marked as changes in your context, and SaveChanges() will save those changes.
Keep in mind that your Owner object already needs to be part of the context, if it is an existing owner, though. If it is not, EF will assume you are trying to save a new Owner.
Owners will have to point to something concrete sooner or later, and certainly before you try adding anything to it. EF knows to look at this property through the ICollection interface.
You could do something such as:
var p = new Package { SomePackageProperty = "Some value",
Owners = new List<Owner>>() { new Owner { SomeOwnerProperty = "Some other value" } } };
yourContext.DbSet<Package>().Add(p);
But that's just a best guess, given the small snippet provided in the question.
I think you are thinking of including Owners .Include(p => p.Owners), and then saying p.Owners.Add(new_owner) to add a new Owner, which will work after .SaveChanges.

Full custom properties in EF

Using EF with Winforms in C#. I’d like to add full custom properties to our entities, using partial classes. All entities already have partial classes with validation stuff and some more so I’d just add the properties that I need. By full property I mean property with getter and setter so not just a computed/readonly property. I want to this mostly to get around working directly with some DB mapped properties which are badly designed or have other problems.
For example, one case would be like this:
// entity class, generated
public partial class Customer
{
public string Spot {get;set}
}
// partial class, manually changed
public partial class Customer
{
public int? xxxSpot
{ get { return Int32.Parse(Spot.Trim()); } // some code omitted
{ set { Spot = value.ToString().PadLeft(5); }
}
So my custom properties will be built around existing, DB mapped properties of the entity. I’d like to use these custom properties like normal ones, ie to bind them to UI controls and so on. I’ve tried one and so far it works great.
Is this a good idea? If not, why ? And what else should I consider when doing this?
You have answered your own question - it works and there is no reason why to not do that. If you want to improve design of your entities you can even try to change visibility of your mapped properties to ensure that other classes must use only your custom properties with additional logic.

Single Page Application, upshot.js, DbContext and DbDataController : Only Entity Models are supported?

When using the examples for Single Page Application, I've the following TodoItem controller:
public partial class MVC4TestController : DbDataController<MVC4TestContext>
{
public IQueryable<TodoItem> GetTodoItems()
{
return DbContext.TodoItems.OrderBy(t => t.TodoItemId);
}
}
Question 1:
It seems that only EntityModels are supported ?
When using a real ViewModel (model only used for the Views, not not used as 1:1 mapping to database entity), the DbDataController does not support this.
Also using Linq.Translations or PropertyTranslator does not seem to work, see this code extract:
private static readonly CompiledExpressionMap<TodoItem, string> fullExpression =
DefaultTranslationOf<TodoItem>.Property(t => t.Full).Is(t => t.Title + "_" + t.IsDone);
public string Full
{
get
{
return fullExpression.Evaluate(this);
}
}
Question 2:
What is the recommended design when using SPA, DBContext and ViewModels ?
As far as I know so far is - it instists on the usage of "real" model classes bound to DbContext.
I have the same problem as you - I need to use my own DTO objects which are "flat".
The Json serialisation is currently not able to serialize data which has parent references in child objects (cyclic references). Usually I do not need the entity tree anyways so I created smaller classes which fits perfectly to the view.
I tried to use a normal Controller with JsonResult and parsed the returned model into ko.mapping.fromJS after retrieved the data. Thats working fine. But - you have to take care of all the nice stuff the MVC4 generated viewmodels are already dealing with (like creating navigation, etc.).
Maybe someone finds a workaround to "fake" a DbContext with DTO data.

Entity Framework: Cancel a property change if no change in value

When setting a property on an entity object, it is saving the value to the database even if the value is exactly the same as it was before. Is there anyway to prevent this?
Example:
If I load a Movie object and the Title is "A", if I set the Title to "A" again and SaveChanges() I was hoping that I wouldn't see the UPDATE statement in SqlProfiler but I am. Is there anyway to stop this?
Yes, you can change this. Doing so isn't trivial, however, in the current version of the Entity Framework. It will become easier in the future.
The reason you're seeing this behavior is because of the default code generation for the entity model. Here is a representative example:
public global::System.Guid Id
{
get
{
return this._Id;
}
set
{
// always!
this.OnIdChanging(value);
this.ReportPropertyChanging("Id");
this._Id = global::System.Data.Objects.DataClasses
.StructuralObject.SetValidValue(value);
this.ReportPropertyChanged("Id");
this.OnIdChanged();
}
}
private global::System.Guid _Id;
partial void OnIdChanging(global::System.Guid value);
partial void OnIdChanged();
This default code generation is reasonable, because the Entity Framework doesn't know the semantics of how you intend to use the values. The types in the property may or may not be comparable, and even if they are, the framework can't know how you intend to use reference equality versus value equality in all cases. For certain value types like decimal, it's pretty clear, but in a general sense it's not obvious.
You, on the other hand, know your code, and can customize this some. The trouble is that this is generated code, so you can't just go in and edit it. You need to either take over the code generation, or make it unnecessary. So let's look at the three options.
Take over the code generation
The essential approach here is to create a T4 template which does the code behind, and that the default code generation from the Entity Framework. Here is one example. One advantage of this approach is that the Entity Framework will be moving to T4 generation in the next version, so your template will probably work well in future versions.
Eliminate code generation
The second approach would be to eliminate cogeneration altogether, and do your change tracking support manually, via IPOCO. Instead of changing how the code is generated, with this approach you don't do any code generation at all, and instead provide change tracking support to the Entity Framework by implementing several interfaces. See the linked post for more detail.
Wait
Another option is to live with the Entity Framework the way it is for the time being, and wait until the next release to get the behavior you desire. The next version of the Entity Framework will use T4 by default, so customizing the code generation will be very easy.
According to MSDN:
The state of an object is changed from
Unchanged to Modified whenever a
property setter is called. This occurs
even when the value being set is the
same as the current value. After the
AcceptAllChanges method is called, the
state is returned to Unchanged. By
default, AcceptAllChanges is called
during the SaveChanges operation.
Looks like you'll want to check the value of properties on your Entity objects before you update to prevent the UPDATE statement.
At a generic level, if your entities are implementing INotifyPropertyChanged, you don't want the PropertyChanged event firing if the value is the same. So each property looks like this :-
public decimal Value
{
get
{
return _value;
}
set
{
if (_value != value)
{
_value = value;
if (_propertyChanged != null) _propertyChanged(this, new PropertyChangedEventArgs("Value"));
}
}
}
Hope that's relevant to Entity Framework.
One thing you can do is just wrap the property yourself using a partial class file, and then use your property instead of the first one:
public sealed partial class MyEFType {
public string MyWrappedProperty {
get {
return MyProperty;
}
set {
if (value == MyProperty)
return;
MyProperty = value;
}
}
}
It wouldn't be very practical to do this to every property, but if you have a need to detect that a particular property has actually changed and not just been written to, something like this could work.