Geeks With Blogs
Sean's Blog My Development Blog

With .NET 4.0 right around the corner, I thought it would be cool to download Visual Studio 2010 beta 2 and start playing around with the next release of Entity Framework.

The initial release of Entity Framework came with a great deal of criticism. To make matters worse, there was a large uproar when it was rumored that Microsoft would be abandoning LINQ to SQL, in favor of Entity Framework. This was because, at the time, many developers felt Entity Framework was an inferior technology to LINQ to SQL. To set things right, Microsoft proactively moved forward with improving Entity Framework, in time for the 4.0 release of the .NET Framework. This is good news because my initial impressions, so far, have been nothing but positive.

One of the concerns that many developers had with the initial Entity Framework release was the difficulty in building a repository. Having said that, the first thing I did upon opening Visual Studio 2010 was to build one. Soon into my test project, I noticed some interesting things. The first thing that really caught my attention was the IObjectSet interface, which provides the ability to modify data objects. Every bit as important, the ObjectContext class now has a generic CreateObjectSet<TEntity>() method. As soon as I saw these two things, I realized the potential, not only for creating a repository, but for creating a generic repository. That’s right, one generic class that can perform all of the CRUD operations for your entire application.

To start out, I built a generic interface for our repository. Notice that with the exception of the two SaveChanges() methods, every method either takes an object of type T as a parameter, or returns an object (either a collection or a single entity), also of type T.

public interface IRepository<T> : IDisposable where T : class

{

    IQueryable<T> Fetch();

    IEnumerable<T> GetAll();

    IEnumerable<T> Find(Func<T, bool> predicate);

    T Single(Func<T, bool> predicate);

    T First(Func<T, bool> predicate);

    void Add(T entity);

    void Delete(T entity);

    void Attach(T entity);

    void SaveChanges();

    void SaveChanges(SaveOptions options);

}

 

Moving onward, implementing the interface was a breeze. Take note of the constructor and the call to CreateObjectSet() off of the global _context variable. This is the magic that makes the generic repository possible.

 

/// <summary>

/// A generic repository for working with data in the database

/// </summary>

/// <typeparam name="T">A POCO that represents an Entity Framework entity</typeparam>

public class DataRepository<T> : IRepository<T> where T : class

{

 

    /// <summary>

    /// The context object for the database

    /// </summary>

    private ObjectContext _context;

 

    /// <summary>

    /// The IObjectSet that represents the current entity.

    /// </summary>

    private IObjectSet<T> _objectSet;

 

    /// <summary>

    /// Initializes a new instance of the DataRepository class

    /// </summary>

    public DataRepository()

        : this(new AdventureWorksEntities())

    {

    }

 

    /// <summary>

    /// Initializes a new instance of the DataRepository class

    /// </summary>

    /// <param name="context">The Entity Framework ObjectContext</param>

    public DataRepository(ObjectContext context)

    {

        _context = context;

        _objectSet = _context.CreateObjectSet<T>();

    }

 

    /// <summary>

    /// Gets all records as an IQueryable

    /// </summary>

    /// <returns>An IQueryable object containing the results of the query</returns>

    public IQueryable<T> Fetch()

    {

        return _objectSet;

    }

 

    /// <summary>

    /// Gets all records as an IEnumberable

    /// </summary>

    /// <returns>An IEnumberable object containing the results of the query</returns>

    public IEnumerable<T> GetAll()

    {

        return GetQuery().AsEnumerable();

    }

 

    /// <summary>

    /// Finds a record with the specified criteria

    /// </summary>

    /// <param name="predicate">Criteria to match on</param>

    /// <returns>A collection containing the results of the query</returns>

    public IEnumerable<T> Find(Func<T, bool> predicate)

    {

        return _objectSet.Where<T>(predicate);

    }

 

    /// <summary>

    /// Gets a single record by the specified criteria (usually the unique identifier)

    /// </summary>

    /// <param name="predicate">Criteria to match on</param>

    /// <returns>A single record that matches the specified criteria</returns>

    public T Single(Func<T, bool> predicate)

    {

        return _objectSet.Single<T>(predicate);

    }

 

    /// <summary>

    /// The first record matching the specified criteria

    /// </summary>

