2
votes

I know the title sounds like a duplicate of quite a few existing posts, but I've read quite a few of them and my situation is actually quite different. I would really appreciate it if anyone experienced with Entity Framework could offer some advice on the best architecture for the following scenario.

I have a wpf application with a 3-tier layout, Data Access Layer, Business Logic Layer, and UI Presentation Layer. The UI uses MVVM. DAL uses Entity Framework. UI and Data Access Layer each have their own models, UIModel and DataModel.

The current design uses a global DbContext for Entity Framework across the application. For a simple update operation, an entity is retrieved from the database as a DataModel, converted into a GUIModel, wired to the ViewModel and View for updates, and converted back into a DataModel to update in the database. And here's the problem: when the new DataModel is created from the conversion, it is no longer related to the original entity retrieved, and Entity Framework cannot perform the update because it now has two duplicate models of the same primary key attached to the same DbContext.

I did a little bit of research and found a couple of possible ways to fix this. One is to use a single model entity across all layers instead of separating GUIModel and DataModel, and break the global DbContext into unit of work. This seems to be a very common design, but my concerns with this approach is that the merge of GUIModel and DataModel violates the separation of responsibilities, and using unit of work required Business Layer to control the lifetime of DbContext, which also blurs the boundary between BLL and DAL.

The second alternative would be to use a local DbContext for every database query with a using block. This seems most memory efficient. But doing it this way makes lazy loading not possible, and eager loading all navigation properties in every query would likely affect performance. Also the short lived DbContexts require working completely in disconnected graph, which becomes quite complicated in terms of change tracking.

A third possibility would be to cache all original DataModels and update on those entities after the update.

I am new to Entity Framework and I'm sure there should be other ways to fix this issue too. I'll really appreciate it if anyone could offer some insights on the best way to approach this.

3

3 Answers

1
votes

Better approach is when you are going for update call in your repository firstly get the entity by primary key now you are in dbContext with required entity to be updated then assign updated fields and update the Context.

Here is code:

public void UpdateEntity(Entity updatedEntity)
    {
        using (var db = new DBEntities())
        {
            var entity = db.Entities.Find(updatedEntity.Id);
            if (entity!= null)
            {
                entity.Name = updatedEntity.Name;
                entity.Description = updatedEntity.Description;
                entity.LastModifiedBy = updatedEntity.LastModifiedBy;
                entity.Value = updatedEntity.Value;
                entity.LastModifiedOn = DateTime.Now;
                db.SaveChanges();
            }
        }
     }
0
votes

I would recommend using separate Business Objects as described in your second alternative. In a multi-tier scenario, you would create reusable objects that support your use case from the UI perspective, modelling the behavior of your business domain (as you call them "GUIModel"). Those models should focus on the behavior of your system and only contain the data needed to support this behavior. This is in direct contrast to entity classes that focus on data.

Example: Northwind Database, Customers Table. The entity would be a class containing all properties of a customer, probably having navigation properties to related things. Would you really want to use this model when you need to display a list of condensed customer information in the dropdown of an auto completion search box? Would you want to use the same model to display customers together with their aggregated invoice data in a grid? You would need to load all customer information together with related invoices to your presentation tier. You probably don't want to do that.

If you had different models for different use cases, things would make more sense from an object oriented point of view:

Class CustomerSearchResult: Id, Name. GetCustomerEdit method.

Class CustomerInvoiceInfo: Id, Name, Aggregated invoice values. GetCustomerEdit method.

Class CustomerEdit: All properties you want to display and edit, timestamp for optimistic concurrency checks. Change tracking logic, validation logic. Methods that model behavior that you need while editing a customer.

Class CustomerEntity: this is your data object that resembles the customers table. You use it as DTO to initialize the other objects from the database or push changes back into the database. You don't send it across the wire.

This way, when you get to the data access layer, you can put your DbContext into using blocks and respect the unit of work pattern. Of course, you will need to reflect changes made to the CustomerEdit instance by creating a new CustomerEntity from it and reattach it to the context as modified:

context.Entry(entity).State = EntityState.Modified;
context.SaveChanges();

This seems complex and burdensome at first, but actually, Entity Framework doesn't contain any magic that helps you much in a disconnected (n-tier) scenario. If you try using things like lazy loading or keeping DbContext instances open all the time, things get out of hand pretty fast.

If you're looking for a framework that helps in creating Business Objects and supports multi tier architectures, take a look into CSLA.net. Disclaimer: Many people here don't like it. It will make things worse if used wrong. Still, it helped me in some projects and I'm happy with it.

0
votes
  1. You can attach entity to an existing dbContext by using the following code, also here is a good post about entities states from MSDN
    var existingBlog = new Blog { BlogId = 1, Name = "ADO.NET Blog" }; 
    using (var context = new BloggingContext()) 
    { 
        context.Entry(existingBlog).State = EntityState.Modified; 
        // Do some more work...
    context.SaveChanges(); }
  2. Regarding 3-Tier, I would like to start by giving small description with .net context for each tier

    • Presentation, This is the layer that return the results to the use and it could be in the form of ASP.Net Website, Windows Forms, Web Api, WCF service or anything else
    • Business, this should include the domain model of your business, business logic and services that provide business across multiple domain entities
    • Data access/ persistence, This layer should include the logic to persist and retrieve the domain model into durable media such as DB, file system,...

    Generally the common issue here is which model goes into which layer for example should class X goes into presentation or business and I recommend an easy way to help you take your decision which is introducing new sibling layer so ask your self if you would like to build another presentation layer as console instead of windows will you copy and paste that logic into the new layer? if yes then there is good probability that your classes not in the right place.

Finally some concrete recommendations,

  • Keep each layer having its models as each layer has unique responsibility, also there are good frameworks that might help you in mapping between models such as AutoMapper
  • Don't transfer Entity Framework models across the layers as this will ruin the separation of concerns also it has more and more issues if you enabled lazy loading.
  • Try to avoid lazy loading unless you know what you are doing, one of the common pitfalls is Select N+1 and here is a good article describing it.
  • Also if you have a complex business, try to separate between querying the system and updating it by applying CQRS pattern, and there are some frameworks that can help you such as Dapper