简体   繁体   中英

How to Unit Test with Dynamically-Generated ADO.NET Entity Data Model Code

I'm following this tutorial which explains how to mock the entity framework when performing unit testing on a Web API 2 service.

The code works by using dependency injection by defining an interface

namespace StoreAcreTransmissions.Models
{
  public interface IStoreAcreTransmissionsContext : IDisposable
  {
    DbSet<AcreReportTransaction> AcreReportTransactions { get; }
    int SaveChanges();
    void MarkAsModified(AcreReportTransaction item);
  }
} 

that is then implemented by a class

namespace StoreAcreTransmissions.Models
{
    public class StoreAcreTransmissionsContext : DbContext, IStoreAcreTransmissionsContext
    {
        // You can add custom code to this file. Changes will not be overwritten.
        // 
        // If you want Entity Framework to drop and regenerate your database
        // automatically whenever you change your model schema, please use data migrations.
        // For more information refer to the documentation:
        // http://msdn.microsoft.com/en-us/data/jj591621.aspx

        public StoreAcreTransmissionsContext() : base("name=StoreAcreTransmissionsContext")
        {
        }

        public System.Data.Entity.DbSet<StoreAcreTransmissions.Models.AcreReportTransaction> AcreReportTransactions { get; set; }

        public void MarkAsModified(AcreReportTransaction item)
        {
          Entry(item).State = EntityState.Modified;
        }
    }
}

and passed to the constructor of the Controller

public AcreReportTransactionsController(IStoreAcreTransmissionsContext context)
{
    db = context;
}

All this works great, but in the normal operation of the service I'm utilizing an auto-generated ADO Entity Data Model to communicate with the database. So in order for me to use the actual Data Model in my controller, I have to set it in the default constructor:

private IStoreAcreTransmissionsContext db;

public AcreReportTransactionsController() 
{
    db = new DataModelContext();
}

But I also have to modify the dynamically generated context class ( AcreReportTransactionModel.Context.cs ) that is generated by the model so that it also implements the IStoreAcreTransmissionsContext interface:

//------------------------------------------------------------------------------
// <auto-generated>
//     This code was generated from a template.
//
//     Manual changes to this file may cause unexpected behavior in your application.
//     Manual changes to this file will be overwritten if the code is regenerated.
// </auto-generated>
//------------------------------------------------------------------------------

namespace StoreAcreTransmissions.Models
{
    using System;
    using System.Data.Entity;
    using System.Data.Entity.Infrastructure;

    public partial class ACRSIEntities : DbContext, IStoreAcreTransmissionsContext
    {
      public ACRSIEntities()
        : base("name=ACRSIEntities")
        {
        }

        protected override void OnModelCreating(DbModelBuilder modelBuilder)
        {
            throw new UnintentionalCodeFirstException();
        }

        public virtual DbSet<AcreReportTransaction> AcreReportTransactions { get; set; }

        public void MarkAsModified(AcreReportTransaction item)
        {
          Entry(item).State = EntityState.Modified;
        }
    }
}

The problem is, every time I re-sync my model with the database, this dynamically-generated context class is re-created and the code I added to implement the IStoreAcreTransmissionsContext interface is removed.

How can I configure my code such that the unit tests work and I can re-generate my model without having to re-add code?

You shouldn't modify the generated code. Modify the template used to generate the code.

In the Solution Explorer open the tree of your EDMX-Model, here you find a file named *.Context.tt, open it and modify the appropriate section.

You can see that the generated context class includes the partial keyword. This means you can, in a new file, declare the following:

public partial class ACRSIEntities : IStoreAcreTransmissionsContext {
     // Implement missing methods
     public void MarkAsModified(AcreReportTransaction item)
     {
       Entry(item).State = EntityState.Modified;
     }
}

There is more on partial classes in MSDN

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM