ASP.NET MVC / EF4 / POCO / Repository - How to Update Relationships? - asp.net-mvc

I have a 1..* relationship between Review and Recommendations.
The relevant portion of my model (which is also the POCO mapped by EF4):
public class Review
{
public ICollection<Recommendations> Recommendations { get; set; }
}
On an Edit View, i represent the Recommendations as a set of checkboxes.
When i try and add a new Recommendation as part of editing the Review (e.g check another box), nothing is happening - and i know why...
I use the "stub technique" to update my entities - e.g i create a entity with the same key, attach it to the graph, then do ApplyCurrentValues. But this only works for scalar properties, not for navigational properties.
I found this StackOverflow question which looks good, but i am trying to work out how to get this to work with POCO's/Repository (and ASP.NET MVC - detached context).
As i'm using POCO's, review.Recommendations is an ICollection<Recommendation>, so i can't do review.Recommendations.Attach. I'm not using Self-Tracking Entities either, so i need to manually work with the graph/change tracking - which hasn't been a problem until now.
So you can visualize the scenario:
Review:
Recommendations (ICollection<Recommendation>):
RecommendationOne (Recommendation)
RecommendationTwo (Recommendation)
If im on the edit view, two of the checkboxes are already checked. The third one (representing RecommendationThree) is unchecked.
But if i check that box, the above model becomes:
Review:
Recommendations (ICollection<Recommendation>):
RecommendationOne (Recommendation)
RecommendationTwo (Recommendation)
RecommendationThree (Recommendation)
And so i need to attach RecommendationThree to the graph as a new entity.
Do i need hidden fields to compare the posted data the existing entity? Or should i store the entity in TempData and compare that to the posted entity?
EDIT
To avoid confusion, here is the full app stack call:
ReviewController
[HttpPost]
public ActionResult Edit(Review review)
{
_service.Update(review); // UserContentService
_unitOfWork.Commit();
}
UserContentService
public void Update<TPost>(TPost post) where TPost : Post, new()
{
_repository.Update(post); // GenericRepository<Post>
}
GenericRepository - used as GenericRepository<Post>
public void Update<T2>(T2 entity) where T2 : class, new()
{
// create stub entity based on entity key, attach to graph.
// override scalar values
CurrentContext.ApplyCurrentValues(CurrentEntitySet, entity);
}
So, the Update (or Add or Delete) Repository methods needs to be called for each recommendation, depending it's new/modified/deleted.

I've accepted #jfar's answer because he put me on the right track, but thought i'd add an answer here for other people's benefit.
The reason the relationships were not getting updated is for the following reasons:
1) Completely disconnected scenario. ASP.NET = stateless, new context newed up each HTTP request.
2) Edited entity created by MVC (model binding), but not existing in graph.
3) When using POCO's with no change tracking, performing .Attach on an entity will add it to the graph, but the entity and any child relationships will be Unchanged.
4) I use the stub entity trick and ApplyCurrentValues to update the entity, but this only works for scalar properties, not navigational ones.
So - in order to get the above to work, i would have to explicity set the EntityState for the object (which happens automatically because of ApplyCurrentValues), and also the navigational properties.
And there is the problem - how do i know if the navigational property was added/modified/deleted? I have no object to compare to - only a entity which i know was "edited", but i don't know what was edited.
So the solution in the end was this:
[HttpPost]
public ActionResult Edit(Review review)
{
var existingReview = _service.FindById(review.Id); // review is now in graph.
TryUpdateModel(existingReview); // MVC equivalent of "ApplyCurrentValues" - but works for ALL properties - including navigationals
_unitOfWork.Commit(); // save changed
}
That's it. I don't even need my _service.Update method - as i don't need the stub trick anymore - because the review is in the graph with the retrieval, and ApplyCurrentValues is replaced by TryUpdateModel.
Now of course - this is not a concurrency-proof solution.
If i load the Review Edit View, and before i click "Submit" someone else changes the Review, my changes could be lost.
Fortunately i have a "last-in-wins" concurrency mode, so it's not an issue for me.
I love POCO's, but man are they a pain when you have the combination of a stateless environment (MVC) and no change tracking.

