Sean's Blog

My Development Blog

  Home  |   Contact  |   Syndication    |   Login
  18 Posts | 0 Stories | 99 Comments | 0 Trackbacks

News

Twitter












Archives

.NET Development

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

Feedback

# re: Creating a Generic Entity Framework 4.0 Repository 12/4/2009 9:35 AM BigJim
Super article, thanks for this entry!

# re: Creating a Generic Entity Framework 4.0 Repository 12/5/2009 12:24 PM Yoann .B
Great Post ! Nicely writen, Thanks.

# re: Creating a Generic Entity Framework 4.0 Repository 12/6/2009 12:03 PM Yoann .B
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!

# re: Creating a Generic Entity Framework 4.0 Repository 12/8/2009 3:44 AM David Bishop
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

# re: Creating a Generic Entity Framework 4.0 Repository 12/12/2009 3:11 PM Sean
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

# re: Creating a Generic Entity Framework 4.0 Repository 12/26/2009 3:19 PM td
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?

# re: Creating a Generic Entity Framework 4.0 Repository 1/5/2010 4:27 AM Markus
What about explicit transactions? Does this work by wrapping several repository method calls with a TransactionScope?

# Single Context 1/6/2010 2:04 PM Sean Fao
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.

# re: Creating a Generic Entity Framework 4.0 Repository 1/6/2010 2:07 PM Sean Fao
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

# do you mean Fetch() where you say GetQuery() ? 6/11/2010 5:27 AM Milton

public IEnumerable<T> GetAll()
{
return GetQuery().AsEnumerable();
}

# re: Creating a Generic Entity Framework 4.0 Repository 6/11/2010 9:19 AM Sean
Indeed, I did...That's what I get for editing the code here rather than Visual Studio.

Thank you for pointing it out.

# re: Creating a Generic Entity Framework 4.0 Repository 6/11/2010 9:22 AM Sean
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.

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


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

# re: Creating a Generic Entity Framework 4.0 Repository 3/4/2011 8:19 AM e36M3
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.

# re: Creating a Generic Entity Framework 4.0 Repository 3/21/2011 4:28 AM Da_Wolf
Hi, isn't it also possible to use the Singleton pattern for retriving the context?

# re: Creating a Generic Entity Framework 4.0 Repository 4/16/2011 9:09 AM Xiaofeng
what's the method GetQuery() in GetAll()? lt should be _objectSet, right?

# re: Creating a Generic Entity Framework 4.0 Repository 4/16/2011 9:40 AM Xiaofeng
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/

# re: Creating a Generic Entity Framework 4.0 Repository 4/18/2011 5:46 PM derek
Nice artical. help to clear my mind

# re: Creating a Generic Entity Framework 4.0 Repository 4/25/2011 9:29 PM Muthu Annamalai
Is there a way to add Entity Max Id property to the repository ?
Thanks

# re: Creating a Generic Entity Framework 4.0 Repository 5/5/2011 6:38 AM lee
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?

# re: Creating a Generic Entity Framework 4.0 Repository 5/13/2011 7:29 AM Owen
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?

# re: Creating a Generic Entity Framework 4.0 Repository 5/13/2011 8:19 AM Owen
Sorry please ignore my stupid question. I hadnt added the:

: IRepository<T> where T : class

# re: Creating a Generic Entity Framework 4.0 Repository 11/4/2011 5:35 PM HF
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


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

# re: Creating a Generic Entity Framework 4.0 Repository 11/15/2011 12:51 PM HF
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

# re: Creating a Generic Entity Framework 4.0 Repository 1/20/2012 5:57 AM ab
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?

# re: Creating a Generic Entity Framework 4.0 Repository 1/23/2012 1:03 PM Christian Douven
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.

# re: Creating a Generic Entity Framework 4.0 Repository 1/23/2012 4:14 PM Sean
@Christian, very good catch. I'll make that update.

# re: Creating a Generic Entity Framework 4.0 Repository 3/24/2012 9:27 AM David Insklovich
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.

# re: Creating a Generic Entity Framework 4.0 Repository 3/24/2012 2:11 PM David Insklovich
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!

# re: Creating a Generic Entity Framework 4.0 Repository 4/11/2012 6:37 PM Hichem Sharaeen
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.

# re: Creating a Generic Entity Framework 4.0 Repository 5/12/2012 11:55 PM Rafael Botelho
Congratulations! Excellent article!
It helped me a lot! Thank you!

Post A Comment
Title:
Name:
Email:
Comment:
Verification: