Updating object and relations with Entity Framework Code First and ASP.Net MVC - asp.net-mvc-3

I'm using Entity Framework Code First and whilst I have working code, I'm having to make what are strictly unnecessary database calls in order to process the following update.
I have a simple POCO class for an album with a collection of related tags:
public class Album
{
public int Id { get; set; }
public string Title { get; set; }
public decimal Price { get; set; }
public virtual IList<Tag> Tags { get; private set; }
}
public class Tag
{
public int Id { get; set; }
public string Name { get; set; }
}
This is updated via an MVC form - with the tags represented by a series of check-boxes.
So when I get to my Update method in the respository, I have an album class populated with a list of tags - with in theory all I need to make the update.
However the only way I could find to get the list of tags to update (to delete any that were previously set but are now unchecked, and to add any that are currently checked) was to retrieve the original Album from the context and update it.
And secondly because in my implementation the Name field of the Tag is marked with [Required], and that in my Album object populated from the form I only have the IDs of the tags, I also have to retrieve each tag before updating.
Here's my code:
public void Update(Album album)
{
var albumToUpdate = GetById(album.Id); // - need to retrieve album with tags in order to update tags
albumToUpdate.Title = album.Title;
albumToUpdate.Price = album.Price;
albumToUpdate.Tags.Clear();
if (album.Tags != null)
{
foreach (var tag in album.Tags)
{
var tagToAdd = context.Tags.Find(tag.Id); // - need to retrieve full details of tag so doesn't fail validation
albumToUpdate.AddTag(tagToAdd);
}
}
}
Appreciate any thoughts as to how I could accomodate this with fewer database hits. It's not a major deal for this particular function (part of a site admin tool) but would like to know I'm doing things the best way.

Your approach - reloading the entity graph from the database and merge the changes manually into it - is correct in my opinion and the best you can do.
Forget for a moment that you use Entity Framework. What would you do if you had to write SQL statements manually? (EF is a wrapper around a SQL statement generator.) You get posted back an object graph - an Album with a list of Tags. How would you decide now which tags you have to write an INSERT, which tags a DELETE and which tags an UPDATE statement for? (I assume that your relationship between Album and Tag is many-to-many, so you write into a join table.) If you don't know the original state in the database you can't decide. Does the tag relation exist in the database or not? You have to query the database to find the answer, no matter if you use EF or direct SQL.
I see only two alternatives:
Track the entity changes yourself. For you MVC web application it would mean that you have to store the original state with the former GET request somewhere, for example in a session state or in hidden input fields in the page. With the POST request you can retrieve then the original state, build and attach the orginal graph and merge changes into it.
Write a Stored Procedure which takes the album and tag collection and let the SP do the work to create the appropriate SQL statements.
The first way is complicated and has its costs in HTTP payload (hidden input fields) or is depending on a fragile session state. And the second conflicts with why you are using an ORM. Unless you have really serious performance problems or are a SQL master I would not consider a Stored Procedure.

Firstly, I think that this pattern of updates is wrong somehow in that instead of passing in an Album which I assume is a replica or partial replica of the one you want to update (same ID at least), why don't you load the actual one first and apply your changes to it?
If you cannot do that, it might be less confusing to not pass in the same entity (Album) but instead use a data transfer object (DTO) or other message with just the fields you need and then apply that to the loaded Album.
As to the main problem of how to avoid loading each tag, EF should do that for you, but I don't know that it does. For example, NHibernate will not load a lazy entity if you are only setting a relationship because you have not touched any properties of Tag, so it only needs the Id to use it. Hopefully, EF does the same but maybe not (I'm assuming you've profiled it).
If EF does not behave like that you could try two things: firstly, so long as there is no cascade update on Tag, use a skeleton one with just the ID (that is, create the object yourself and just set the Id); this won't work if EF cascade updates the Tag. Secondly, you could implement your own cache for Tags and get them from memory.

Related

Entity Framework - ObjectContext or DbContext when AuditLog and CreatedOn/ModifiedOn and DB Modeling Team