Working with detached object graphs is my favorite drawback of EF. Simply pain in the ass. First you have to deal with it at your own. EF will not help you with it. It means that in addition to Review you also have to send some information about made changes. When you attach Review to context it sets Review all Recommendation and all relations to Unchanged state. ApplyCurrentValues works only for scalar values as you have already found. So you have to use your additional information about made changes and set state of relations to Added by using ObjectContext.ObjectStateManager.ChangeRelationshipState.
I personaly gave up with this approach and I'm loading object graph from DB first merging my changes into attached graph and save it.
I answered similar question more deeply here.

Perhaps I need more context but whats wrong with:
recommendations.Add(newRecomendation)
?
In reply to comment:
Ok so whats wrong with
SomeServiceOrRepository.AddNewRecommendation( newRecommendation )
or
SomeServiceOrRepository.AddNewRecommendation( int parentId, newRecommendation )
Last Sentence? You mean the two questions?
This shouldn't be hard at all.
To summarize my answer I think you are doing things "the hard way" and really should focus on posting form values that correspond to the CRUD action your trying to accomplish.
If a new entity could come in at the same time as your edited entities you should really prefix them differently so the model binder can pick up on it. Even if you have multiple new items you can use the same [0] syntax just prefix the "name" field with New or something.
A lot of times in this scenario you can't rely on Entity Frameworks graph features because removing an entity from a collection never means it should be set for deletion.
If the form is immutable you could also try using the generized attach function off of ObjectSet:
theContect.ObjectSet<Review>().Attach( review )
Tons of ways out of this. Maybe you could post your controller and view code?

Related

Update relationships when saving changes of EF4 POCO objects

