今天记录一下自己的EntityFramework数据访问层。这里用通过泛型Repository的方式实现了数据的访问。先上一张结构图。

     
原文链接:

图片 1

系列目录:

Configuration文件夹里面的类是全部实体映射类。这些类全部继承至EntityConfigurationBase类。

 

EntityConfigurationBase又继承至EntityTypeConfiguration类,这是EntityFramework的实体映射基类

  • Relationship in Entity Framework Using Code First Approach With
    Fluent API【【使用EF Code-First方式和Fluent
    API来探讨EF中的关系】】
  • Code First Migrations with Entity Framework【使用EF
    做数据库迁移】
  • CRUD Operations Using Entity Framework 5.0 Code First Approach in
    MVC【在MVC中使用EF
    5.0做增删查改】
  • CRUD Operations Using the Repository Pattern in
    MVC【在MVC中使用仓储模式,来做增删查改】
  • CRUD Operations Using the Generic Repository Pattern and Unit of
    Work in
    MVC【在MVC中使用泛型仓储模式和工作单元来做增删查改】
  • CRUD Operations Using the Generic Repository Pattern and Dependency
    Injection in
    MVC【在MVC中使用泛型仓储模式和依赖注入,来做增删查改】

图片 2图片 3

 
 

 1 using System.Data.Entity.ModelConfiguration; 2 using System.Data.Entity.ModelConfiguration.Configuration; 3  4 using ZY.Core.Entities; 5  6 namespace ZY.Repositories.EntityFramework 7 { 8     /// <summary> 9     /// 数据实体映射配置基类10     /// </summary>11     /// <typeparam name="TEntity"></typeparam>12     /// <typeparam name="TKey"></typeparam>13     public abstract class EntityConfigurationBase<TEntity, TKey> : EntityTypeConfiguration<TEntity>, IEntityMapper14         where TEntity : class15     {16         //映射实体添加到数据上下文17         public void RegistorTo(ConfigurationRegistrar configurations)18         {19             configurations.Add(this);20         }21     }22 }

源代码下载:

View Code

 
这篇文章,我将会介绍在ASP.NET
MVC应用程序中使用泛型仓储模式和工作单元。我将开发一个程序,对Book实体进行增删查改,为了保证这篇文章简单,便于大家理解泛型仓储模式和工作单元,在这篇文章中,我将只会使用一个Book实体。

这里有个重要的方法就是RegistorTo(ConfigurationRegistrar configurations)
这个方法是将当前实体添加到数据上下文。这样不用在数据上下文写每一个实体的映射,将实体与上下文解耦出来了。在OnModelCreating(DbModelBuilder
modelBuilder)
方法里面用到了反射,通过反射将所有实体映射关系添加到数据上下文中。这里可以优化一下就是,反射的时候可以用缓存。

在上篇文章中“4.CRUD
Operations Using the Repository Pattern in
MVC【在MVC中使用仓储模式进行增删查改】“讲到了仓储模式,里面我们为Book实体,创建了一个仓储类,但是这个仓储仅仅是只能为一个实体服务的。试想一下,如果是真正的企业级开发,我们会有很多实体,难道,我们要为每一个实体都创建一个仓储类么???显然是不现实的。对于这个问题,我们需要创建一个可以为所有实体公用的仓储,所以这里引入泛型仓储。这样就避免了重复编码。

图片 4图片 5

下面的图中,讲到了两个开发者,讨论一个问题:”是否需要创建一个新的零件或者是利用已经存在的零件?”
 ——如果你选择第一种,那么就是这篇文章讲到的,”4.CRUD Operations
Using the Repository Pattern in
MVC【在MVC中使用仓储模式进行增删查改】”

 1 using System; 2 using System.Collections.Generic; 3 using System.Linq; 4 using System.Text; 5 using System.Threading.Tasks; 6 using System.Data.Entity; 7 using System.Data.Entity.ModelConfiguration.Conventions; 8 using System.Reflection; 9 10 namespace ZY.Repositories.EntityFramework11 {12     public class BaseDbContext : DbContext13     {14         public BaseDbContext()15             : base("Default")16         { }17 18         public BaseDbContext(string connectionString)19             :base(connectionString)20         { }        21 22         protected override void OnModelCreating(DbModelBuilder modelBuilder)23         {24             //关闭级联删除25             modelBuilder.Conventions.Remove<OneToManyCascadeDeleteConvention>();26             //获取所有映射实体类27             IEnumerable<IEntityMapper> entityMappers = GetEntityMappers().Select(type => Activator.CreateInstance as IEntityMapper).ToList();28 29             foreach (IEntityMapper mapper in entityMappers)30             {31                 mapper.RegistorTo(modelBuilder.Configurations);32             }33         }34 35         /// <summary>36         /// 通过反射 获取所有实体映射对象 优化的做法是保存在缓存中37         /// </summary>38         /// <returns></returns>39         private Type[] GetEntityMappers()40         {41             Type[] mapperTypes = Assembly.GetExecutingAssembly().GetTypes()42             .Where(type => !String.IsNullOrEmpty(type.Namespace))43             .Where(type => type.BaseType != null && type.BaseType.IsGenericType &&44             type.BaseType.GetInterface(typeof(IEntityMapper).Name) == typeof(IEntityMapper)).ToArray();45             return mapperTypes;46         }47     }48 }

但我在这里选择第二种,也就是这篇文章,我将要讲到的。–使用泛型仓储模式来重用代码,减少冗余代码。

View Code

图片 6

数据迁移用了自动迁移,之前刚刚开始用EF的时候没有用自动迁移,遇到了很多坑,自从用了自动迁移,就没有管过迁移的事情了。

 

图片 7图片 8

既然说到仓储,那么什么是仓储模式呢?

 1 using System.Data.Entity.Migrations; 2  3  4 namespace ZY.Repositories.EntityFramework.Migrations 5 { 6     /// <summary> 7     /// 自动迁移设置 8     /// </summary> 9     public class AutoMigrationsConfiguration : DbMigrationsConfiguration<BaseDbContext>10     {11         public AutoMigrationsConfiguration()12         {13             AutomaticMigrationsEnabled = true;//自动迁移14             AutomaticMigrationDataLossAllowed = true;//允许数据丢失15         }16     }17 }

仓储模式旨在数据访问层和业务逻辑层之间创建一个抽象层。仓储模式是数据访问模式,旨在达到数据访问的更松散的耦合性。我们在单独的类,或者类库中创建数据访问的逻辑,这就是仓储。仓储的职责就是和业务逻辑层进行通信。

View Code

在这篇文章中,我将会为所有的实体设计一个公共的泛型仓储,另外还有一个工作单元类。这个工作单元类,为每个实体创建仓储的实例,然后仓储实例用来做增删查改操作。我在控制器中创建工作单元类的实例,然后依据具体的实体,创建仓储的实例,然后就可以使用仓储中的方法进行每个操作了。

Repository的代码在上一篇中已经贴出来了。实现了异步和同步的方法。

下面的图表显示了仓储和EF数据上下文之间的关系。在图中,MVC控制器直接通过工作单元和仓储进行交互,而不是直接和EF进行交互。

后续会将整个代码放到github上面

图片 9

 

 

讲到这里,大家可能就会有疑问了,为什么要使用工作单元呢???

工作单元就像它的名字一样,做某件事情。在这篇文章中,工作单元主要做的是:我们创建工作单元的实例,然后工作单元为我们初始化EF数据上下文,然后每个仓储的实例都使用同一个数据上下文实例进行数据库操作。因此工作单元就是,用来确保所有的仓储实例都使用同一个数据上下文实例。

 

好了理论到此为止,讲的差不多了。现在我们开始进入正题:

 

先看看项目的结构:

图片 10

 

这三个项目就不用做过多介绍了吧,前面的文章已经说了很多次了…

 

EF.Entity类库中,添加BaseEntity实体:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace EF.Entity
{
    public abstract class BaseEntity
    {
        /// <summary>
        /// ID编号
        /// </summary>
        public int ID { get; set; }

        /// <summary>
        /// 添加时间
        /// </summary>
        public DateTime AddedDate { get; set; }

        /// <summary>
        /// 修改时间
        /// </summary>
        public DateTime ModifiedDate { get; set; }

        /// <summary>
        /// IP地址
        /// </summary>
        public string IP { get; set; }


    }
}

Book实体:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace EF.Entity
{
   public class Book:BaseEntity
    {
       /// <summary>
       /// 书名
       /// </summary>
       public string Title { get; set; }

       /// <summary>
       /// 作者
       /// </summary>
       public string Author { get; set; }

       /// <summary>
       /// ISBN编号
       /// </summary>
       public string ISBN { get; set; }

       /// <summary>
       /// 出版时间
       /// </summary>
       public DateTime PublishedDate { get; set; }
    }
}

Entity.Data类库中BookMap类:

using EF.Entity;
using System;
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations.Schema;
using System.Data.Entity.ModelConfiguration;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace EF.Data
{
    public class BookMap:EntityTypeConfiguration<Book>
    {
        public BookMap()
        {
            //配置主键
            this.HasKey(s => s.ID);

            //配置字段
            this.Property(s => s.ID).HasDatabaseGeneratedOption(DatabaseGeneratedOption.Identity);
            this.Property(s => s.Author).HasColumnType("nvarchar").HasMaxLength(50).IsRequired();
            this.Property(s => s.AddedDate).IsRequired();
            this.Property(s => s.IP).IsOptional();
            this.Property(s => s.ISBN).HasColumnType("nvarchar").HasMaxLength(50).IsRequired();
            this.Property(s => s.ModifiedDate).IsOptional();
            this.Property(s => s.PublishedDate).IsRequired();
            this.Property(s => s.Title).HasColumnType("nvarchar").HasMaxLength(50).IsRequired();

            //配置表名
            this.ToTable("Books");
        }
    }
}

EF数据上下文类:

using System;
using System.Collections.Generic;
using System.Data.Entity;
using System.Data.Entity.ModelConfiguration;
using System.Linq;
using System.Reflection;
using System.Text;
using System.Threading.Tasks;

namespace EF.Data
{
   public class EFDbContext:DbContext
    {
       public EFDbContext()
           : base("name=DbConnectionString")
       { }

       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.Data项目中创建泛型仓储类,里面提供了增删查改的方法,这个泛型的仓储类中,有一个带DbContext参数的构造函数,所以当我们实例化仓储的时候,传递一个数据上下文对象给仓储,所有的实体就可以使用同一个数据上下文对象了。我们使用了数据上下文的SaveChange方法,但是你同样可以使用工作单元类的Save方法,因为这两者使用的是同一个数据上下文对象,下面是泛型的仓储类代码:【为了使文章更容易理解,这里我不创建泛型的仓储接口】。

 

图片 11图片 12

  1 using EF.Entity;
  2 using System;
  3 using System.Collections.Generic;
  4 using System.Data.Entity;
  5 using System.Data.Entity.Validation;
  6 using System.Linq;
  7 using System.Text;
  8 using System.Threading.Tasks;
  9 
 10 namespace EF.Data
 11 {
 12     /// <summary>
 13     /// 泛型仓储类
 14     /// </summary>
 15     /// <typeparam name="T"></typeparam>
 16    public class Repository<T> where T:BaseEntity
 17     {
 18        /// <summary>
 19        /// 数据上下文变量
 20        /// </summary>
 21        private readonly EFDbContext db;
 22        string errorMessage = string.Empty;
 23 
 24        #region 封装属性
 25        /// <summary>
 26        /// 实体访问字段
 27        /// </summary>
 28        private IDbSet<T> entities;
 29 
 30        /// <summary>
 31        /// 封装属性
 32        /// </summary>
 33        public IDbSet<T> Entities
 34        {
 35            get
 36            {
 37                if (entities == null)
 38                {
 39                    return entities = db.Set<T>();
 40                }
 41                else
 42                {
 43                    return entities; 
 44                }
 45            }
 46        } 
 47        #endregion
 48 
 49        #region 构造函数
 50        public Repository(EFDbContext context)
 51        {
 52            this.db = context;
 53        } 
 54        #endregion
 55 
 56        #region 泛型方法--根据Id查找实体
 57        /// <summary>
 58        /// 泛型方法--根据Id查找实体
 59        /// </summary>
 60        /// <param name="id"></param>
 61        /// <returns></returns>
 62        public T GetById(int id)
 63        {
 64            return this.entities.Find(id);
 65        } 
 66        #endregion
 67 
 68        #region Insert
 69        public void Insert(T entity)
 70        {
 71            try
 72            {
 73                if (entity == null)
 74                {
 75                    throw new ArgumentNullException("entity");
 76                }
 77                else
 78                {
 79                    this.Entities.Add(entity);//把实体的状态设置为Added
 80                    this.db.SaveChanges();//保存到数据库
 81                }
 82            }
 83            catch (DbEntityValidationException dbEx)//DbEntityValidationException
 84            {
 85                foreach (var validationErrors in dbEx.EntityValidationErrors)
 86                {
 87                    foreach (var item in validationErrors.ValidationErrors)
 88                    {
 89                        errorMessage += string.Format("Property:{0} Error:{1}", item.PropertyName, item.ErrorMessage) + Environment.NewLine;
 90                    }
 91                }
 92                throw new Exception(errorMessage, dbEx);
 93            }
 94        } 
 95        #endregion
 96 
 97        #region Update
 98        public void Update(T entity)
 99        {
100            try
101            {
102                if (entity == null)
103                {
104                    throw new ArgumentNullException("entity");
105                }
106                else
107                {
108                    this.db.SaveChanges();//保存到数据库
109                }
110            }
111            catch (DbEntityValidationException dbEx)
112            {
113                foreach (var validationErrors in dbEx.EntityValidationErrors)
114                {
115                    foreach (var error in validationErrors.ValidationErrors)
116                    {
117                        errorMessage += string.Format("Property:{0} Error:{1}", error.PropertyName, error.ErrorMessage) + Environment.NewLine;
118                    }
119                }
120                //抛出捕获的异常
121                throw new Exception(errorMessage, dbEx);
122            }
123        } 
124        #endregion
125 
126        #region Delete
127        public void Delete(T entity)
128        {
129            try
130            {
131                if (entity == null)
132                {
133                    throw new ArgumentException("entity");
134                }
135                else
136                {
137                    this.db.Entry(entity).State = EntityState.Deleted;
138                    this.db.SaveChanges();
139                }
140            }
141            catch (DbEntityValidationException dbEx)
142            {
143                foreach (var validationErrors in dbEx.EntityValidationErrors)
144                {
145                    foreach (var error in validationErrors.ValidationErrors)
146                    {
147                        errorMessage += string.Format("Property:{0} Error:{1}", error.PropertyName, error.ErrorMessage) + Environment.NewLine;
148                    }
149                }
150 
151                throw new Exception(errorMessage, dbEx);
152            }
153        } 
154        #endregion
155 
156        #region Table
157        public virtual IQueryable<T> Table
158        {
159            get
160            {
161                return this.Entities;
162            }
163        } 
164        #endregion
165 
166 
167     }
168 }

View
Code

发表评论

电子邮件地址不会被公开。 必填项已用*标注