Solved

Using FakeDbSet without modify DbContext entity when using EF 5

Posted on 2014-03-18
5
706 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

Is Your Active Directory as Secure as You Think?

More than 75% of all records are compromised because of the loss or theft of a privileged credential. Experts have been exploring Active Directory infrastructure to identify key threats and establish best practices for keeping data safe. Attend this month’s webinar to learn more.

Question has a verified solution.

If you are experiencing a similar issue, please ask a related question

Introduction Since I wrote the original article about Handling Date and Time in PHP and MySQL (http://www.experts-exchange.com/articles/201/Handling-Date-and-Time-in-PHP-and-MySQL.html) several years ago, it seemed like now was a good time to updat…
Real-time is more about the business, not the technology. In day-to-day life, to make real-time decisions like buying or investing, business needs the latest information(e.g. Gold Rate/Stock Rate). Unlike traditional days, you need not wait for a fe…
Viewers will learn about arithmetic and Boolean expressions in Java and the logical operators used to create Boolean expressions. We will cover the symbols used for arithmetic expressions and define each logical operator and how to use them in Boole…
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…

863 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

24 Experts available now in Live!

Get 1:1 Help Now