Entity Framework 4, POCO objects and ASP.Net MVC2. I have a many to many relationship, lets say between BlogPost and Tag entities. This means that in my T4 generated POCO BlogPost class I have:
public virtual ICollection<Tag> Tags {
// getter and setter with the magic FixupCollection
}
private ICollection<Tag> _tags;
I ask for a BlogPost and the related Tags from an instance of the ObjectContext and send it to another layer (View in the MVC application). Later I get back the updated BlogPost with changed properties and changed relationships. For example it had tags "A" "B" and "C", and the new tags are "C" and "D". In my particular example there are no new Tags and the properties of the Tags never change, so the only thing which should be saved is the changed relationships. Now I need to save this in another ObjectContext. (Update: Now I tried to do in the same context instance and also failed.)
The problem: I can't make it save the relationships properly. I tried everything I found:
Controller.UpdateModel and Controller.TryUpdateModel don't work.
Getting the old BlogPost from the context then modifying the collection doesn't work. (with different methods from the next point)
This probably would work, but I hope this is just a workaround, not the solution :(.
Tried Attach/Add/ChangeObjectState functions for BlogPost and/or Tags in every possible combinations. Failed.
This looks like what I need, but it doesn't work (I tried to fix it, but can't for my problem).
Tried ChangeState/Add/Attach/... the relationship objects of the context. Failed.
"Doesn't work" means in most cases that I worked on the given "solution" until it produces no errors and saves at least the properties of BlogPost. What happens with the relationships varies: usually Tags are added again to the Tag table with new PKs and the saved BlogPost references those and not the original ones. Of course the returned Tags have PKs, and before the save/update methods I check the PKs and they are equal to the ones in the database so probably EF thinks that they are new objects and those PKs are the temp ones.
A problem I know about and might make it impossible to find an automated simple solution: When a POCO object's collection is changed, that should happen by the above mentioned virtual collection property, because then the FixupCollection trick will update the reverse references on the other end of the many-to-many relationship. However when a View "returns" an updated BlogPost object, that didn't happen. This means that maybe there is no simple solution to my problem, but that would make me very sad and I would hate the EF4-POCO-MVC triumph :(. Also that would mean that EF can't do this in the MVC environment whichever EF4 object types are used :(. I think the snapshot based change tracking should find out that the changed BlogPost has relationships to Tags with existing PKs.
Btw: I think the same problem happens with one-to-many relations (google and my colleague say so). I will give it a try at home, but even if that works that doesn't help me in my six many-to-many relationships in my app :(.
Let's try it this way:
Attach BlogPost to context. After attaching object to context the state of the object, all related objects and all relations is set to Unchanged.
Use context.ObjectStateManager.ChangeObjectState to set your BlogPost to Modified
Iterate through Tag collection
Use context.ObjectStateManager.ChangeRelationshipState to set state for relation between current Tag and BlogPost.
SaveChanges
Edit:
I guess one of my comments gave you false hope that EF will do the merge for you. I played a lot with this problem and my conclusion says EF will not do this for you. I think you have also found my question on MSDN. In reality there is plenty of such questions on the Internet. The problem is that it is not clearly stated how to deal with this scenario. So lets have a look on the problem:
Problem background
EF needs to track changes on entities so that persistance knows which records have to be updated, inserted or deleted. The problem is that it is ObjectContext responsibility to track changes. ObjectContext is able to track changes only for attached entities. Entities which are created outside the ObjectContext are not tracked at all.
Problem description
Based on above description we can clearly state that EF is more suitable for connected scenarios where entity is always attached to context - typical for WinForm application. Web applications requires disconnected scenario where context is closed after request processing and entity content is passed as HTTP response to the client. Next HTTP request provides modified content of the entity which has to be recreated, attached to new context and persisted. Recreation usually happends outside of the context scope (layered architecture with persistance ignorace).
Solution
So how to deal with such disconnected scenario? When using POCO classes we have 3 ways to deal with change tracking:
Snapshot - requires same context = useless for disconnected scenario
Dynamic tracking proxies - requires same context = useless for disconnected scenario
Manual synchronization.
Manual synchronization on single entity is easy task. You just need to attach entity and call AddObject for inserting, DeleteObject for deleting or set state in ObjectStateManager to Modified for updating. The real pain comes when you have to deal with object graph instead of single entity. This pain is even worse when you have to deal with independent associations (those that don't use Foreign Key property) and many to many relations. In that case you have to manually synchronize each entity in object graph but also each relation in object graph.
Manual synchronization is proposed as solution by MSDN documentation: Attaching and Detaching objects says:
Objects are attached to the object
context in an Unchanged state. If you
need to change the state of an object
or the relationship because you know
that your object was modified in
detached state, use one of the
following methods.
Mentioned methods are ChangeObjectState and ChangeRelationshipState of ObjectStateManager = manual change tracking. Similar proposal is in other MSDN documentation article: Defining and Managing Relationships says:
If you are working with disconnected
objects you must manually manage the
synchronization.
Moreover there is blog post related to EF v1 which criticise exactly this behavior of EF.
Reason for solution
EF has many "helpful" operations and settings like Refresh, Load, ApplyCurrentValues, ApplyOriginalValues, MergeOption etc. But by my investigation all these features work only for single entity and affects only scalar preperties (= not navigation properties and relations). I rather not test this methods with complex types nested in entity.
Other proposed solution
Instead of real Merge functionality EF team provides something called Self Tracking Entities (STE) which don't solve the problem. First of all STE works only if same instance is used for whole processing. In web application it is not the case unless you store instance in view state or session. Due to that I'm very unhappy from using EF and I'm going to check features of NHibernate. First observation says that NHibernate perhaps has such functionality.
Conclusion
I will end up this assumptions with single link to another related question on MSDN forum. Check Zeeshan Hirani's answer. He is author of Entity Framework 4.0 Recipes. If he says that automatic merge of object graphs is not supported, I believe him.
But still there is possibility that I'm completely wrong and some automatic merge functionality exists in EF.
Edit 2:
As you can see this was already added to MS Connect as suggestion in 2007. MS has closed it as something to be done in next version but actually nothing had been done to improve this gap except STE.
I have a solution to the problem that was described above by Ladislav. I have created an extension method for the DbContext which will automatically perform the add/update/delete's based on a diff of the provided graph and persisted graph.
At present using the Entity Framework you will need to perform the updates of the contacts manually, check if each contact is new and add, check if updated and edit, check if removed then delete it from the database. Once you have to do this for a few different aggregates in a large system you start to realize there must be a better, more generic way.
Please take a look and see if it can help http://refactorthis.wordpress.com/2012/12/11/introducing-graphdiff-for-entity-framework-code-first-allowing-automated-updates-of-a-graph-of-detached-entities/
You can go straight to the code here https://github.com/refactorthis/GraphDiff
I know it's late for the OP but since this is a very common issue I posted this in case it serves someone else.
I've been toying around with this issue and I think I got a fairly simple solution,
what I do is:
Save main object (Blogs for example) by setting its state to Modified.
Query the database for the updated object including the collections I need to update.
Query and convert .ToList() the entities I want my collection to include.
Update the main object's collection(s) to the List I got from step 3.
SaveChanges();
In the following example "dataobj" and "_categories" are the parameters received by my controller "dataobj" is my main object, and "_categories" is an IEnumerable containing the IDs of the categories the user selected in the view.
db.Entry(dataobj).State = EntityState.Modified;
db.SaveChanges();
dataobj = db.ServiceTypes.Include(x => x.Categories).Single(x => x.Id == dataobj.Id);
var it = _categories != null ? db.Categories.Where(x => _categories.Contains(x.Id)).ToList() : null;
dataobj.Categories = it;
db.SaveChanges();
It even works for multiple relations
The Entity Framework team is aware that this is a usability issue and plans to address it post-EF6.
From the Entity Framework team:
This is a usability issue that we are aware of and is something we have been thinking about and plan to do more work on post-EF6. I have created this work item to track the issue: http://entityframework.codeplex.com/workitem/864 The work item also contains a link to the user voice item for this--I encourage you to vote for it if you have not done so already.
If this impacts you, vote for the feature at
http://entityframework.codeplex.com/workitem/864
All of the answers were great to explain the problem, but none of them really solved the problem for me.
I found that if I didn't use the relationship in the parent entity but just added and removed the child entities everything worked just fine.
Sorry for the VB but that is what the project I am working in is written in.
The parent entity "Report" has a one to many relationship to "ReportRole" and has the property "ReportRoles". The new roles are passed in by a comma separated string from an Ajax call.
The first line will remove all the child entities, and if I used "report.ReportRoles.Remove(f)" instead of the "db.ReportRoles.Remove(f)" I would get the error.
report.ReportRoles.ToList.ForEach(Function(f) db.ReportRoles.Remove(f))
Dim newRoles = If(String.IsNullOrEmpty(model.RolesString), New String() {}, model.RolesString.Split(","))
newRoles.ToList.ForEach(Function(f) db.ReportRoles.Add(New ReportRole With {.ReportId = report.Id, .AspNetRoleId = f}))

ASP.NET MVC save new record verse update existing record conventions

I'm working on my first ASP.NET MVC (beta for version 3) application (using EF4) and I'm struggling a bit with some of the conventions around saving a new record and updating an existing one. I am using the standard route mapping.
When the user goes to the page /session/Evaluate they can enter a new record and save it. I have an action defined like this:
[ActionName("Evaluate")]
[AcceptVerbs(HttpVerbs.Post)]
public ActionResult EvaluateSave(EvaluteSessionViewModel evaluatedSession)
{
}
When they save I grab an entity off the view model and attach it to my context and save. So far, so good. Now I want the user to be able to edit this record via the url /session/Evaluate/1 where '1' is the record ID.
Edit: I have my EF entity attached as a property to the View Model.
If I add an overloaded method, like this (so I can retrieve the '1' portion automatically).
[ActionName("Evaluate")]
[AcceptVerbs(HttpVerbs.Post)]
public ActionResult EvaluateSave(ID, EvaluteSessionViewModel evaluatedSession)
{
}
I get an "The current request for action 'Evaluate' on controller type 'SessionsController' is ambiguous between the following action" error. I'm not sure why they're ambiguous since they look unique to me.
I decided that I was just going to skip over this issue for now and see if I could get it to update an existing record, so I commented out the EvaluateSave that didn't have the ID parameter.
What I'd like to do is this:
// Load the original entity from EF
// Rebind the postback so that the values posted update the entity
// Save the result
Since the entity is populated as the parameter (evaluatedSession) the rebinding is happening too soon. But as I look at the approach I'd like to take I realized that it opens my code up to hacking (since a user could add in fields into the posted back page and these could override the values I set in the entity).
So it seems I'm left with having to manually check each field to see if it has changed and if it has, update it. Something like this:
if (evaluatedSession.MyEntity.myField <> savedSession.myField)
savedSession.myField = evaluatedSession.MyEntity.myField;
Or, save a copy of the entity and make sure none of the non-user editable ones have changed. Yuck.
So two questions:
First: how do I disambiguate the overloaded methods?
Second: is there a better way of handling updating a previously saved record?
Edit: I guess I could use something like Automapper...
Edit 9/22/2010 - OK, it looks like this is supposed to work with a combination of two items: you can control what fields bind (and specifically exclude some of them) via the [Bind(Exclude="field1,field2")] attribute either on the class level or as part of the method doing the saving, ex.
public ActionResult EvaluateSave([Bind(Exclude="field1")] EvaluateSessionViewModel evaluatedSession)
From the EF side of things you are supposed to be able to use the ApplyCurrentValues() method from the context, ex.
context.ApplyCurrentValues(savedEval.EntityKey.EntitySetName, evaluatedSession);
Of course, that doesn't appear to work for me. I keep getting "An object with a key that matches the key of the supplied object could not be found in the ObjectStateManager. Verify that the key values of the supplied object match the key values of the object to which changes must be applied.".
I tried attaching the original entity that I had just loaded, just in case it wasn't attached to the context for some reason (before ApplyCurrentValues):
context.AttachTo(savedEval.EntityKey.EntitySetName, savedEval);
It still fails. I'm guessing it has something to do with the type of EF entity object MVC creates (perhaps it's not filled in enough for EF4 to do anything with it?). I had hoped to enable .NET framework stepping to walk through it to see what it was attempting to do, but it appears EF4 isn't part of the deal. I looked at it with Reflector but it's a little hard for me to visualize what is happening.
Well, the way it works is you can only have one method name per httpverb. So the easiest way is to create a new action name. Something like "Create" for new records and "Edit" for existing records.
You can use the AntiForgeryToken ( http://msdn.microsoft.com/en-us/library/dd492767.aspx ) to validate the data. It doesn't stop all attempts at hacking but it's an added benefit.
Additional
The reason you can only have one action name per httpverb is because the model binders only attempt to model bind and really aren't type specific. If you had two methods with the same action name and two different types of parameters it can't just try and find the best match because your intent might be clearly one thing while the program only sees some sort of best match. For instance, your might have a parameter Id and a model that contains a property Id and it might not know which one you intend to use.

Some issues about Rob Conery's repository pattern

Please read my update at the end of question after reading the answers:
I'm trying to apply repository pattern
as Rob Conery's described on
his blog under "MVC Storefront".
But I want to ask about some issues
that I had before I apply this design
pattern.
Rob made his own "Model" and used some
ORM "LINQ to SQL or Entity Framework (EF)" to map his database to
Entities.
Then he used custom Repositories which
gives IQueryable<myModel> and in
these repositories he made sort of
Mapping or "Parsing" between ORM Entities and his Model classes.
What I'm asking here:
Is it possible to make custom mapping between ORM Entities and my
model "classes" and load just
properties that I want? I hope
the point is clear.
Update For POCO
**
This is what I decided after many of suggestions and many of tries:
**
After all and with respect to Mr. Rob Conery's opinion I've got better solution as:
I built my model as "POCOs" and put them in my "Models Layers" so they had nothing to do with the "edmx" file.
Built my repositories to deal with this "POCO" model dependent on "DbContext"
Then I created a "ViewModels" to get just the information that needed by view from those repositories.
So I do not need to add one more layer to be between "EF Models" and "My Model". I just twist my model a little and force EF to deal with it.
As I see this pattern is better than Rob Conery's one.
Yes, it's possible if you're using LINQ to SQL. All you need to do is use projection to pull out the data you want into an object of your choosing. You don't need all this decoration with interfaces and whatnot - if you use a model specific to a view (which it sounds like you need) - create a ViewModel class.
Let's call it ProductSummaryView:
public class ProductSummaryView{
public string Name {get;set;}
public decimal Price {get;set;}
}
Now load it from the repository:
var products= from p in _repository.GetAllProducts
where p.Price > 100
select new ProductSummaryView {
Name=p.ProductName,
Price=p.Price
}
This will pull all products where the price > 100 and return an IQueryable. In addition, since you're only asking for two columns, only two columns will be specified in the SQL call.
Not a dodge to your question, but it's ultimately up to you to decide how your repository would work.
The high-level premise is that your controller would point to some repository interface, say IRepository<T> where T : IProduct. The implementation of which could do any number of things---load up your whole database from disk and store in memory and then parse LINQ expressions to return stuff. Or it could just return a fixed set of dummy data for testing purposes. Because you're banging away on an repository interface, then you could have any number of concrete implementations.
Now, if you're looking for a critique of Rob's specific implementation, I'm not sure that's germane to Stack Overflow.
While it's possible to populate part of an object based on a query of a subset of the columns for that object using a query (which has nothing to do with the repository pattern), that's not how things are "normally" done.
If you want to return a subset of an object, you generally create a new class with just that subset of properties. This is often (in the MVC world view) referred to as a View Model class. Then, you use a projection query to fill that new class.
You can do all of that whether you are using the repository pattern or not. I would argue there is no conflicting overlap between the two concepts.
DeferringTheLoad
Remember that IQueryable defers all the loading up to the last responsible moment. You probably won't have to load all the data using the LINQ operators to get the data you want. ; )
Respecting the dependency in domain classes in views, I will say NO. Use a ViewModel pattern for this. It's more maintainable; you could use AutoMapper to avoid the mapping problems, and they are very flexible in composite views scenarios : )
According to the new question...The answer is yes, you can. Just as Rob Conery says, use projection ; ):
var query = from p in DataContext.Persons}
select new Persons
{
firstname = p.firstname,
lastname = p.lastname
});

Is it good to use a static EF object context in an MVC application for better perf?

Let's start with this basic scenario:
I have a bunch of Tables that are essentially rarely changed Enums (e.g. GeoLocations, Category, etc.) I want to load these into my EF ObjectContext so that I can assign them to entities that reference them as FK. These objects are also used to populate all sorts of dropdown controls. Pretty standard scenarios so far.
Since a new controller is created for each page request in MVC, a new entity context is created and these "enum" objects are loaded repeatedly. I thought about using a static context object across all instances of controllers (or repository object).
But will this require too much locking and therefore actually worsen perf?
Alternatively, I'm thinking of using a static context only for read-only tables. But since entities that reference them must be in the same context anyway, this isn't any different from the above.
I also don't want to get into the business of attaching/detaching these enum objects. Since I believe once I attach a static enum object to an entity, I can't attach it again to another entity??
Please help, I'm quite new to EF + MVC, so am wondering what is the best approach.
Personally, I never have any static Context stuff, etc. For me, when i call the database (CRUD) I use that context for that single transaction/unit of work.
So in this case, what you're suggesting is that you wish to retrieve some data from the databse .. and this data is .. more or less .. read only and doesn't change / static.
Lookup data is a great example of this.
So your Categories never change. Your GeoLocations never change, also.
I would not worry about this concept on the database/persistence level, but on the application level. So, just forget that this data is static/readonly etc.. and just get it. Then, when you're in your application (ie. ASP.NET web MVC controller method or in the global.asax code) THEN you should cache this ... on the UI layer.
If you're doing a nice n-tiered MVC app, which contains
UI layer
Services / Business Logic Layer
Persistence / Database data layer
Then I would cache this in the Middle Tier .. which is called by the UI Layer (ie. the MVC Controller Action .. eg. public void Index())
I think it's important to know how to seperate your concerns .. and the database stuff is should just be that -> CRUD'ish stuff and some unique stored procs when required. Don't worry about caching data, etc. Keep this layer as light as possible and as simple as possible.
Then, your middle Tier (if it exists) or your top tier should worry about what to do with this data -> in this case, cache it because it's very static.
I've implemented something similar using Linq2SQL by retrieving these 'lookup tables' as lists on app startup and storing them in ASP's caching mechanism. By using the ASP cache, I don't have to worry about threading/locking etc. Not sure why you'd need to attach them to a context, something like that could easily be retrieved if necessary via the table PK id.
I believe this is as much a question of what to cache as how. When your are dealing with EF, you can quickly run into problems when you try to persist EF objects across different contexts and attempt to detach/attach those objects. If you are using your own POCO objects with custom t4 templates then this isn't an issue, but if you are using vanilla EF then you will want to create POCO objects for your cache.
For most simple lookup items (i.e numeric primary key and string text description), you can use Dictionary. If you have multiple fields you need to pass and back with the UI then you can build a more complete object model. Since these will be POCO objects they can then be persisted pretty much anywhere and any way you like. I recommend using caching logic outside of your MVC application such that you can easily mock the caching activity for testing. If you have multiple lists you need to cache, you can put them all in one container class that looks something like this:
public class MyCacheContainer
{
public Dictionary<int, string> GeoLocations { get; set; }
public List<Category> Categories { get; set; }
}
The next question is do you really need these objects in your entity model at all. Chances are all you really need are the primary keys (i.e. you create a dropdown list using the keys and values from the dictionary and just post the ID). Therefore you could potentially handle all of the lookups to the textual description in the construction of your view models. That could look something like this:
MyEntityObject item = Context.MyEntityObjects.FirstOrDefault(i => i.Id == id);
MyCacheContainer cache = CacheFactory.GetCache();
MyViewModel model = new MyViewModel { Item = item, GeoLocationDescription = GeoLocations[item.GeoLocationId] };
If you absolutely must have those objects in your context (i.e. if there are referential entities that tie 2 or more other tables together), you can pass that cache container into your data access layer so it can do the proper lookups.
As for assigning "valid" entities, in .Net 4 you can just set the foreign key properties and don't have to actually attach an object (technically you can do this in 3.5, but it requires magic strings to set the keys). If you are using 3.5, you might just try something like this:
myItem.Category = Context.Categories.FirstOrDefault(c => c.id == id);
While this isn't the most elegant solution and does require an extra roundtrip to the DB to get a category you don't really need, it works. Doing a single record lookup based on a primary key should not really be that big of a hit especially if the table is small like the type of lookup data you are talking about.
If you are stuck with 3.5 and don't want to make that extra round trip and you want to go the magic string route, just make sure you use some type of static resource and/or code generator for your magic strings so you don't fat finger them. There are many examples here that show how do assign a new EntityKey to a reference without going to the DB so I won't go into that on this question.

