24

I'm playing with the latest Entity Framework CTP 5 release and building a simple asp.net MVC blog where I just have two tables: Post and Comments. This is done entirely in POCO, I just need help on the DbContext part, where I need it to be unit testable (using IDbSet?) and I need a simple/generic repository pattern for add, update, delete, retrieval. Any help is appreciated.

Thanks.

Saxman
  • 5,009
  • 11
  • 51
  • 72
  • 1
    Is it a good first step? http://romiller.com/2010/09/07/ef-ctp4-tips-tricks-testing-with-fake-dbcontext/ – Devart Dec 15 '10 at 13:22
  • I like that Devart, but it kinda moves away from the Generic Repository Pattern, I'd like to see a repository that takes a single type. Maybe based off DBSet instead? – mxmissile Dec 15 '10 at 15:23

3 Answers3

50

Start with you DbContext, create a new file called Database.cs:

Database.cs

public class Database : DbContext
    {

        private IDbSet<Post> _posts;

        public IDbSet<Post> Posts {
            get { return _posts ?? (_posts = DbSet<Post>()); }
        }

        public virtual IDbSet<T> DbSet<T>() where T : class {
            return Set<T>();
        }
        public virtual void Commit() {
            base.SaveChanges();
        }
}

Define a IDatabaseFactory and implement it with DatabaseFactory:

IDatabaseFactory.cs

public interface IDatabaseFactory : IDisposable
    {
        Database Get();
    }

DatabaseFactory.cs

public class DatabaseFactory : Disposable, IDatabaseFactory {
        private Database _database;
        public Database Get() {
            return _database ?? (_database = new Database());
        }
        protected override void DisposeCore() {
            if (_database != null)
                _database.Dispose();
        }
    }

Disposable extension method:

Disposable.cs

public class Disposable : IDisposable
    {
        private bool isDisposed;

        ~Disposable()
        {
            Dispose(false);
        }

        public void Dispose()
        {
            Dispose(true);
            GC.SuppressFinalize(this);
        }
        private void Dispose(bool disposing)
        {
            if(!isDisposed && disposing)
            {
                DisposeCore();
            }

            isDisposed = true;
        }

        protected virtual void DisposeCore()
        {
        }
    }

Now we can define our IRepository and our RepositoryBase

IRepository.cs

public interface IRepository<T> where T : class
{
    void Add(T entity);
    void Delete(T entity);
    void Update(T entity);
    T GetById(long Id);
    IEnumerable<T> All();
    IEnumerable<T> AllReadOnly();
}

RepositoryBase.cs

public abstract class RepositoryBase<T> where T : class
    {
        private Database _database;
        private readonly IDbSet<T> _dbset;
        protected RepositoryBase(IDatabaseFactory databaseFactory)
        {
            DatabaseFactory = databaseFactory;
            _dbset = Database.Set<T>();
        }

        protected IDatabaseFactory DatabaseFactory
        {
            get; private set;
        }

        protected Database Database
        {
            get { return _database ?? (_database = DatabaseFactory.Get()); }
        }
        public virtual void Add(T entity)
        {
            _dbset.Add(entity);
        }

        public virtual void Delete(T entity)
        {
            _dbset.Remove(entity);
        }

        public virtual void Update(T entity)
        {
            _database.Entry(entity).State = EntityState.Modified;
        }
        public virtual T GetById(long id)
        {
            return _dbset.Find(id);
        }

        public virtual IEnumerable<T> All()
        {
            return _dbset.ToList();
        }
        public virtual IEnumerable<T> AllReadOnly()
        {
            return _dbset.AsNoTracking().ToList();
        }
    }

Now you can create your IPostRepository and PostRepository:

IPostRepository.cs

     public interface IPostRepository : IRepository<Post>
        {
            //Add custom methods here if needed
            Post ByTitle(string title);
        }

PostRepository.cs

    public class PostRepository : RepositoryBase<Post>, IPostRepository
        {
            public PostRepository(IDatabaseFactory databaseFactory) : base(databaseFactory)
            {
            }
            public Post ByTitle(string title) {
                return base.Database.Posts.Single(x => x.Title == title);
            }
        }

Lastly, the UoW:

IUnitOfWork.cs

public interface IUnitOfWork
{
    void Commit();
}

UnitOfWork.cs

private readonly IDatabaseFactory _databaseFactory;
private Database _database;

public UnitOfWork(IDatabaseFactory databaseFactory)
{
    _databaseFactory = databaseFactory;
}

