Repository模式--采用EF Fluent API使用EntityTypeConfiguration分文件配置Model映射关系

Repository模式--采用EF Fluent API使用EntityTypeConfiguration分文件配置Model映射关系

6912发表于2015-07-19

EF中类EntityTypeConfiguration是一个很有用的类,在nopCommerence中就使用这个类来分文件分文件配置Model映射关系。今天我就来谈谈Repository模式在Entity Framework Code First中使用EntityTypeConfiguration的实现例子。

背景

为了简化我们就只使用两个表:分类表Category,产品类Product。最终项目结构如下:

注意:EfRepPatTest.Entity和EfRepPatTest.Data是类库项目,EfRepPatTest.Implementation是控制台项目。项目EfRepPatTest.Data需要对Entity Framework类库的引用。

BaseEntity.cs

创建一个所有实体的基类BaseEntity,把一些公共的属性封装在里面。

public class BaseEntity<T>
{
    public T Id { get; set; }
}

这样表示所有实体都有一个字段为Id,类型为泛型这样可以满足所有类型的情况。

IRepository.cs:

下面定义一个泛型的接口IRepository,其中包括一个泛型的增、删、改。

public interface IRepository<TEntity> where TEntity:class 
{
    IQueryable<TEntity> GetAll();
    TEntity GetById(object id);
    void Insert(TEntity entity);
    void Update(TEntity entity);
    void Delete(TEntity entity);
}

Category.cs:

实体分类类

public class Category:BaseEntity<int>
{
    public virtual string Name { get; set; }

    public List<Product> Products { get; set; }
}

Product.cs:

实体产品类

public class Product:BaseEntity<long>
{
    public virtual int CategoryId { get; set; }
    public virtual Category Category { get; set; }
    public virtual string Name { get; set; }
    public virtual int MinimumStockLevel { get; set; }
}

IDbContext.cs:

接口IDbContext封装一些EF的公共接口方法。

public interface IDbContext
{

    IDbSet<TEntity> Set<TEntity>() where TEntity:class;
    int SaveChanges();
    void Dispose();
}

DataContext.cs:

public class DataContext: DbContext,IDbContext
{
    public new IDbSet<TEntity> Set<TEntity>() where TEntity : class
    {
        return base.Set<TEntity>();
    }
}

CategoryMap.cs:

分类映射类继承于EntityTypeConfigureation<T>

public class CategoryMap:EntityTypeConfiguration<Category>
{
    public CategoryMap()
    {
        ToTable("Category");
        HasKey(c => c.Id).Property(c => c.Id).HasDatabaseGeneratedOption(DatabaseGeneratedOption.Identity);
        Property(c => c.Name).IsRequired().HasMaxLength(50);
    }
}

ProductMap.cs:

产品类映射类继承于EntityTypeConfigureation<T>

public class ProductMap:EntityTypeConfiguration<Product>
{
    public ProductMap()
    {
        ToTable("Product");
        HasKey(p => p.Id).Property(p => p.Id).HasDatabaseGeneratedOption(DatabaseGeneratedOption.Identity);
        //CategoryId as foreign key
        HasRequired(p => p.Category)
            .WithMany(c=>c.Products)
            .HasForeignKey(p => p.CategoryId);
        Property(p => p.Name).IsRequired().HasMaxLength(100);
        Property(p => p.MinimumStockLevel);
    }
}

在类DataContext中重写OnModelCreating方法依次加上我们新建的EF的Map配置文件,加入以下代码:

modelBuilder.Configurations.Add(new CategoryMap());
modelBuilder.Configurations.Add(new ProductMap());
base.OnModelCreating(modelBuilder);

上面的代码可以优化一下,可以利用反射自动添加EF的Map配置文件,如下:

public class DataContext: DbContext,IDbContext
{
    public new IDbSet<TEntity> Set<TEntity>() where TEntity : class
    {
        return base.Set<TEntity>();
    }

    protected override void OnModelCreating(DbModelBuilder modelBuilder)
    {
        var typesToRegister = Assembly.GetExecutingAssembly().GetTypes()
        .Where(type => !String.IsNullOrEmpty(type.Namespace))
        .Where(type => type.BaseType != null && type.BaseType.IsGenericType && 
           type.BaseType.GetGenericTypeDefinition() == typeof(EntityTypeConfiguration<>));
        foreach (var type in typesToRegister)
        {
            dynamic configurationInstance = Activator.CreateInstance(type);
            modelBuilder.Configurations.Add(configurationInstance);
        }

        base.OnModelCreating(modelBuilder);
    }

}