When to use a ViewModel rather than a Model?

I have a business model called Customer which has many required properties (via DataAnnotations) and other validation rules.
I have a View which is meant to allow editing of the customer's address fields.
The problem I have is that I want a strongly-typed view but I can't get away with using the Customer type here. Since the view will only be editing address data it won't return any of the other required data the Customer object would need in order to validate.
This suggests that I should use a ViewModel. However, there are many business rules that apply to the address related properties on Customer that I would have to duplicate on the new ViewModel (address lengths, zipcodes, state formatting, etc). They need duplicated because the client-side validation (I'm using xVal) requires that information in order to function.
I feel I've reached a catch-22 scenario. DRY tells me that I should not duplicate my business rules on a ViewModel that my Model already has, but on the other hand I can't use the Model because it will never validate.
What is the best practice in this situation?
The chosen path
The solution I ultimately chose was the ViewModel path. In order to get the validation I needed to work there was simply no other practical way.
However, was was unable to eliminate some rough spots using the ViewModel brought up. I refactored some of my models to use interfaces containing the properties I knew would re-used in the ViewModels. Since the ViewModels could now use the same interfaces as the models it allowed me to do things like this:
public ActionResult Edit(AddressViewModel address)
{
if(!ModelState.IsValid)
return View();
var customer = Customer.Load(address.CustomerId);
UpdateModel<IAddress>(customer);
// more stuff ....
}
This saves me the step of using an automapper.
The answer I selected below (by Wyatt Barnett) I felt was good for most cases and I use it on other projects I have, especially useful with Linq-to-Sql.
I ran into the same issue with complex model classes not playing well with simpler views and model binding. I also happened to be using xVal. The trick I fell upon was to use Validation Buddies to cover the DRY angle for basic validation then use AutoMapper to push things back into the full-blown model classes. I can then run a second round of server-side validation to cover the more complex bits that require access to the database and such.
From a technical standpoing, your view should only talk to your ViewModel, not the model. So your viewmodel should delegate all the validation to the model. The ViewModel should be adding interaction layer stuff.
Of course this all falls apart in Silverlight, where you typically need some kind of quick validation done at the client side, so all of a sudden you're copying all your validation rules up to the ViewModel anyway. I haven't figured a way around that yet.

Resources