    /// <param name="predicate">Criteria to match on</param>

    /// <returns>A single record containing the first record matching the specified criteria</returns>

    public T First(Func<T, bool> predicate)

    {

        return _objectSet.First<T>(predicate);

    }

 

    /// <summary>

    /// Deletes the specified entitiy

    /// </summary>

    /// <param name="entity">Entity to delete</param>

    /// <exception cref="ArgumentNullException"> if <paramref name="entity"/> is null</exception>

    public void Delete(T entity)

    {

        if (entity == null)

        {

            throw new ArgumentNullException("entity");

        }

 

        _objectSet.DeleteObject(entity);

    }

 

    /// <summary>

    /// Deletes records matching the specified criteria

    /// </summary>

    /// <param name="predicate">Criteria to match on</param>

    public void Delete(Func<T, bool> predicate)

    {

        IEnumerable<T> records = from x in _objectSet.Where<T>(predicate) select x;

 

        foreach (T record in records)

        {

            _objectSet.DeleteObject(record);

        }

    }

 

    /// <summary>

    /// Adds the specified entity

    /// </summary>

    /// <param name="entity">Entity to add</param>

    /// <exception cref="ArgumentNullException"> if <paramref name="entity"/> is null</exception>

    public void Add(T entity)

    {

        if (entity == null)

        {

            throw new ArgumentNullException("entity");

        }

 

        _objectSet.AddObject(entity);

    }

 

    /// <summary>

    /// Attaches the specified entity

    /// </summary>

    /// <param name="entity">Entity to attach</param>

    public void Attach(T entity)

    {

        _objectSet.Attach(entity);

    }

 

    /// <summary>

    /// Saves all context changes

    /// </summary>

    public void SaveChanges()

    {

        _context.SaveChanges();

    }

 

    /// <summary>

    /// Saves all context changes with the specified SaveOptions

    /// </summary>

    /// <param name="options">Options for saving the context</param>

    public void SaveChanges(SaveOptions options)

    {

        _context.SaveChanges(options);

    }

 

    /// <summary>

    /// Releases all resources used by the WarrantManagement.DataExtract.Dal.ReportDataBase

    /// </summary>

    public void Dispose()

    {

        Dispose(true);

        GC.SuppressFinalize(this);

    }

 

    /// <summary>

    /// Releases all resources used by the WarrantManagement.DataExtract.Dal.ReportDataBase

    /// </summary>

    /// <param name="disposing">A boolean value indicating whether or not to dispose managed resources</param>

    protected virtual void Dispose(bool disposing)

    {

        if (disposing)

        {

            if (_context != null)

            {

                _context.Dispose();

                _context = null;

            }

        }

    }

}

 

To use it, you need only instantiate the generic repository of whichever entity type you wish to query. For example, using Adventure Works, you could bind to a GridView on an ASP.NET web form, with the following:

 

using (IRepository<Employee> repository = new DataRepository<Employee>())

{

    this.GridView1.DataSource = from x in repository.Fetch()

                                select new

                                {

                                    x.MaritalStatus,

                                    x.Contact.FirstName,

                                    x.Contact.LastName,

                                    x.BirthDate

                                };

    this.GridView1.DataBind();

}

 

To change the table that you wish to query, simply change the type instantiated.

 

Conclusion

Hopefully, you have gotten a little insight into the power that the new Entity Framework has to offer. I really believe that Microsoft has made great progress in this release. For those that were turned off by eariler versions, I urge you to give Entity Framework another go with the 4.0 release.

Posted on Thursday, December 3, 2009 5:05 PM | Back to top


Comments on this post: Creating a Generic Entity Framework 4.0 Repository

# re: Creating a Generic Entity Framework 4.0 Repository
Requesting Gravatar...
Super article, thanks for this entry!
Left by BigJim on Dec 04, 2009 9:35 AM

# re: Creating a Generic Entity Framework 4.0 Repository
Requesting Gravatar...
Great Post ! Nicely writen, Thanks.
Left by Yoann .B on Dec 05, 2009 12:24 PM

# re: Creating a Generic Entity Framework 4.0 Repository
Requesting Gravatar...
Hi,

I've just wrote a post about my blog based on your Repository implementation for Caching usage with EF 4.