这样的好处是以后新加EF的实体Map类,不用修改DataContext。

RepositoryService.cs:

IRepositroy接口的一个具体实现的RepositoryService,数据访问采用EF的IDbContext。

public class RepositoryService<TEntity>:IRepository<TEntity> where TEntity:class 
{
    private IDbContext Context;

    private IDbSet<TEntity>  Entities
    {
        get { return this.Context.Set<TEntity>(); }
    }

    public RepositoryService(IDbContext context)
    {
        this.Context = context;

    }

    public IQueryable<TEntity> GetAll()
    {
        return Entities.AsQueryable();
    }

    public TEntity GetById(object id)
    {
        return Entities.Find(id);
    }

    public void Insert(TEntity entity)
    {
        Entities.Add(entity);
    }

    public void Update(TEntity entity)
    {
        if (entity == null)
            throw new ArgumentNullException("entity");

        this.Context.SaveChanges();
    }

    public void Delete(TEntity entity)
    {
        Entities.Remove(entity);
    }

    public void Dispose()
    {
        Dispose(true);
        GC.SuppressFinalize(this);
    }
    protected virtual void Dispose(bool disposing)
    {
        if (disposing)
        {
            if (this.Context != null)
            {
                this.Context.Dispose();
                this.Context = null;
            }
        }
    }
}

新建一个类DataBaseInitializer为EF Code First数据库访问的初始化类。

public class DataBaseInitializer : IDatabaseInitializer<DataContext>
{
    public void InitializeDatabase(DataContext context)
    {
        context.Database.CreateIfNotExists();
    }
}

新建一个控制台程序来测试上面的代码Program.cs:

class Program
{
    static void Main(string[] args)
    {
        var context = new DataContext();
        var dataBaseInitializer = new DataBaseInitializer();
        dataBaseInitializer.InitializeDatabase(context);

        var categoryRepository = new RepositoryService<Category>(context);

        //Adding category in the category entity
        var category = new Category()
        {
            Name = "Baverage"
        };
        var products = new List<Product>();

        //Adding product in the product entity
        var product = new Product()
            {
                Name = "Soft Drink A",
                MinimumStockLevel = 50
            };
        products.Add(product);

        product = new Product()
        {
            Name = "Soft Drink B",
            MinimumStockLevel = 30
        };
        products.Add(product);

        category.Products = products;

        //Insert category and save changes
        categoryRepository.Insert(category);
        context.SaveChanges();

        ///////////////////////////////////////////////////////////////////////////////
        /////////////////For the next project we shall add Dependency Injection////////
        ////////////////But now we have add a Service layer for test manually//////////
        ///////////////////////////////////////////////////////////////////////////////
        IProductService productRepository = new ProductService();

        Console.WriteLine("\n");
        Console.WriteLine("Product List:");
        Console.WriteLine("-------------------------------------------------");
        foreach (var product1 in productRepository.GetAll())
        {
            Console.WriteLine(string.Format("Product Name : {0}",product1.Name));
            if (product1.Id == 9)
            {

                product1.Name = "Soft Drink AAA";
                productRepository.Update(product1);
            }
        }
        Console.WriteLine("Press any key to exit");
        Console.ReadKey();
    }
}

在配置文件添加数据库的链接。

App.config:

<?xml version="1.0" encoding="utf-8"?>
<configuration>
  <configSections>
    <!-- For more information
on Entity Framework configuration, visit
http://go.microsoft.com/fwlink/?LinkID=237468 -->
    <section name="entityFramework" 
             type="System.Data.Entity.Internal.ConfigFile.EntityFrameworkSection,
EntityFramework, Version=4.4.0.0, Culture=neutral,
PublicKeyToken=b77a5c561934e089" 
             requirePermission="false" />
  </configSections>
  <entityFramework>
    <defaultConnectionFactory type="System.Data.Entity.Infrastructure.SqlConnectionFactory,
EntityFramework" />
  </entityFramework>

  <connectionStrings>
      <add name="DataContext" 
         providerName="System.Data.SqlClient" 
         connectionString="Data
Source=YourSERVER;Initial Catalog=EfDBExistRepository;Integrated
Security=True;MultipleActiveResultSets=True;"/>
 
  </connectionStrings>
</configuration>

注意:数据库链接结点名为“DataContext”,正好和我们自己写的类“DataContext”名字一样。这样EF框架就可以自动找到这个数据库链接信息。

参考:http://www.codeproject.com/Articles/561584/Repository-Pattern-with-Entity-Framework-using


小编蓝狐