• 5.在MVC中使用泛型仓储模式和工作单元来进行增删查改


     原文链接:http://www.c-sharpcorner.com/UploadFile/3d39b4/crud-operations-using-the-generic-repository-pattern-and-uni/

    系列目录:

       

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

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

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

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

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

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

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

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

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

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

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

    先看看项目的结构:

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

    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方法,因为这两者使用的是同一个数据上下文对象,下面是泛型的仓储类代码:【为了使文章更容易理解,这里我不创建泛型的仓储接口】。

     View Code

    下面创建工作单元类,UnitOfWork,这个工作单元类继承自IDisposable接口,所以它的实例将会在每个控制器中被 释放掉。工作单元类初始化了程序的上下文,工作单元类的核心就是Repository<T>() 类型的泛型方法,这个方法为继承自BaseEntity的每个实体返回了仓储实例。下面是工作单元类的代码:

    复制代码
    using EF.Entity;
    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Text;
    using System.Threading.Tasks;
    
    namespace EF.Data
    {
        public class UnitOfWork : IDisposable
        {
            private readonly EFDbContext db;
            private bool disposed;
            private Dictionary<string, object> repositories;
    
            public UnitOfWork(EFDbContext context)
            {
                this.db = context;  //构造函数中初始化上下文对象
            }
    
            public UnitOfWork()
            {
                db = new EFDbContext(); //构造函数中初始化上下文对象
            }
    
            #region Dispose
            public void Dispose()
            {
                Dispose(true);
                GC.SuppressFinalize(this);
            }
            public virtual void Dispose(bool disposing)
            {
                if (!disposed)
                {
                    if (disposing)
                    {
                        db.Dispose();
                    }
                }
                disposed = true;
            }
            #endregion
    
            #region Save
            public void Save()
            {
                db.SaveChanges();
            } 
            #endregion
    
            #region Repository<T>()
            public Repository<T> Repository<T>() where T : BaseEntity
            {
                if (repositories == null)
                {
                    repositories = new Dictionary<string, object>();
    
                }
                var type = typeof(T).Name;//获取当前成员名称
                if (!repositories.ContainsKey(type))//如果repositories中不包含Name
                {
                    var repositoryType = typeof(Repository<>);//获取Repository<>类型
                    var repositoryInstance = Activator.CreateInstance(repositoryType.MakeGenericType(typeof(T)), db);
                    repositories.Add(type, repositoryInstance);
    
                }
                return (Repository<T>)repositories[type];
    
            } 
            #endregion
    
    
        }
    }
    复制代码

     好了,底层的代码,写完了,现在开始写控制器的代码:我们创建一个Book控制器,进行增删查改。

     View Code

    Index 视图代码:

    复制代码
    @model IEnumerable<EF.Entity.Book>
    
    <div class="book-example panel panel-primary">
        <div class="panel-heading panel-head">Books Listing</div>
        <div class="panel-body">
            <a id="createEditBookModal" href="@Url.Action("CreateEditBook")" class="btn btn-success">
                <span class="glyphicon glyphicon-plus"></span>Book
            </a>
    
            <table class="table" style="margin: 4px">
                <tr>
                    <th>
                        @Html.DisplayNameFor(model => model.Title)
                    </th>
                    <th>
                        @Html.DisplayNameFor(model => model.Author)
                    </th>
                    <th>
                        @Html.DisplayNameFor(model => model.ISBN)
                    </th>
                    <th>
                        Action
                    </th>
    
                    <th></th>
                </tr>
                @foreach (var item in Model)
                {
                    <tr>
                        <td>
                            @Html.DisplayFor(modelItem => item.Title)
                        </td>
                        <td>
                            @Html.DisplayFor(modelItem => item.Author)
                        </td>
                        <td>
                            @Html.DisplayFor(modelItem => item.ISBN)
                        </td>
                        <td>
                            @Html.ActionLink("Edit", "CreateEditBook", new { id = item.ID }, new { @class = "btn btn-success" }) |
                            @Html.ActionLink("Details", "DetailBook", new { id = item.ID }, new { @class = "btn btn-primary" }) |
                            @Html.ActionLink("Delete", "DeleteBook", new { id = item.ID }, new { @class = "btn btn-danger" })
                        </td>
                    </tr>
                }
    
            </table>
        </div>
    </div>  
    复制代码

    CraeteEdit视图代码:

    复制代码
    @model EF.Entity.Book
    
    @{
        ViewBag.Title = "Create Edit Book";
    }
    <div class="book-example panel panel-primary">
        <div class="panel-heading panel-head">Add / Edit Book</div>
        <div class="panel-body">
            @using (Html.BeginForm())
            {
                <div class="form-horizontal">
                    <div class="form-group">
                        @Html.LabelFor(model => model.Title, new { @class = "col-lg-1 control-label" })
                        <div class="col-lg-9">
                            @Html.TextBoxFor(model => model.Title, new { @class = "form-control" })
                        </div>
                    </div>
                    <div class="form-group">
                        @Html.LabelFor(model => model.ISBN, new { @class = "col-lg-1 control-label" })
                        <div class="col-lg-9">
                            @Html.TextBoxFor(model => model.ISBN, new { @class = "form-control" })
                        </div>
                    </div>
                    <div class="form-group">
                        @Html.LabelFor(model => model.Author, new { @class = "col-lg-1 control-label" })
                        <div class="col-lg-9">
                            @Html.TextBoxFor(model => model.Author, new { @class = "form-control" })
                        </div>
                    </div>
                    <div class="form-group">
                        @Html.LabelFor(model => model.Published, new { @class = "col-lg-1 control-label" })
                        <div class="col-lg-9">
                            @Html.TextBoxFor(model => model.Published, new { @class = "form-control datepicker" })
                        </div>
                    </div>
                    <div class="form-group">
                        <div class="col-lg-8"></div>
                        <div class="col-lg-3">
                            @Html.ActionLink("Back to List", "Index", null, new { @class = "btn btn-default" })
                            <button class="btn btn-success" id="btnSubmit" type="submit">
                                Submit
                            </button>
                        </div>
                    </div>
                </div>
            }
        </div>
    </div>
    @section scripts  这里的话,在布局页中要添加这个块: @RenderSection("scripts", required: false)
    {
        <script src="~/Scripts/bootstrap-datepicker.js" type="text/javascript"></script>
        <script src="~/Scripts/book-create-edit.js" type="text/javascript"></script>
    }  
    复制代码

    DeleteBook视图:

     View Code

    Detail视图:

     View Code

    修改一下默认路由为Book控制器,Index方法,然后运行项目》》》

  • 相关阅读:
    Nginx日志切割
    Spring Cloud Alibaba基础教程:Nacos+Dubbo
    Spring Cloud Alibaba基础教程:Sentinel
    Gogs+Drone搭建CI/CD平台
    Spring事件机制
    OpenGL 安装
    Melkman's Algorithm
    Tools: python 安装
    Tools: windbg 使用指南
    Tools: java安装指南
  • 原文地址:https://www.cnblogs.com/jinguanzhang/p/5809929.html
Copyright © 2020-2023  润新知