It's more a draft than a complete solution, but it's a beggining

You should take a look : http://blog.inetux.net/post/Entity-Framework-4-Series-Caching.aspx

You should use google translator to view it, it's written in french (my natural language) :)

Have a nice day!
Left by Yoann .B on Dec 06, 2009 12:03 PM

# re: Creating a Generic Entity Framework 4.0 Repository
Requesting Gravatar...
I think there's a slight issue with this implementation.

The problem being, if I had multiple poco objects, for instance, Repository<customer> and Repository<product> - you'd not be able to run queries across both, as they'd be created via different instances of your object context (You'd also use automatic fix-up, meaning you'd lose any level 1 caching).

I see you've provided

I think the best way around this, is to do something as follows:

public class ContextFactory : ObjectContext
{
private Dictionary<Type, object> contexts = new Dictionary<Type, object>();

internal ContextFactory(string connection, string defaultContainerName)
: base(connection, defaultContainerName) { }

public ObjectSet<T> GetContext<T>(bool lazyLoading = false) where T : class
{
var tType = typeof(T);

if (!contexts.ContainsKey(tType))
{
contexts.Add(tType, GetObjectSet<T>(lazyLoading));
}

return (ObjectSet<T>)contexts[tType];
}

private ObjectSet<T> GetObjectSet<T>(bool lazyLoading) where T : class
{
var ctx = CreateObjectSet<T>();
ctx.Context.ContextOptions.LazyLoadingEnabled = lazyLoading;
return ctx;
}
}


Now, when you query, you can use multiple repositories:

var fact = ContextFactory({connection stuff});

var q = from c in fact.GetContext<Customers>()
join p in fact.GetContext<Purchases>() on c.ID equals p.CustomerID
select c.Name + " bought a " + p.Description

You'd also probably want to store your context factory as a singleton somewhere (for web apps, the HTTPContext.Current) - as to avoid making loads of Object Contexts, and making the most of the level 1 caching.

Dave
Left by David Bishop on Dec 08, 2009 3:44 AM

# re: Creating a Generic Entity Framework 4.0 Repository
Requesting Gravatar...
David,

That's a good observation, however, the intent in this situation was simply to use the second constructor overload, such that you can pass in a single/common context that can be shared.

Thank you for the comment,

Sean
Left by Sean on Dec 12, 2009 3:11 PM

# re: Creating a Generic Entity Framework 4.0 Repository
Requesting Gravatar...
Hi Sean - could you provide a code sample of what you just explained to Dave? Even better - could you provide the code to do the same query that Dave presented using your method with the 2nd constructor?
Left by td on Dec 26, 2009 3:19 PM

# re: Creating a Generic Entity Framework 4.0 Repository
Requesting Gravatar...
What about explicit transactions? Does this work by wrapping several repository method calls with a TransactionScope?
Left by Markus on Jan 05, 2010 4:27 AM

# Single Context
Requesting Gravatar...
David and td,

Notice how the concrete type DataRepository<T> has a constructor that takes an ObjectContext. What I did was simply created a utility class with a method that returns an ObjectContext that I pass into the second constructor overload.

/// <summary>
/// Basic utility class for database-related methods
/// </summary>
public static class DatabaseUtility
{
/// <summary>
/// Gets the default ObjectContext for the project
/// </summary>
/// <returns>The default ObjectContext for the project</returns>
public static FrogBloggerEntities GetContext()
{
string connectionString = ConfigurationManager.ConnectionStrings["YourConnection"].ConnectionString;

return GetContext(connectionString);
}

/// <summary>
/// Gets the default ObjectContext for the project
/// </summary>
/// <param name="connectionString">Connection string to use for database queries</param>
/// <returns>The default ObjectContext for the project</returns>
public static FrogBloggerEntities GetContext(string connectionString)
{
return new FrogBloggerEntities(connectionString);
}
}


To use it, I simply do something like the following:

MyContext context = DatabaseUtility.GetContext();

using (IDataRepository<Blog> blogRepository = new DataRepository<Blog>(context))
using (IDataRepository<BlogPost> blogPostRepository = new DataRepository<BlogPost>(context))
{
// Do stuff here
}


