posts - 1,  comments - 0,  trackbacks - 0
公告

贴一个EF6 CodeFirst模式结合MVC5和Autofac(泛型注册)的一个入门实例

 

网上类似的例子实在太少,最近自己也有用到这一块的知识,总结了一下,不要让后人踩了自己踩过的坑。

 

1:新建三个项目,Web(MVC)、EntityFramework类库(EF框架)、Core类库(核心框架),nuget EntityFramework,Autofac,AutofacMvc5

 

2:建立简单对象:书籍(Book)Model,继承主键为Int的基类

①:接口

namespace:Core.Domain.Interface

 1 /// <summary>
 2     /// 实体对象接口
 3     /// </summary>
 4     public interface IEntity<PrimaryKeyType>
 5     {
 6         /// <summary>
 7         /// 主键
 8         /// </summary>
 9         PrimaryKeyType Id { get; }
10     }
IEntity

②:基类

namespace:Core.Domain.Base

 1     /// <summary>
 2     /// Int抽象基类
 3     /// </summary>
 4     /// <typeparam name="Type"></typeparam>
 5     public abstract class EntityBase : Interface.IEntity<int>
 6     {
 7         #region Properties
 8 
 9         /// <summary>
10         ///  数据状态
11         /// </summary>
12         public Enum.EntityEnumType Status { get; set; }
13 
14         public DateTime CreateDate { get; set; }
15 
16         public DateTime UpdateDate { get; set; }
17 
18         /// <summary>
19         /// 主键
20         /// </summary>
21         [Key]
22         public int Id { get; set; }
23         #endregion
24 
25         #region Constructor
26         public EntityBase()     :this(Enum.EntityEnumType.Normal,DateTime.Now,DateTime.Now)
27         { }
28 
29         /// <summary>
30         /// 带参初始化
31         /// </summary>
32         public EntityBase(Enum.EntityEnumType _status,DateTime _createDate,DateTime _updateDate)
33         {
34             Status = _status;
35             CreateDate = _createDate;
36             UpdateDate = _updateDate;
37         }
38         #endregion
39 
40         #region Methods
41 
42         #endregion
43 
44     }
EntityBase

③:书籍类

namespace:Web.Models

 1     [Table("Book")]//表名  
 2     /// <summary>
 3     /// 实体—书籍
 4     /// </summary>
 5     public class Book:EntityBase
 6     {
 7         #region Constructor
 8         public Book()
 9             : base()
10         { }
11         #endregion
12 
13         #region Fileds
14         /// <summary>
15         /// 书籍编号
16         /// </summary>
17         [DisplayName("书籍编号")]
18         public int BookCode { get; set; }
19 
20         /// <summary>
21         /// 书名
22         /// </summary>
23         [DisplayName("书名")]
24         [MaxLength(50,ErrorMessage = "书名过长"),MinLength(2,ErrorMessage = "书名过短")]
25         public string BookName { get; set; }
26 
27         /// <summary>
28         /// 作者
29         /// </summary>
30         [MaxLength(50,ErrorMessage = "作者姓名过长")]
31         [DisplayName("作者")]
32         public string Author { get; set; }
33 
34         #endregion
35 
36         #region 导航属性
37 
38         #endregion
39 
40 
41         #region IEntity成员
42         #endregion
43     }
Book

解释一下类的配置

CodeFirst 利用一种被称为约定Conventions)优于配置Configuration)的编程模式允许你使用自己的 对象 来表示 EF 所依赖的模型去执行查询、更改追踪、以及更新功能

简单的说,你创建的对象必须遵循EF给你指定的规则,比如最后两位以Id为结尾就是主键啊什么什么什么,反正我是没遵循。

如果你不想遵循EF的规则,那么——

Code First 提供了两种方式来配置你的类:

  • DataAnnotations, 使用简单属性;
  • Fluent API, 以编程命令行式的方式来描述你的配置

有关于配置的文章,请参考

EF官方文档:https://msdn.microsoft.com/en-us/library/ee712907(v=vs.113).aspx

 另外,有问题记得F1看文档,不要一直百度

3:仓储Repository设计

①:IRepository

