数据访问层无非就是对数据进行增删改查,其中增、删、改等我们可以抽象出来写一个公共的接口或抽象类来定义这些方法,并采用一个基类实现这些方法,这样该基类派生的子类都会继承增、删、改这些方法,这样我们就避免了每个实体都要重复实现这些方法。一句话概括就是:通过接口 泛型 与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=ERIC\SQLEXPRESS;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模式曾看了一天也没有理解其精髓。
每天学习一点点,每天进步一点点。