protected Database Database
{
    get { return _database ?? (_database = _databaseFactory.Get()); }
}

public void Commit()
{
    Database.Commit();
}

Using in your controller:

private readonly IPostRepository _postRepository;
private readonly IUnitOfWork_unitOfWork;

        public PostController(IPostRepository postRepository, IUnitOfWork unitOfWork)
        {
            _postRepository = postRepository;
            _unitOfWork = unitOfWork;
        }

        public ActionResult Add(Post post) {
            _postRepository.Add(post);
            _unitOfWork.Commit();
        }

You will need to use an IoC container like StructureMap to make this work. You can install structure map via NuGet, or if you are using MVC 3, you can install the StructureMap-MVC NuGet package. (Links Below)

Install-Package StructureMap.MVC4

Install-Package StructureMap.MVC3

Install-Package Structuremap

If you have questions just let me know. Hope it helps.

Paul
  • 12,392
  • 4
  • 48
  • 58
  • What SM Lifecycle did you use for each of these? – mxmissile Dec 16 '10 at 20:58
  • 3
    You want to use HttpContextScoped. So it would look like: x.For().HttpContextScoped().Use(); etc – Paul Dec 16 '10 at 21:55
  • Same lifecycle for IDatabaseFactory? – mxmissile Dec 16 '10 at 22:08
  • Yes, you want all data access concerns to be HttpContextScoped. Including any I...Repository() – Paul Dec 16 '10 at 22:27
  • Hi Paul, I'm having a little problem with the RepositoryBase class, for the Add/Delete, etc... methods... Where is this code comes from: `Check.Argument.IsNotNull` ? I actually am using StructureMap for IoC. Thanks. – Saxman Dec 17 '10 at 20:34
  • That is from a custom class that I use to check for NULLs. You can just remove it, it does not need to be there for the Repository to work. So just delete those lines from Add and Delete and it all should work. Let me know if you have any other questions. – Paul Dec 18 '10 at 03:54
  • Paul, I think I got 99% of it working from what you showed here! Thank you so much! I have a question on the PostRepository.cs, where I want specific/custom methods. How do I have access to the "Post" object so that I can do something like `_post.Where(p => p.Title.Equals("something"));` ? Thanks again for all your helps. – Saxman Dec 21 '10 at 00:15
  • @Saxman Good Question, I will update the post to show this so it can help others as well. Take a look at the altered IPostRepository and PostRepository above. – Paul Dec 21 '10 at 00:37
  • Hi Paul, one more question :) Can you show an example of how to use the UnitOfWork class. I'm trying to do some custom update method in the PostRepository. Thanks. – Saxman Dec 21 '10 at 01:52
  • @Saxman Added an example Controller. Simply, you can use any Repo, then call the Commit method on the UnitOfWork and the will be persisted to the database. – Paul Dec 21 '10 at 04:27
  • Thanks again for your quick and very helpful responses! Happy holidays to you! – Saxman Dec 21 '10 at 04:51
  • Good answer. You can wrap the DbContext in a using to create a transaction. I think you can ditch the unit of work then as well. – nick Jan 19 '11 at 20:52
  • Hi Paul, very nice code.. but i used most of the code in here and it seem i cannot get it to work. Ive been able to bind everything.. but nothing is added to the database (no error on compile, plus it do create the database so it seem everything is working here) so i was wondering what would be wrong. I add a new entity then use commit and nothing is aved. Any ideas ? Do you have a working implementation ? Thanks a lots! – Rushino Jan 24 '11 at 00:37
  • Got it to work! It was due to ninject which wasnt used in there. Thanks for the implementation. – Rushino Jan 24 '11 at 23:48
  • @Paul - So I have decided to go the Code First route, and I can not for the life of me figure out how to save an object that is detached. I am getting the object from the repository, mappng it to a view model, sending it to the view, post it back, map the model to an object, send it to the repository to save, but it never saves??? – Sam Mar 11 '11 at 23:31
  • @Sam - how you are attaching it? Just because it is attached does not mean it is marked as dirty/changed. I added a Update Method to the IRepository above you can use that should work. Just pass the entity to the Update method on the repository then call the Commit method on the UnitOfWork. Let me know if it doesn't work for you. – Paul Mar 12 '11 at 04:00
  • Hello Guys, How to initialize the repository, I try to call it from a service but I receive FaultException, I said how to use the repo in a service layer for example! – Jonathan Escobedo Mar 15 '11 at 16:08
  • @Angel - You would use constructor injection and let Structuremap supply the instance. Have you setup Structuremap as your IoC? If not, read that last part of the post. – Paul Mar 15 '11 at 17:03
  • @Paul, I can't figure how to config StructureMap on MVC3, It seems that.. x.For().Use(() => new DatabaseFactory().Get()); is not working for me – Jonathan Escobedo Mar 15 '11 at 23:39
  • @Angel, did you install the Structuremap-MVC3 NuGet package? If so, you would configure it like this: x.For().HttpContextScoped().Use(); – Paul Mar 16 '11 at 01:22
  • @Paul, When I call for initialize my Repository it seems to need to pass a IDatabaseFactory as a parameter, is that correct? – Jonathan Escobedo Mar 16 '11 at 03:29
  • @Paul, Could you check my question on : http://stackoverflow.com/questions/5319539/first-try-structuremap-and-mvc3-via-nuget Thanks – Jonathan Escobedo Mar 16 '11 at 03:32
  • @Paul - You should give credit to Shiju Varghese and his EFMVC project and posts that you are basing your answer on. See http://weblogs.asp.net/shijuvarghese/archive/2011/01/06/developing-web-apps-using-asp-net-mvc-3-razor-and-ef-code-first-part-1.aspx – Dan Diplo Apr 20 '11 at 21:19
  • @Dan - I have never read that post, good information though. The post looks like it came out almost a month after my answer. If I was going to credit anyone for the core idea it would be Kazi Manzur Rashid – Paul Apr 21 '11 at 00:13
  • @Paul If that's the case sorry. It's just that the code bares more than a passing resemblance, being almost identical. But it's very helpful, which is the main point! – Dan Diplo Apr 21 '11 at 19:05
  • @Paul Just seen this.Fantastic.We dont use structureMap I could try Unity.I need this in serviceLayer is there any difference if instead of the controller that would be my serviceLayer.Also I have noticed that in your repositoryBase you have a dependency on your context.Isnt it the idea or repository base that should be indipendent of your context? – user9969 Apr 23 '11 at 13:03
  • @uesr - You can call the Repo from your service layer if you like. I do is most apps. The dependency is on IDatabaseFactory, and it is what supplies the database context for EF. – Paul Apr 24 '11 at 00:17
  • @Paul Just wondering, why doesn't your RepositoryBase implement IRepository? – Justin Jun 28 '11 at 12:58
  • @Justin - The RepositoryBase does implement the IRepository it just does not inherit from it. – Paul Jun 30 '11 at 21:48
  • @Paul - Why is it that the RepositoryBase doesn't inherit IRepository? – Justin Jul 06 '11 at 19:37
  • what if you need more repositories to deal with ? You would inject them too ? In other languages they use something like a locator to get the repository.So is it bad ?? – GorillaApe Oct 22 '12 at 19:45
