Solved

Using FakeDbSet without modify DbContext entity when using EF 5

Posted on 2014-03-18
5
696 Views
Last Modified: 2014-03-28
I am investigating TDD with my MVC 4 application that uses Entity Framework 5.0 (Code First).  I followed the example at this link, http://www.nogginbox.co.uk/blog/mocking-entity-framework-data-context, to make the modifications to my application to get it to work.  Making the changes mentioned in the link I was able to get my unit tests to work using a "Fake" database and also to get the application to work connecting to the database.

My problem is the instructions have me changing the DbContext file generated by the EF Designer.  The changes needed include:
Derive from DbContext and my Interface
replace DbSet with IDbSet on my properties

Now whenever my model changes and the DbContext file is regenerated my changes are lost.

Can someone suggest a way of getting around this?  Any help is greatly appreciated!

I am including the code so you can see what is going on.

auto-generated DbContext file with my manual changes:
namespace AppCatalog.Models
{
    using System;
    using System.Data.Entity;
    using System.Data.Entity.Infrastructure;
    
    public partial class AppCatalogContainer : DbContext, IAppCatalogContainer
    {
        public AppCatalogContainer()
            : base("name=AppCatalogContainer")
        {
        }
    
        protected override void OnModelCreating(DbModelBuilder modelBuilder)
        {
            throw new UnintentionalCodeFirstException();
        }
    
        public IDbSet<AppCatalog_Database> AppCatalog_Database { get; set; }
        public IDbSet<AppCatalog_ServerType> AppCatalog_ServerType { get; set; }
        public IDbSet<AppCatalog_DBConnection> AppCatalog_DBConnection { get; set; }
        public IDbSet<AppCatalog_Environment> AppCatalog_Environment { get; set; }
        public IDbSet<AppCatalog_PIIData> AppCatalog_PIIData { get; set; }
        public IDbSet<AppCatalog_Application> AppCatalog_Application { get; set; }
        public IDbSet<AppCatalog_ApplicationTable> AppCatalog_ApplicationTable { get; set; }
    }
}

Open in new window


My Interface
using System;
using System.Data.Entity;

namespace AppCatalog.Models 
{
    public interface IAppCatalogContainer 
    {
        IDbSet<AppCatalog_Database> AppCatalog_Database { get; set; }
        IDbSet<AppCatalog_ServerType> AppCatalog_ServerType { get; set; }
        IDbSet<AppCatalog_DBConnection> AppCatalog_DBConnection { get; set; }
        IDbSet<AppCatalog_Environment> AppCatalog_Environment { get; set; }
        IDbSet<AppCatalog_PIIData> AppCatalog_PIIData { get; set; }
        IDbSet<AppCatalog_Application> AppCatalog_Application { get; set; }
        IDbSet<AppCatalog_ApplicationTable> AppCatalog_ApplicationTable { get; set; }

        int SaveChanges();
        void Dispose();
     }
}

Open in new window


FakeDbSet.cs
using System;
using System.Collections.Generic;
using System.Linq;
using System.Data.Entity;
using System.Collections.ObjectModel;
using System.Linq.Expressions;
using System.Collections;

namespace AppCatalog.Tests.Controllers
{
    public class FakeDbSet<T> : IDbSet<T> where T : class
    {
        HashSet<T> _data;
        IQueryable _query;

        public FakeDbSet()
        {
            _data = new HashSet<T>();
            _query = _data.AsQueryable();
        }


        public virtual T Find(params object[] keyValues)
        {
            throw new NotImplementedException("Derive from FakeDbSet<T> and override Find");
        }

        public T Add(T item)
        {
            _data.Add(item);
            return item;
        }

        public T Remove(T item)
        {
            _data.Remove(item);
            return item;
        }

        public void DeleteObject(T item)
        {
            _data.Remove(item);
        }

        public T Attach(T item)
        {
            _data.Add(item);
            return item;
        }

        public void Detach(T item)
        {
            _data.Remove(item);
        }

        public T Create()
        {
            return Activator.CreateInstance<T>();
        }

        public TDerivedEntity Create<TDerivedEntity>() where TDerivedEntity : class, T
        {
            return Activator.CreateInstance<TDerivedEntity>();
        }

        public ObservableCollection<T> Local
        {
            get { return new ObservableCollection<T>(_data); }
        }

        Type IQueryable.ElementType
        {
            get { return _query.ElementType; }
        }

        Expression IQueryable.Expression
        {
            get { return _query.Expression; }
        }

        IQueryProvider IQueryable.Provider
        {
            get { return _query.Provider; }
        }

        IEnumerator System.Collections.IEnumerable.GetEnumerator()
        {
            return _data.GetEnumerator();
        }

        IEnumerator<T> IEnumerable<T>.GetEnumerator()
        {
            return _data.GetEnumerator();
        }

    }
}

Open in new window


My Controller:
namespace AppCatalog.Controllers
{
    public class DBCatalogController : Controller
    {
        private IAppCatalogContainer db { get; set; }

        public DBCatalogController(IAppCatalogContainer dataContext = null)
        {
            db = dataContext ?? new AppCatalogContainer();
        }
        public DBCatalogController()
        {
            db = new AppCatalogContainer();
        }

