数据访问层无非就是对数据进行增删改查,其中增、删、改等我们可以抽象出来写一个公共的接口或抽象类来定义这些方法,并采用一个基类实现这些方法,这样该基类派生的子类都会继承增、删、改这些方法,这样我们就避免了每个实体都要重复实现这些方法。一句话概括就是:通过接口 泛型 与ORM结合 实现了数据访问层更好的复用。
在《企业架构模式》中,译者将Repository翻译为资源库。给出如下说明:通过用来访问领域对象的一个类似集合的接口,在领域与数据映射层之间进行协调。
下面我们就用EF来实现一个简单的Repository模式
1、我们对实体的公共操作部分,提取为IRepository接口,比如常见的增加,删除、修改等方法。如下代码
我们发现接口的泛型TEntity有一个约束需要继承BaseEntity,BaseEntity就是把实体中公共的属性抽取出来,比如:Id(主键),CreateDate(创建时间)等。
using System; using System.Collections.Generic; using System.Linq; using System.Text; using Repository.Model; using System.Data.Entity; namespace Repository.Data { public interface IRepository<TEntity> where TEntity:BaseEntity { DbSet<TEntity> Entities { get; } //增加单个实体 int Insert(TEntity entity); //增加多个实体 int Insert(IEnumerable<TEntity> entities); //更新实体 int Update(TEntity entity); //删除 int Delete(object id); //根据逐渐获取实体 TEntity GetByKey(object key); } }
2、BaseEntity类
BaseEntity类中定义了所有参加数据操作实体的公共属性,因此我们把该类定义为抽象类,作为派生类的的基类。代码如下:
using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.ComponentModel.DataAnnotations; namespace Repository.Model { public abstract class BaseEntity { public BaseEntity() { Id = Guid.NewGuid(); CreateDate = DateTime.Now; } [Key] public Guid Id { get; set; } public DateTime CreateDate { get; set; } } }
3、IRepository接口定义完毕,肯定需要一个雷来实现接口中的方法,下面我们定义一个抽象类EFRepositoryBase来实现该接口方法
我们用一个抽象类EFRepositoryBase来实现接口中的方法,这样派生的类都具有接口中定义的方法,也防止EFRepositoryBase直接被实例化,下面我们直接看代码:
using System; using System.Collections.Generic; using System.Linq; using System.Text; using Repository.Model; using System.Data.Entity; namespace Repository.Data { public abstract class EFRepositoryBase<TEntity> : IRepository<TEntity> where TEntity:BaseEntity { DemoDbContext Db = new DemoDbContext(); #region IRepository<TEntity> 成员 public DbSet<TEntity> Entities { get { return Db.Set<TEntity>(); } } public int Insert(TEntity entity) { Db.Set<TEntity>().Add(entity); return Db.SaveChanges(); } public int Insert(IEnumerable<TEntity> entities) { throw new NotImplementedException(); } public int Update(TEntity entity) { return 0; } public int Delete(object id) { throw new NotImplementedException(); } public TEntity GetByKey(object key) { return Db.Set<TEntity>().Find(key); } #endregion } }
因为我们用的EF作为数据访问,因此我们需要定义一个数据上下文,代码如下
using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Data.Entity; using Repository.Model; using System.Data.Entity.Migrations; using System.Data.Entity.ModelConfiguration.Conventions; namespace Repository.Data { public class DemoDbContext : DbContext { public DemoDbContext() : base("default") { Database.SetInitializer<DemoDbContext>(new MigrateDatabaseToLatestVersion<DemoDbContext, ReportingDbMigrationsConfiguration>()); } public DbSet<Member> Members { get; set; } public DbSet<Score> Scores { get; set; } protected override void OnModelCreating(DbModelBuilder modelBuilder) { modelBuilder.Conventions.Remove<OneToManyCascadeDeleteConvention>(); modelBuilder.Entity<Member>().HasMany(b => b.Scores); } } internal sealed class ReportingDbMigrationsConfiguration : DbMigrationsConfiguration<DemoDbContext> { public ReportingDbMigrationsConfiguration() { AutomaticMigrationsEnabled = true;//任何Model Class的修改將會直接更新DB AutomaticMigrationDataLossAllowed = true; } } }
<connectionStrings> <add name="default" connectionString="Data Source=ERICSQLEXPRESS;Initial Catalog=EfSample;Integrated Security=True" providerName="System.Data.SqlClient" /> </connectionStrings>
我们一共定义两个实体,一个是Members(学生)类和Scores(成绩)类,Members与Scores为一对多的关系。
4、Members类和Scores类,都要继承BaseEntity基类
using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.ComponentModel.DataAnnotations.Schema; namespace Repository.Model { [Table("tb_Member",Schema="dbo")] public class Member:BaseEntity { public int Num { get; set; } public string UserName { get; set; } public string Sex { get; set; } public int Age { get; set; } [ForeignKey("MemberId")] public virtual ICollection<Score> Scores { get; set; } } }
using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.ComponentModel.DataAnnotations.Schema; namespace Repository.Model { [Table("tb_Score",Schema="dbo")] public class Score:BaseEntity { public Guid MemberId { get; set; } public double Scores { get; set; } public Guid courseId { get; set; } } }
基础工作都已经完成了,下面我们来看 MemberRepository.cs类和ScoreRespository.cs类。
所有的数据操作都在EFRepositoryBase.cs中实现了,因此MemberRepository.cs和ScoreRespository.cs只需要继承EFRepositoryBase,即可实现增删改查。
1、MemberRepository.cs
MemberRepository为实体Member的操作类,因此EFRepositoryBase基类中的泛型被替换成实体Member,这样该类中就已经有了对Member的增删改查操作,我们也可以在MemberRepository中定义其他方法。
using System; using System.Collections.Generic; using System.Linq; using System.Text; using Repository.Model; namespace Repository.Data { public class MemberRepository : EFRepositoryBase<Member>, IRepository<Member> { } }
2、ScoreRespository.cs
ScoreRespository与MemberRepository一样,只不过是对实体Score的操作。
3、简单测试
public void test() { MemberRepository mr = new MemberRepository(); var entity = new Member() { UserName = "eric", Age = 25, Sex = "男" }; mr.Insert(entity); var score = new Score() { MemberId = entity.Id, Scores = 80, courseId = Guid.NewGuid() }; ScoreRespository sr = new ScoreRespository(); sr.Insert(score); }
我们发现数据操作成功。
一般Repository都会跟Unit of Work模式联合使用,如果你有好的学习资料欢迎分享,Unit of Work模式曾看了一天也没有理解其精髓。
每天学习一点点,每天进步一点点。