namespace:Core.Repository

 1  public interface IRepository<TEntity> where TEntity:class
 2     {
 3         /// <summary>
 4         /// 添加一个对象
 5         /// </summary>
 6         /// <param name="item"></param>
 7         void Insert(TEntity item);
 8 
 9         /// <summary>
10         /// 删除一个对象
11         /// </summary>
12         void Delete(TEntity item);
13 
14         /// <summary>
15         /// 更新一个对象
16         /// </summary>
17         void Update(TEntity item);
18 
19         /// <summary>
20         /// 通过主键取对象
21         /// </summary>
22         /// <returns></returns>
23         TEntity Find(params object[] id);
24 
25         /// <summary>
26         /// 拿到可查询结果集
27         /// </summary>
28         /// <returns></returns>
29         System.Linq.IQueryable<TEntity> GetModel();
30 
31         /// <summary>
32         /// 设置数据上下文
33         /// </summary>
34         /// <param name="db"></param>
35         void SetDataContextByResloveName(string dbContext);
36     }
IRepository

SetDataContextByResloveName这个方法,到下文的Autofac配置时再说

②:IExtentionRepository 扩展

namespace:Core.IExtentionRepository

 1     /// <summary>
 2     /// 扩展仓储
 3     /// </summary>
 4     /// <typeparam name="TEntity"></typeparam>
 5     public interface IExtentionRepository<TEntity>:IRepository<TEntity> where TEntity:class
 6     {
 7         #region 行为
 8         /// <summary>
 9         /// 添加集合
10         /// </summary>
11         /// <param name="item"></param>
12         void Insert(IEnumerable<TEntity> item);
13         /// <summary>
14         /// 修改集合
15         /// </summary>
16         /// <param name="item"></param>
17         void Update(IEnumerable<TEntity> item);
18         /// <summary>
19         /// 删除集合
20         /// </summary>
21         /// <param name="item"></param>
22         void Delete(IEnumerable<TEntity> item);
23         #endregion
24 
25         #region 查询
26         /// <summary>
27         /// 根据指定lambda表达式,得到结果集
28         /// </summary>
29         /// <param name="predicate"></param>
30         /// <returns></returns>
31         IQueryable<TEntity> GetModel(Expression<Func<TEntity, bool>> predicate);
32 
33         /// <summary>
34         /// 根据指定lambda表达式,得到第一个实体
35         /// </summary>
36         /// <param name="predicate"></param>
37         /// <returns></returns>
38         TEntity Find(Expression<Func<TEntity, bool>> predicate);
39         #endregion
40     }
IExtentionRepository