9

I just love this in-depth article about Entity Framework 4 POCO, Repository and Specification Pattern

http://huyrua.wordpress.com/2010/07/13/entity-framework-4-poco-repository-and-specification-pattern/

Korayem
  • 12,108
  • 5
  • 69
  • 56
1

The only thing I'd do differently is in the implementation, i.e. expose the IPostRepository in the service layer and have an interface field of type IPostService in the controller just as another layer of abstraction but otherwise this is a good example - nice one, Paul.

santos
  • 434
  • 3
  • 6
  • 1
    I do the same in my projects. It is good abstraction and also keeps the controller actions clean and simple. – Paul Jan 12 '11 at 01:37
  • Hi Paul, it has been quite awhile and what you showed here has worked really well. But I haven't been doing any unit tests at all :) Just today that I'm thinking about doing unit test, and using the MOQ framework to mock the data, how would I do that? Do I have to re-create the repositories as Mock Repositories, or I can just re-use what I've already have? Thank you very much. – Saxman Jan 18 '11 at 17:16
  • @Saxman - I would ask a new questoins. How to Unit Test my Repository or something. I can answer it there – Paul Jan 18 '11 at 22:44
  • :) Yes Paul, how would I test my Repository :) Those add, remove, update methods that we all have to deal with. Even better *if* we can mock the data, or not have to connect to a real database. Thank you. – Saxman Jan 19 '11 at 04:08
  • Hi Paul, here is it, my new question :) http://stackoverflow.com/questions/4738477/entity-framework-4-ctp-5-poco-how-to-unit-test-my-repositoryt – Saxman Jan 19 '11 at 17:22