        //
        // GET: /DBCatalog/

        public ViewResult Index()
        {
            ViewBag.Title = "Application Catalog -> Database Catalog";

            var appcatalog_database = db.AppCatalog_Database.Include(a => a.AppCatalog_ServerType);
            return View(appcatalog_database.ToList());
        }
    }
}

Open in new window


My Test using Moq and FakeDbSet:
using System;
using System.Text;
using System.Collections.Generic;
using System.Linq;
using Microsoft.VisualStudio.TestTools.UnitTesting;
using AppCatalog.Controllers;
using System.Web.Mvc;
using AppCatalog.Models;
using System.Data.Entity;
using Moq;

namespace AppCatalog.Tests.Controllers
{
    [TestClass]
    public class DBCatalogControllerTest
    {
        [TestMethod]
        public void VerifyTitleIsApplicationCatalogDatabaseCatalog()
        {
            // Create Fake Data
            var inMemoryItems = new FakeDbSet<AppCatalog_Database>
            {
                new AppCatalog_Database { databaseID=1, databaseName="AppCatalog", databaseDescription="Contains tables used by Application Catalog app", databaseServerTypeID=2 },
                new AppCatalog_Database { databaseID=2, databaseName="EMP_OWNER", databaseDescription="Contains tables used by Employee Profile app", databaseServerTypeID=1 }
            };

            // Create mock unit of work
            var mockData = new Mock<IAppCatalogContainer>();
            mockData.Setup(m => m.AppCatalog_Database).Returns(inMemoryItems);

            // Setup controller
            DBCatalogController controller = new DBCatalogController(mockData.Object);

            // Invoke
            ViewResult viewResult = controller.Index();

            // Assert
            Assert.AreEqual("Application Catalog -> Database Catalog", viewResult.ViewBag.Title);
        }

        [TestMethod]
        public void VerifyDatabaseReturnsAllRecords()
        {
            // Create Fake Data
            var inMemoryItems = new FakeDbSet<AppCatalog_Database>
            {
                new AppCatalog_Database { databaseID=1, databaseName="AppCatalog", databaseDescription="Contains tables used by Application Catalog app", databaseServerTypeID=2 },
                new AppCatalog_Database { databaseID=2, databaseName="EMP_OWNER", databaseDescription="Contains tables used by Employee Profile app", databaseServerTypeID=1 }
            };

            // Create mock unit of work
            var mockData = new Mock<IAppCatalogContainer>();
            mockData.Setup(m => m.AppCatalog_Database).Returns(inMemoryItems);

            // Setup controller
            DBCatalogController controller = new DBCatalogController(mockData.Object);

            // Invoke
            ViewResult viewResult = controller.Index();
            var model = viewResult.Model as IEnumerable<AppCatalog_Database>;

            Assert.AreEqual(2, model.Count());
        }
    }
}

Open in new window

0
Comment
Question by:dyarosh
  • 2
5 Comments
 
LVL 11

Accepted Solution

by:
Mihai Stancescu earned 500 total points
ID: 39958187
Hi,

Why don't you try the repository pattern and an dependency injection?
By using this patterns you can modify your entities anytime and how many times you need.

This way you can have two repositories one "fake" for testing and one for production environment that actually connects to a db.

As for dependency injection I've worked with Ninject and had no problem with it. It's a nuget package and it also works for API controllers with little more configuration.

For more info on repository pattern in mvc: repository pattern in mvc.
This link also talks about Unit of Work pattern but I think it's optional depending on your domain model of your application.

I know that this does not answer your question but I think it can help you in your application and development process.


Hope this helps,
Mishu
0
 

Author Closing Comment

by:dyarosh
ID: 39959381
Thank you for the response.  I reviewed the link and think that it will work perfectly for my situation.  I am going to backup and use the repository pattern.
0
 
LVL 11

Expert Comment

by:Mihai Stancescu
ID: 39960880
Glad I could help you.

Have a nice day,
Mishu
0

Featured Post

What Should I Do With This Threat Intelligence?

Are you wondering if you actually need threat intelligence? The answer is yes. We explain the basics for creating useful threat intelligence.

Join & Write a Comment

I found this questions asking how to do this in many different forums, so I will describe here how to implement a solution using PHP and AJAX. The logical flow for the problem should be: Write an event handler for the first drop down box to get …
Have you tried to learn about Unicode, UTF-8, and multibyte text encoding and all the articles are just too "academic" or too technical? This article aims to make the whole topic easy for just about anyone to understand.
The viewer will receive an overview of the basics of CSS showing inline styles. In the head tags set up your style tags: (CODE) Reference the nav tag and set your properties.: (CODE) Set the reference for the UL element and styles for it to ensu…
The viewer will learn the basics of jQuery including how to code hide show and toggles. Reference your jQuery libraries: (CODE) Include your new external js/jQuery file: (CODE) Write your first lines of code to setup your site for jQuery…

760 members asked questions and received personalized solutions in the past 7 days.

Join the community of 500,000 technology professionals and ask your questions.

Join & Ask a Question

Need Help in Real-Time?

Connect with top rated Experts

19 Experts available now in Live!

Get 1:1 Help Now