③:EFRepository EF仓储

 namespace:EntityFramework

  1 public class EFRepository<TEntity> : 
  2         IRepository<TEntity>,
  3         IExtentionRepository<TEntity>
  4         where TEntity:class
  5     {
  6         #region Properties
  7         /// <summary>
  8         /// EF上下文
  9         /// </summary>
 10         private DbContext EFDbContext;
 11         #endregion
 12 
 13         #region Constructors
 14         public EFRepository()
 15             : this(null)
 16         { }
 17 
 18         /// <summary>
 19         /// 带参构造函数
 20         /// </summary>
 21         public EFRepository(DbContext db)
 22         {            
 23             EFDbContext = db;
 24         }
 25         #endregion
 26 
 27         #region Methods
 28         /// <summary>
 29         /// 提交到数据库
 30         /// </summary>
 31         public void SaveChanges()
 32         {
 33             try
 34             {
 35                 EFDbContext.SaveChanges();
 36             }
 37             catch (Exception ex)
 38             {
 39                 //保存日志
 40                 //抛出异常
 41                 throw new MithrilCommonException(ex.Message, ex);
 42             }
 43         }
 44 
 45 
 46         #endregion
 47 
 48         #region IReposiotry<TEntity>
 49 
 50         /// <summary>
 51         /// 少量数据插入
 52         /// </summary>
 53         /// <param name="item"></param>
 54         public void Insert(TEntity item)
 55         {
 56             if (item != null)
 57             {
 58                 EFDbContext.Entry<TEntity>(item as TEntity);
 59                 EFDbContext.Set<TEntity>().Add(item as TEntity);
 60                 this.SaveChanges();
 61             }
 62         }
 63 
 64         /// <summary>
 65         /// 删除一个实体
 66         /// </summary>
 67         /// <param name="id"></param>
 68         public void Delete(TEntity item)
 69         {
 70             if (item != null)
 71             {
 72                 //将实体以"UnChanged"的状态放置到上下文中
 73                 EFDbContext.Set<TEntity>().Attach(item as TEntity);
 74                 //给定实体标记为“已删除”
 75                 EFDbContext.Entry(item).State = EntityState.Deleted;
 76                 //EFDbContext.Set<TEntity>().Remove(item as TEntity);
 77                 this.SaveChanges();
 78             }
 79         }
 80 
 81         /// <summary>
 82         /// 更新一个实体
 83         /// </summary>
 84         /// <param name="item"></param>
 85         public void Update(TEntity item)
 86         {
 87             if(item != null)
 88             {
 89                 //将实体以"UnChanged"的状态放置到上下文中
 90                 EFDbContext.Set<TEntity>().Attach(item as TEntity);
 91                 //更新
 92                 EFDbContext.Entry(item).State = EntityState.Modified;
 93                 this.SaveChanges();
 94             }
 95         }
 96 
 97         /// <summary>
 98         /// 根据id获取实体
 99         /// </summary>
100         /// <param name="id"></param>
101         /// <returns></returns>
102         public TEntity Find(params object[] id)
103         {
104             return EFDbContext.Set<TEntity>().Find(id);
105         }
106 
107         /// <summary>
108         /// 拿到可查询结果集
109         /// </summary>
110         /// <returns></returns>
111         System.Linq.IQueryable<TEntity> GetModel()
112         {
113             return null;
114         }
115 
116         //设置数据上下文
117         public void SetDataContextByResloveName(string contextName)
118         {
119             try
120             {
121                 EFDbContext = ServiceLocator.Instance.GetService<DbContext>(contextName);
122             }
123             catch (Exception)
124             {
125                 throw new ArgumentException("设置EFContext出错");
126             }
127 
128         }
129 
130         /// <summary>
131         /// 拿到可查询结果集
132         /// </summary>
133         /// <returns></returns>
134         IQueryable<TEntity> IRepository<TEntity>.GetModel()
135         {
136             throw new NotImplementedException();
137         }
138 
139 
140         #endregion
141 
142         #region IExtentionRepository
143         public void Insert(IEnumerable<TEntity> item)
144         {
145             foreach (var entity in item)
146             {
147                 EFDbContext.Entry<TEntity>(entity as TEntity);
148                 EFDbContext.Set<TEntity>().Add(entity as TEntity);
149             }
150             this.SaveChanges();
151         }
152 
153         public void Update(IEnumerable<TEntity> item)
154         {
155             #region 给每个对象改变状态
156             foreach (var model in item)
157             {
158                 EFDbContext.Set<TEntity>().Attach(model as TEntity);
159                 EFDbContext.Entry(model).State = EntityState.Modified;
160                 this.SaveChanges();
161             }
162             #endregion
163         }
164 
165         public void Delete(IEnumerable<TEntity> item)
166         {
167             throw new NotImplementedException();
168         }
169 
170         public IQueryable<TEntity> GetModel(Expression<Func<TEntity, bool>> predicate)
171         {
172             throw new NotImplementedException();
173         }
174 
175         public TEntity Find(Expression<Func<TEntity, bool>> predicate)
176         {
177             throw new NotImplementedException();
178         }
179         #endregion
180     }
EFRepository

 其中抛出的Exception为自定义的异常类

4:CodeFirst迁移

vs17中:视图 > 其他窗口 > 程序包管理控制台

默认项目栏中选择相应的项目,

①:Enable-Migrations -ContextTypeName -Force

为你制定的上下文开始迁移工作,-ContextTypeName 为你的上下文名称,-Force为可选项,是否覆盖你所选的上下文的迁移

运行后,会生成Migration文件夹

②:Add-Migration -MigrationName -Force

当你新建数据库,添加字段、删除、更新字段时,输入此命令,根据你DbContext实现类的策略以及之前对象的约束,CodeFirst会生成数据库以及相应的表

