使用 EntityTypeConfiguration 的 Entity Framework 存储库模式
使用 EntityTypeConfiguration 的 Entity Framework 存储库模式
引言
我一直在寻找使用 Entity Framework Code First 和 EntityTypeConfiguration
的仓储模式的集成示例,但未能找到。在此,我只涉及基础知识。这是我的第一篇文章。
背景
为了使本文简单易懂,我使用了两个简单的对象:Category
和 Product
。
我使用 EntityTypeConfiguration
来实现不同层与数据库的交互以及字段验证。
以下是解决方案的结构树。
**注意:我们需要在 EfRepPatTest.Data
项目中添加 Entity Framework 引用。
开发工具
- VS 2010
- Entity Framework 库
Using the Code
首先,我们将在解决方案中创建三个项目,其中两个是类库,另一个是用于实现的控制台项目。
解决方案名称 | EfRepositoryPatterTest |
类库名称 | EfRepPatTest.Entity EfRepPatTest.Data |
控制台应用程序 | EfRepPatTest.Implementation |
在 EfRepPatTest.Entity
项目中,我们将创建以下类
BaseEntity.cs
public class BaseEntity<T>
{
public T Id { get; set; }
}
IRepository.cs [接口]
其中包含以下操作
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
它将继承 BaseEntity
类以获取 Id
属性并将 Id
字段指定为整数。
public class Category:BaseEntity<int>
{
public virtual string Name { get; set; }
public List<Product> Products { get; set; }
}
Product.cs
与 category
类相同。
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; }
}
EfRepPatTest.Data
项目包含以下类或接口。
在此项目中添加 EntityFramework
和 EfRepPatTest.Entity
的引用。
首先,创建一个驱动 DbContext
类的接口,即 DataContext
类将使用的接口。
IDbContext.cs
public interface IDbContext
{
IDbSet<TEntity> Set<TEntity>() where TEntity:class;
int SaveChanges();
void Dispose();
}
此处,IDbSet
接口属于 System.Data.Entity
命名空间。创建一个名为 DataContext
的类,该类将继承 DbContext
并使用 IDbContext
。
DataContext.cs [部分]
public class DataContext: DbContext,IDbContext
{
public new IDbSet<TEntity> Set<TEntity>() where TEntity : class
{
return base.Set<TEntity>();
}
}
在讨论 DataContext
类的功能之前,我们需要另外两个类,它们将 Entity
与 EntityTypeConfiguration
进行映射,并保存数据库表的 Field Validation
逻辑。
创建文件夹名 "Mapping",并将 CategoryMap
和 ProductMap
类放在其中 [可选]。
CategoryMap.cs
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
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);
}
}
以上部分将帮助我们避免直接使用 Entity [Product
和 Category
] 作为数据库表,而是涵盖数据库表逻辑和验证。
现在,在 DataContext
类中添加 "OnModelCreating
" 方法,用于将 Entity 作为模型的一部分附加。以下代码片段将演示如何手动配置由 EntityTypeConfiguration
驱动的类 [ProductMap
, CategoryMap
] 与 ModelBuilder
。
{
modelBuilder.Configurations.Add(new CategoryMap());
modelBuilder.Configurations.Add(new ProductMap());
base.OnModelCreating(modelBuilder);
}
这里的问题是所有类都需要手动配置。因此,我希望避免这样做。以下代码将帮助我们自动完成此操作。
DataContext.cs [完整]
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);
}
}
我们尚未实现 IRepository
接口,通过 RepositoryService
类,我们将实现它。
RepositoryService.cs
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;
}
}
}
}
"private IDbSet<TEntity
> Entities
" 属性将返回 DbSet
实例,以便在上下文中访问给定类型的实体。
现在,您需要一个用于初始化数据库的类。添加一个名为 DataBaseInitializer
的类。
DataBaseInitializer.cs
public class DataBaseInitializer : IDatabaseInitializer<DataContext>
{
public void InitializeDatabase(DataContext context)
{
context.Database.CreateIfNotExists();
}
}
通过 EfRepPatTest.Implementation
控制台项目实现应用程序
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();
}
}
在此,我们添加了 "EfRepPatTest.Service
" 项目来测试服务。这将在下一篇文章中有所帮助。
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>
连接字符串的名称 [name="DataContext"]
与 DataContext.cs 类相同,它将自动工作。
希望有所帮助!
下一篇文章
历史
- 2013 年 3 月 20 日:初始版本