Entity Framework POCO, Repository and Specification Pattern [Upgraded to EF 5]

[Updated on Nov 18 2012]: The source code now moved to github @ https://github.com/huyrua/efprs. Let start making pull requests yourself :)

I am happy to announce that the framework has been upgraded to target .NET 4.5, Entity Framework 5 and Visual Studio 2012. This post is also a response to someone asking me when should the framework being upgraded to Entity Framework 5.

Some notes regarding this upgrade:

- The source code is now restructured to two versions: one for .Net Framework 4.0, the other one for .Net Framework 4.5. You can access to the source code at http://ef4prs.googlecode.com/svn/trunk/ef4prs-read-only. However, the download page only shows specific versions for you to choose which version that best fits your need.

- Removed repository implementation against ObjectContext, this means the repository is now interfacing mainly with DbContext (not a Lab version anymore). So, if you prefer to work with DbContext, you can get the framework up and running quickly. On the other hand, if you want to work with ObjectContext, this is also easy by upgrading/migrating code from .NET 4.0 version.

That’s it! Not much changes from this upgrade, but that’s a good sign to me since EF 5 does not break some low level code.

Download links http://code.google.com/p/ef4prs/downloads/list

Cheers.

About these ads

Posted on September 16, 2012, in Entity Framework and tagged . Bookmark the permalink. 38 Comments.

  1. I’ve been following your articles for a while now, great work!

  2. Huyrua, thank you very much for all your effort and for upgrading your code to EF5. I’m following you for improving my projects. Actually I have to implement an abstract generic repository based on your code. Something sort of IGenericRepository and GenericRepositoty where T are the classes generated by a DatabaseFirst EF Model, in order to be able to deal with some CRUD and repetitive catalogs operations within an MVC application and then using some dependency injection for generics, in my case OpenGenerics from Autofac (code.google.com/p/autofac/wiki/OpenGenerics) and save some logic and code. My question is how should I transform both the interface and the concrete class in order to achieve it using Generics? Thank you in advance for your advices.

  3. Have you considered moving your library from code.google.com to github to make it easier for others to submit pull requests?

  4. Thanks for good article. My question is :
    How can I implement IoC container?

  5. How would this code be modified to manage objectcontext lifetime? If you have two instances of a program using this code running on two different computers and each one is making periodic changes to the DB, the caching that is done will prevent the changes that are persisted in the DB from the other program appearing since the cache is read instead of refreshing from the DB.

    If i change the code in:
    public IQueryable GetQuery() where TEntity : class

    to:

    IQueryable ThisQuery = ((IObjectContextAdapter)DbContext).ObjectContext.CreateQuery(entityName);
    System.Data.Objects.ObjectQuery objectQuery = (System.Data.Objects.ObjectQuery)ThisQuery;
    objectQuery.MergeOption = System.Data.Objects.MergeOption.OverwriteChanges;

    return ThisQuery;

    Then the data is ALWAYS refreshed from the DB so any persisted changes by other running copies of the software is read. However with lazy loading enabled if you try to access any information from joined tables the data is still old from the cache.

    I read that you want to keep objectcontext around only as long as you need it, since creating a new one will read all values from the DB. I do not want to do this because if I load an edit form I want the objectcontext around to track the changes through binding that are being made. If I did dispose of the objectcontext then errors are thrown indicating this when the user tries to save their changes on a long running edit form.

    Is there a way to force a complete refresh for all data when running a query and avoid using the cache when lazy loading is enabled?

    Another problem I am running into is that I have one instance of this software running to edit data, and another instance to read data from the DB and populate a text file. If I read the data and populate the text file, then edit the data in the database using my edit instance and save the changes, the second read and populating of the text file will read the original old cached data, unless I close and restart the program. I do not see in the source any method or way of clearing the objectcontext once a generic repository is created, and there is no dispose method on the repository.

    • Dru,

      IMO, this is a concurrency problem, and the solution is out of scope of this data access layer, I think. There have been many solutions to solve this out there. My experience is to use a column, say RevisionNumber for that entity, when one instance save that entity, it’s RevisionNumber is increased, when other instance wants to save the same entity, it should check whether the current RevisionNumber is the same as the one in the database (yes, we might need to load that entity from database again before saving!), if true, then save the entity, otherwise, prompt user that there are changes on the entity from the database and provide some options like: 1) Save and lose any previous change 2) Discard change user make, re-load the data and edit again.

      • Thanks for the reply, I see what you are saying there and it does make sense. I guess my question may not be entirely related to concurrency though.

        Lets go back to just a single instance of an application using this generic repository framework. What I find happens is that once I start up a generic repository accessing a table in the DB that the objectcontext is never cleared for the lifetime of the application. With the dbcontext manager I could not find a way to dispose and start over with a new dbcontext/objectcontext/cache when I am done with a particular dialog.

        I am still looking into it but do you know of anything that can be done so that you can use something like repository.ClearCache to reset the context?

        I see there is:
        DbContextManager.CloseAllDbContexts();
        but this appears to close the connection to the database and does not clear any cache or context.

        I guess it is something to try out but if I call:
        DbContextManager.Current.Dispose();
        or DbContextManager.CurrentFor(“key”).Dispose();
        I wonder if when i try to use the repository it will create a new DbContext automatically, which it looks like the code tries to do, and then this will be a fresh instance with a clear cache.

        I guess the summary of my question is that I am trying to add a way for me to tell the generic repository that I want to close and clear a DbContext and start fresh, like if the user closes an edit screen with a grid and then opens it again. Otherwise the memory usage will just keep climbing since the cache keeps growing as queries are made.

  6. Hi Dru,

    Here’s one idea you can try…

    1. In DbContextManager, add a method like:

    public static void CleanupStorage()
    {
    if (Storage != null) { Storage.Cleanup(); }
    }

    2. And then in DbContextStorage, implement it simply as:

    public void Cleanup() { Storage.Clear(); }

    (As you know, Storage is simply a Dictionary of string, DbContext.)

    3. Or, if you are using WebDbContextStorage, something like:

    if (HttpContext.Current != null) { HttpContext.Current.Items.Remove(StorageKey); }

    4. And finally, in global.asax of your web application, call DbContextManager.CleanupStorage() (the new method above) in the Application_EndRequest event.

    Because the actual entry for the DbContext has been removed – but the DbContextBuilder entry remains – on next request, it will be invoked and will create a new DbContext for you.

    This should ensure that your DbContext for the current database is created on each request and doesn’t “accumulate” unwanted state tracking entries.

    To further speed things along, you may also want to consider caching the results of intermediate steps in DbContextBuilder.BuildDbContext, such as the compiled model, i.e. this: dbModel.Compile(). (Not sure though if EF5 already does that for you.)

    AR

  7. Sorry, just noticed that the WebDbContextStorage already takes care of context cleanup:

    app.EndRequest += (sender, args) =>
    {
    DbContextManager.CloseAllDbContexts();
    HttpContext.Current.Items.Remove(STORAGE_KEY);
    };

    So, no need to call this yourself in a web scenario.

    AR

  8. Hi Huyrua ,
    How easy it is to use SQL Migration with your framework?

  9. Hello!

    I’m using the 4.0 EF version, and now i’m facing a problem with Many To Many relationships.

    I have like: Article — ArticleTag — Tag

    The problem is when i try to update an Article Entity, changing the Tags.

    It simple doesn’t save my changes, wherever they are.

    What should i do?

    Someone can help?

    Thanks!

  10. Hi Huyrua,

    I have a question that I was hoping to can help. I created the OrderRepository class and CustomerRepository class and both derived from the GenericRepository class but then realized that in order to persist both the Customer and Order entities in the same transaction, I would have to use the UnitOfWork from either CustomerRepository or OrderRepository or an instance of the GenericRepository. Is this how it should work? Thanks.

    • Hi Andy,

      Yes, [Repository].UnitOfWork.SaveChanges() will persist entity.

      • So it sounds like I only need one repository for each of the dbContext that I have? Is there a way to span the transaction across multiple databases? Thanks in advance.

      • So it sounds like I only need one repository for each of the dbContext that I have << Yes, the generic repository is built for it: we only need one to access to db, although you can subclass it to work with specific entity (EmployeeRepository, OrderRepository, etc.)

        Regarding multiple databases, by design, you can create many instances of Repository, each works with a specific database, by passing the connectionString (or an instance of DbContext/ObjectContext which connects to that db) to Repository constructor. Every time you want to persist entity from a repository which works with a specific db, you call [Repository instance].UnitOfWork.SaveChanges();

        If you want to span the transaction across multiple databases, I think you might need to use TransactionScope

    • Thanks for the quick response, huyrua. I understand it better now. BTW, great work!

  11. Hi Huyrua,
    I have a question, how to implement save parent with its child with poco based on the below thread.
    http://stackoverflow.com/questions/7968598/entity-4-1-updating-an-existing-parent-entity-with-new-child-entities/7969372#7969372

    Thanks

    • Assume parent-child is Order-LineItem, try this:

      order.LineItems.Clear();

      order.LineItems.Add(new Order { Price=XX, Quantity=1,… });
      order.LineItems.Add(new Order { Price=XY, Quantity=2,… });

      repository.Update[Order](order);
      repository.UnitOfWork.SaveChanges();

  12. How would you go about adding a generic auditing capabilty to this? The ObjectContext.SavingChanges Event is wrapped up inside the factory class…

  13. Instead of:

    string entitySetName = ((IObjectContextAdapter)DbContext).ObjectContext.MetadataWorkspace.GetEntityContainer(((IObjectContextAdapter)DbContext).ObjectContext.DefaultContainerName, DataSpace.CSpace).BaseEntitySets.Where(bes => bes.ElementType.Name == typeof(TEntity).Name).First().Name;

    Could you do:
    Type entitySetType = typeof(DbSet);
    var entitySetName = DbContext.GetType().GetProperties().First(x => x.PropertyType == entitySetType).Name;

  14. The brackets don’t show => typeof(DbSet[bracket]TEntity[braket])

  15. Hi huyrua,
    I have a dumb question. How would I use the WebDbContextStorage class? My thought is that I would have to add code to the Application_BeginRequest() method as follows:
    DbContextManager.InitStorage(new WebDbContextStorage());
    DbContextManager.Init(“DefaultDb”, new[] { “Infrastructure.Tests” }, true);

    • Hi Andy,

      You can initialize WebDbContextStorage within Global.ascx,cs as below:

      private WebDbContextStorage _storage;
      public override void Init()
      {
      base.Init();
      _storage = new WebDbContextStorage(this);
      }

      protected void Application_BeginRequest(object sender, EventArgs e)
      {
      DbContextInitializer.Instance().InitializeDbContextOnce(() =>
      {
      DbContextManager.InitStorage(_storage);
      DbContextManager.Init(new[] { Server.MapPath(“~/bin/Infrastructure.Tests.dll”) }, false);
      });
      }

      You can replace Infrastructure.Tests.dll with the assembly that contains mapping classes, you can also specify multiple mapping assemblies because the init method accept an array.

      HTH.

  16. Hi huyrua,
    Regarding Transaction, i have this case. I want to add a document then get last document.Id inserted to use it as foreign key in document history.
    as far as i know , i can’t get last inserted id without savechanges().
    savechanges can’t be invoked while i am inside transaction.

    genericRepositroy.UnitOfWork.BeginTransaction();
    genericRepositroy.Add(document);
    // int id = document.Id;
    //documentHistory.Fk_document_id = id;
    genericRepositroy.Add(documentHistory);
    genericRepositroy.UnitOfWork.CommitTransaction();

  17. Is it possible to use SQL Migration feature with your framework?

  18. Hello anh Huy, this is very helpful. I have a question: Is it possible to filter or limits the child items when using Include()? Thank you.

  19. Dear huyrua I am newbie for EF ı need fedback from multiple joins For example ı have user ,
    user has roles roles contains menu menus has forms I want to get forms via multiple joins please help :)

  20. Thanks for this great project!
    I implemented Besnik.GenericRepository first, but its to heavy.
    Your project is much more flexable and adds much fewer unneeded abstraction.

  21. Dear A.Huy, Everybody,

    The framework is so cool. But I face a problem with ObjectContext when mapping a entity modified(add or delete property in model). Could you show me how to add more fields(ex. add more field to customer entity to map to database) when developing once there are some data in database(table customer)?

    if (!ctx.DatabaseExists())
    {
    ctx.CreateDatabase();
    }
    else if (_recreateDatabaseIfExists)
    {
    ctx.DeleteDatabase();
    ctx.CreateDatabase();
    }
    }

    Thanks,
    Truong

  22. Hi Guys, What about If I want to select only 2 colums.can you please help me?

  23. Do you have any advice on strategies for multithreading implementations of GenericRepository? It would seem that the static nature of DBContextManager and ObjectContextManager present some challenges if you try to use in multi-threading environments such as building a WCF DAL.

  24. great post and explanation, just wanted to ask, in the EF5 version of your Repo pattern impl what is the significance of IUnitOfWork ? with the contextmanager class isn’t the need for UOW is removed?
    regards

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Connecting to %s

Follow

Get every new post delivered to your Inbox.

Join 51 other followers

%d bloggers like this: