Solved

Using FakeDbSet without modify DbContext entity when using EF 5

Posted on 2014-03-18
5
723 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
[X]
Welcome to Experts Exchange

Add your voice to the tech community where 5M+ people just like you are talking about what matters.

  • Help others & share knowledge
  • Earn cash & points
  • Learn & ask questions
  • 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

Free Tool: IP Lookup

Get more info about an IP address or domain name, such as organization, abuse contacts and geolocation.

One of a set of tools we are providing to everyone as a way of saying thank you for being a part of the community.

Question has a verified solution.

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

Suggested Solutions

International Data Corporation (IDC) prognosticates that before the current the year gets over disbursing on IT framework products to be sent in cloud environs will be $37.1B.
The article shows the basic steps of integrating an HTML theme template into an ASP.NET MVC project
Explain concepts important to validation of email addresses with regular expressions. Applies to most languages/tools that uses regular expressions. Consider email address RFCs: Look at HTML5 form input element (with type=email) regex pattern: T…
This tutorial will teach you the core code needed to finalize the addition of a watermark to your image. The viewer will use a small PHP class to learn and create a watermark.

762 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