Which one to choose either ObjectContext or DbContext, when the requirements are,
Data Modeling done by Modeler and provides the DEV team a sql
script. Due to this we have opted Model First. Is this a correct
choice ?
Existing denormalized db will be migrated to new db created by
modeler
Need to maintain audit log for all the updates, at the field level,
from the UI
Each table has CreatedBy, CreatedOn, ModifiedBy, ModifiedOn. These
fields should be automatically filled by during
context.SaveChanges().
If you're starting a new app, just use DbContext. You can always drill down to ObjectContext if you need to.
If you prefer no designer, you can use Code First with Migrations and create a SQL Script via update-database -script as well.
Sounds like a task for the DBAs?
field by field changes..If this is a disconnected app, you'll be better off handling that outside of EF (IMHO)
you can easily override SaveChanges for this. You said in a tweet that you have the dbcontext book. There's an example of this where we do this using a base class. However if you are going to use model first, be sure to avoid this problem : http://msdn.microsoft.com/en-us/magazine/jj553510.aspx
Thanks a lot Julie for your super quick response. You are The-EF-Angel.
I have read your MSDN article on Logging in EF.
To your reponse:
1. As a mandate, We need to use sql scripts provided by our Modeler to create our db. Also these scripts will be keep changing(With addition of new tables & update of exising schema) for each sprints. Hope DataFirst Model is fine. Whenever new we get new sql scripts, we plan to recreate the DB and update our EDMX. Do you see any issues with this approach ?
2. Ya we have a migration specialist for this task. I justed pointed that in question as an FYI.
3. We use MVC app and for field by field changes in audit log table, we planned to let EF decide what fields have changed(using change tracking ideas from your book) and capture that info into a DTO(we borrow your repository ideas from the course "EF in enterprise course" you authored in PS). And push that DTO into our messaging infrastructure and that will insert the audit logs to the DB.
Is this fine ? Do you foresee any issues ?
4. As you pointed out, we could change our interface for our needs by referring to your MSDN article and there "Figure 3 Setting the Interface’s DateCreated Property During SaveChanges"
I plan to use,
public interface ITheBaseType
{
DateTime DateCreated { get; set; }
DateTime DateModified { get; set; }
string CreatedBy { get; set; }
string ModifiedBy { get; set; }
}

How to prevent user from modifying some fields on the form?

I am using MVC3 and EF4 to write a web application. I am using an action header like below to capture the form values submitted by the user.
<HttpPost()>
Public Function Edit(ByVal prod as Product) As ActionResult
I use the below code for updating the record.
db.Attach(prod)
db.ObjectStateManager.ChangeObjectState(prod, EntityState.Modified)
db.SaveChanges()
I get the submitted values in prod object which I update in the database. The problem is that there are some users who are not allowed to modify certain fields in a Product, say ProductCost. I have disabled the textboxes for such fields in the HTML. But since it is clientside, the user can easily enable it using some tool like Firebug and modify the value.
The only solution I could come up was to retrieve the existing record from the database and copy its ProductCost value into prod.ProductCost. But I don't like firing a query for achieving this. Is there a better way to achieve this?
Edit: I found the below link to update particular fields. How to update only one field using Entity Framework?
You can use the below code to modify a particular field.
context.ObjectStateManager.GetObjectStateEntry(user).SetModifiedProperty("FieldName");
Now the question is do I have to write the above statement for every field the user is able to modify? If yes, suppose the Product model has 10 fields (1 primary key) and the user is allowed to modify all of them except the primary key, I need to write 9 statements?? Is there a method where you can specify multiple properties at once. Or even better something where you specify the properties which are not modified. (Note: I know I can run a loop over an array of field names to avoid writing 9 statements. I am asking for an alternative method and not refactoring the above)
Never trust client data. Always have your server code to validate the input and do appropriate actions.
I would create separate overloads of my Respiratory method update the product in different ways and then check what is the current user's access type, If he is admin, i will call the overload which updates everything, if he is a manager, i will call the method which updates name,imageUrl and price and if he is an employee, i will call the method which updates only name and ImageURL
[HttpPost]
public ActionResult Edit(Product prod)
{
if(ModelState.IsValid)
{
string userType=GetCurrentUserTypeFromSomeWhere();
if(userType=="admin")
{
repo.UpdateProduct(prod);
}
else if(userType=="manager")
{
repo.UpdateProduct(prod.ID, prod.Name, prod.ImageUrl, prod.Price);
}
else if(userType=="employee")
{
repo.UpdateProduct(prod.ID, prod.Name, prod.ImageUrl);
}
return RedirectToAction("Updated",new {id=prod.ID});
}
}

MVC3, Models, Create & Edit Hidden Fields

I have a few models in my MVC3 web app that have fields that need to be set "behind the scenes" when a user creates or edits an object/entity.
I'm trying to figure out what the best practice is regarding these types of fields.
For example...
public class EntityA {
public int Id { get; set; }
public string Title { get; set; }
...
[ForeignKey("User")]
public int UpdatedBy_Id { get; set; }
public virtual User UpdatedBy { get; set; }
}
The create and edit views for this allow the user to edit the "Title" field, but the "UpdatedBy" field needs to be set by the app when the entity is inserted or updated.
Is it best to drop a hidden field on the views and set "UpdatedBy_Id" there, or use the model property "get/set" body to do so? ...or... Should this be on the HttpPost in the controller?
This is where DTOs (Data Transfer Objects) come in handy.
Your view uses a DTO as it's model. The DTO mirrors your entity object in terms of properties, but excludes properties which you don't want the user to be able to manipulate.
Then in your controller when you are ready to persist the Entity, you create a new Entity object, and take the properties from the DTO passed to the action and copy them to your Entity object. It is at this point you can set the UpdatedBy property.
To make life easier when mapping properties from the Entity to the DTO (and vice versa), you can look at AutoMapper, which will handle this automatically, if you use the same names for your properties.
If you just pass the Entity to the view, there is the potential for the user to change the values of properties that you don't want them to be able to.
I'd prefer to place fields like this outside of user control. Especially if they're integer fields a user can edit to make phony records. The choices then fall between using TempData(if session is enabled) or possibly retrieving it on the fly for the current user. If you're not worried about the user modifying them, then I'd go with a simple hidden field or placing it in the route values for the post, allowing the framework to do the work for you.
I'd say use a hidden field and set the UpdatedBy_Id. It will then be posted back with the form and it can be databound like the rest of the information.

how can get data from another Table

I am designing a project in asp.net mvc3, i have designed my database in sql server, add i am using ado.net.
This is my controller action
public ViewResult ProductFormulationIndex()
{
return View(db.ProductFormulation.ToList());
}
means i want to display all fields of ProductFormulation table.
this is my table:-
and this is my productCategory Table
in my ProductFormulationIndex.cshtml i want to display Code of ProductCategory Table, not only id. So what should i do in controller or in Model for it ?
you may suggest tutorial related to it.
Thanks in advance.
You need a view model which is specifically designed for the view.
When defining your view models you shouldn't be thinking in terms of tables. SQL tables have absolutely no meaning in a view. Think in terms of what information you need to show and define your view models accordingly.
Therefore, You can define a view model like:
public class ProductInformation
{
...
public string CategoryCode {get; set;}
...
}
Or public Category ProductCategory.
You can use AutoMapper to convert between your real models and the view model you have defined.
You can find a good tutorial at http://weblogs.asp.net/shijuvarghese/archive/2010/02/01/view-model-pattern-and-automapper-in-asp-net-mvc-applications.aspx
Although I may not answer your question you are touching on some interesting points.
I subscribe to the school of thought that says one should not query an object model but rather make use of a denormalized lightweght query layer.
You will pobably quickly run into the lazy-loading and projection issue: i.e. you do not always require the related data and you do not always require all the fields. A query layer takes care of this quite nicely since using a denormalized model means that when you do your query you do not need to do N-number of joins to get the related data. You will still at some point need to gather up the data but you actual view queries will be order fo magnitude faster.
Also, getting all the data (by joining) for your denormalized model is a once-off affair as opposed to doing it each and every time you display the relevant data on the front-end.
Hope that makes sense :)

Using Lite Version of Entity in nHibernate Relations?

Is it a good idea to create a lighter version of an Entity in some cases just for performance reason pointing to same table but with fewer columns mapped. E.g If I have a Contact Table which has 50 Columns and in few of the related entities I might be interested in FirstName and LastName property is it a good idea to create a lightweight version of Contact table. E.g.
public class ContactLite
{
public int Id {get; set;}
public string FirstName {get; set;}
public string LastName {get; set;}
}
Also is it possible to map multiple classes to same table?
Don't map multiple classes to the same table. I tried this once and though it worked for what I was doing, I'm sure it would have bitten me later. It's better to use projections to populate the "light" classes.
It's not a good idea. Instead, always map the full class and create smaller ones that you can project on using Transformers.AliasToBean or LINQ.
An example of the latter:
var lightContacts = (from contact in session.Linq<Contact>()
where contact.Country = "Argentina"
select new LightContact
{
Id = contact.Id
FirstName = contact.FirstName,
LastName = contact.LastName
})
.ToList();
This will only select those three fields from the DB, even when filtering by a different one.
It's worth noting that, with LINQ, you could also use an anonymous type to select any projection you want without creating additional types or mappings.
I used this approach for handling an Entity without a BLOB-field (just for handling relations etc).
I had some issues regarding Implicit polymorphism, meaning I had this setup:
public class ImageWithData : Image
The inheritance made NHibernate load an ImageWithData in a second roundtrip every time I resolved an Image directly (not when related with BelongsTo or HasMany).
There is an option in NHibernate to disable this behaviour, called polymorphism="explicit" which you specify on your base-class (in my case, Image).
If it will be bad design in your case, I don't know, it all depends on why you need to lighten your entities.

Resources