③:Update-DataBase,将改动对应到实体库上

贴一个测试DbContext类

 1 public class EFTestContext : DbContext
 2     {
 3 
 4         public EFTestContext()
 5             : base("name=EFTestContext")
 6         {
 7             //模型变更时更新数据库
 8             Database.SetInitializer<EFTestContext>(new CreateDatabaseIfNotExists<EFTestContext>());
 9         }
10 
11         public DbSet<Book> Book { get; set; }
12 
13         protected override void OnModelCreating(DbModelBuilder modelBuilder)
14         {
15             modelBuilder.Entity<Book>().MapToStoredProcedures();
16             base.OnModelCreating(modelBuilder);
17         }
18     }
EFTestContext

5:MVC5 Autofac配置

 网上有很多例子,并不符合实际开发,这里重点说一下一个接口有很多实现类的情况,应该如何配置Autofac

①:Global.asax配置

 1  protected void Application_Start()
 2         {
 3             AreaRegistration.RegisterAllAreas();
 4             FilterConfig.RegisterGlobalFilters(GlobalFilters.Filters);
 5             RouteConfig.RegisterRoutes(RouteTable.Routes);
 6             BundleConfig.RegisterBundles(BundleTable.Bundles);
 7 
 8             IoCConfig();
 9         }
10 
11         /// <summary>
12         /// IoC注入
13         /// </summary>
14         private void IoCConfig()
15         {
16             var builder = new ContainerBuilder();
17             #region Repository注册
18             builder.RegisterGeneric(typeof(EFRepository<>)).Named("EFRepository",typeof(IRepository<>)).InstancePerLifetimeScope();
19             #endregion
20 
21             #region Service注册
22             builder.RegisterType(typeof(EFTestContext)).Named("EF", typeof(DbContext)).InstancePerLifetimeScope();
23             #endregion
24 
25             //注册 Controller 
26             builder.RegisterControllers(typeof(MvcApplication).Assembly);
27             var container = builder.Build();
28             //MVC扩展
29             DependencyResolver.SetResolver(new AutofacDependencyResolver(container));
30         }
泛型配置

泛型配置看官方文档

http://docs.autofac.org/en/latest/advanced/adapters-decorators.html

②:ServiceLocator,用于实现同一接口不同实现的Autofac Reslove

 1 public sealed class ServiceLocator
 2     {
 3         #region Constructor
 4         public ServiceLocator()
 5         {
 6             
 7         }
 8         #endregion
 9 
10         #region Singleton
11         private static readonly ServiceLocator _instance = new ServiceLocator();
12 
13         /// <summary>
14         /// 实例
15         /// </summary>
16         public static ServiceLocator Instance
17         {
18             get
19             {
20                 return _instance;
21             }
22         }
23         #endregion
24 
25         #region Public Methods
26         public TEntity GetService<TEntity>(string resloverName)
27         {
28             return AutofacDependencyResolver.Current.RequestLifetimeScope.ResolveNamed<TEntity>(resloverName);
29         }
30         #endregion
31     }
ServiceLocator

6:MVC Web测试

 1         //获取指定的IoC实例
 2         //Author:Yannefer716
 3         IRepository<Book> bookRepository = ServiceLocator.Instance.GetService<IRepository<Book>>("EFRepository");
 4 
 5         #endregion
 6         public HomeController()
 7         {
 8 
 9         }
10 
11         public ActionResult Index()
12         {
13             var Book = new Book()
14             {
15                 BookCode = 1,
16                 BookName = "Test"
17             };
18             bookRepository.SetDataContextByResloveName("EF");
19             bookRepository.Insert(Book);
20 
21             return View();
22         }
23 }    
HomeController

由于Autofac注册的是泛型,并不知道具体的实现是哪一个,在注册Controller时需要提供一个无参构造函数。

SetDataContextByResloveName 方法指定是哪个DbContext,可在Global中设置多个Context。

关于Autofac对Mvc具体的实现机制,下篇再讨论。

 

(转载请注明)

 

 

 

 

 

 

 

 

posted on 2018-03-09 16:35 Yennefer716 阅读(...) 评论(...) 编辑 收藏