Hopefully that clears things up.
Left by Sean Fao on Jan 06, 2010 2:04 PM

# re: Creating a Generic Entity Framework 4.0 Repository
Requesting Gravatar...
Markus,

I'm honestly not sure the answer to that question, however, I have to imagine that it would work like you would expect it to work so long as the query is performed within the TransactionScope.

Thank you for the comment,

Sean
Left by Sean Fao on Jan 06, 2010 2:07 PM

# do you mean Fetch() where you say GetQuery() ?
Requesting Gravatar...

public IEnumerable<T> GetAll()
{
return GetQuery().AsEnumerable();
}
Left by Milton on Jun 11, 2010 5:27 AM

# re: Creating a Generic Entity Framework 4.0 Repository
Requesting Gravatar...
Indeed, I did...That's what I get for editing the code here rather than Visual Studio.

Thank you for pointing it out.
Left by Sean on Jun 11, 2010 9:19 AM

# re: Creating a Generic Entity Framework 4.0 Repository
Requesting Gravatar...
There seems to be something wrong with the blog and I'm unable to update the post, but I'll make the update as soon as possible.
Left by Sean on Jun 11, 2010 9:22 AM

# re: Creating a Generic Entity Framework 4.0 Repository
Requesting Gravatar...
Excellent. You might want to make your dispose comments more general.
Releases all resources used by the WarrantManagement.DataExtract.Dal.ReportDataBase
Left by Rick Anderson on Dec 29, 2010 11:41 PM

# re: Creating a Generic Entity Framework 4.0 Repository
Requesting Gravatar...
Sean,
One question:
If you use the 2nd constructor should you not set a flag to not dispose the object context?
Left by santiagoIT on Jan 24, 2011 6:07 PM

# re: Creating a Generic Entity Framework 4.0 Repository
Requesting Gravatar...
One thing to note is that Func<T, bool> should be changed to Expression<Func<T, bool>>, the Expression is required in order for the predicate to be evaluated at the database level as opposed to in memory post query. This is because Func<T, bool> operates on IEnumerable as opposed to IQueryable.
Left by e36M3 on Mar 04, 2011 8:19 AM

# re: Creating a Generic Entity Framework 4.0 Repository
Requesting Gravatar...
Hi, isn't it also possible to use the Singleton pattern for retriving the context?
Left by Da_Wolf on Mar 21, 2011 4:28 AM

# re: Creating a Generic Entity Framework 4.0 Repository
Requesting Gravatar...
what's the method GetQuery() in GetAll()? lt should be _objectSet, right?
Left by Xiaofeng on Apr 16, 2011 9:09 AM

# re: Creating a Generic Entity Framework 4.0 Repository
Requesting Gravatar...
btw, i got an exception says 'Invoke' LINQ expression type not supported.

check it out here: http://social.msdn.microsoft.com/forums/en-US/adodotnetentityframework/thread/cd1101ff-105d-400d-a7ec-e6eb7a2b27ce/
Left by Xiaofeng on Apr 16, 2011 9:40 AM

# re: Creating a Generic Entity Framework 4.0 Repository
Requesting Gravatar...
Nice artical. help to clear my mind
Left by derek on Apr 18, 2011 5:46 PM

# re: Creating a Generic Entity Framework 4.0 Repository
Requesting Gravatar...
Is there a way to add Entity Max Id property to the repository ?
Thanks
Left by Muthu Annamalai on Apr 25, 2011 9:29 PM

# re: Creating a Generic Entity Framework 4.0 Repository
Requesting Gravatar...
Hi, nice work.

In your
public interface IRepository<T> : IDisposable where T : class
I changed 'class' to 'EntityObject' to make it more specific to EntityFramework. Do you see any problem with that?
Left by lee on May 05, 2011 6:38 AM

# re: Creating a Generic Entity Framework 4.0 Repository
Requesting Gravatar...
I copy your code, and I get the error:
The type T must be a reference type in order to use it as parameter TEntity in the generic type or method. 'System.Data.Objects.IObjectSet<TEntity>

Any ideas?
Left by Owen on May 13, 2011 7:29 AM

# re: Creating a Generic Entity Framework 4.0 Repository
Requesting Gravatar...
Sorry please ignore my stupid question. I hadnt added the:

: IRepository<T> where T : class
Left by Owen on May 13, 2011 8:19 AM

# re: Creating a Generic Entity Framework 4.0 Repository
Requesting Gravatar...
I can't Compile this because gave this error :(

The name 'GetQuery' does not exist in the current context

Can anyone help me ??? i need this library
Left by HF on Nov 04, 2011 5:35 PM

# re: Creating a Generic Entity Framework 4.0 Repository
Requesting Gravatar...
Replace the call to GetQuery() with a call to Fetch(). I must have renamed the method and forgot to post the updated code.
Left by Sean on Nov 11, 2011 3:13 PM

# re: Creating a Generic Entity Framework 4.0 Repository
Requesting Gravatar...
THANKS Dear Sean
Yes Now it works ! :D
But Can you add an example for update an entity ???
I use your library for CRD but U (Update) !!! I have problem with Update
I need a simple example How Update an entity ???
please help me again
THANKS for awesome work :D
Left by HF on Nov 15, 2011 12:51 PM

# re: Creating a Generic Entity Framework 4.0 Repository
Requesting Gravatar...
When i pass lamda expression to the find method in the base repository it takes a lot of time to return the rows back with the criteria specified. Whereas if i write a direct linq query using linq it is much faster. I noticed that the objectset returns all rows first and then applies the where filter. I have got about 500k records in the db and want to select 1 using the primary key and it takes more than 1 minute at which point the server times out. But if i run the linq query directly it takes less than a second.

Any idea why is that happening?
Left by ab on Jan 20, 2012 5:57 AM

# re: Creating a Generic Entity Framework 4.0 Repository
Requesting Gravatar...
Take a ook where its said that Func<T, bool> should be changed to Expression<Func<T, bool>>.
Passing in a delegate calls IEnumerable<T>.Where which can not be used by the LINQ-Provider to create SQL.
So all your data will be fetched into memory before the predicate is applied.
Left by Christian Douven on Jan 23, 2012 1:03 PM

# re: Creating a Generic Entity Framework 4.0 Repository
Requesting Gravatar...
@Christian, very good catch. I'll make that update.
Left by Sean on Jan 23, 2012 4:14 PM

# re: Creating a Generic Entity Framework 4.0 Repository
Requesting Gravatar...
First of all thank you for such a great post (and to those who have also provided great comments)
I have a simple question may be so simple that I may be laughed on but I see no other choice as to ask - please keep in mind that I am very new to programming!

Taking the following method as an example:

public T Single(Func<T, bool> predicate)
{
return _objectSet.Single<T>(predicate);
}

If the method fails to find a record with the given criteria, then, on the client side, I receive the following InvalidOperationException:
{"Sequence contains no matching element"}

I think that I should be returning <null> from the method if this is the case.

1) Am I correct in assuming this?
2) How would I go about doing this?

Obviously something like this will not work!

var res = _objectSet.Single<T>(predicate);
return res != null ? res : null;

Many thanks for your time.
Left by David Insklovich on Mar 24, 2012 9:27 AM

# re: Creating a Generic Entity Framework 4.0 Repository
Requesting Gravatar...
I was a little too hasty, sorry.

From MSDN:
The First(IEnumerable) method throws an exception if source contains no elements. To instead return a default value when the source sequence is empty, use the FirstOrDefault method

For clarity I also renamed your interface methods, implementing them of course, so that I was in fact calling the "SingleOrDefault" and "FirstOrDefault" methods.

Thank you!
Left by David Insklovich on Mar 24, 2012 2:11 PM

# re: Creating a Generic Entity Framework 4.0 Repository
Requesting Gravatar...
Thanks for this very useful post, just I'm a little bit confused how to use EF4 and a Generic repository class in MVVM design pattern.
Left by Hichem Sharaeen on Apr 11, 2012 6:37 PM

# re: Creating a Generic Entity Framework 4.0 Repository
Requesting Gravatar...
Congratulations! Excellent article!
It helped me a lot! Thank you!
Left by Rafael Botelho on May 12, 2012 11:55 PM

Your comment:
 (will show your gravatar)


Copyright © Sean Fao | Powered by: GeeksWithBlogs.net | Join free