企业🤖AI智能体构建引擎,智能编排和调试,一键部署,支持知识库和私有化部署方案 广告
## 9.1 ABP基础设施层 - 集成Entity Framework ABP可以与任何ORM框架协同工作,它内置了对EntityFramework的集成支持。本文将介绍如何在ABP中使用EntityFramework。本文假定你已经初步掌握了EntityFramework。 > 译者注:怎么才算初步掌握了EntityFramework呢?译者认为应当懂得使用Code First模式进行CRUD。 ### 9.1.1 Nuget包 在ABP中要使用EntityFramework作为ORM框架的话,需要到Nuget上下载一个名为Abp.EntityFramework的包。比较好的做法是:新建一个独立的程序集(dll),然后在这个程序集中调用这个包和EntityFramework。 >ABP官方提供的模板程序就是这样做的。模板程序的下载方法详见《ABP系列之2、ABP入门教程》 ### 9.1.2 创建DbContext 要使用EntityFramework,首先需要定义一个DbContext类。下面是一个DbContex类的示例: ```csharp public class SimpleTaskSystemDbContext : AbpDbContext { public virtual IDbSet<Person> People { get; set; } public virtual IDbSet<Task> Tasks { get; set; } public SimpleTaskSystemDbContext() : base("MyConnectionStringName") { } protected override void OnModelCreating(DbModelBuilder modelBuilder) { base.OnModelCreating(modelBuilder); modelBuilder.Entity<Person>().ToTable("StsPeople"); modelBuilder.Entity<Task>().ToTable("StsTasks").HasOptional(t => t.AssignedPerson); } } ``` 上面的SimpleTaskSystemDbContext本质上是一个DbContext类,它派生自AbpDbContext,而不是DbContext。AbpDbContext提供了很多重载的构造函数,如果需要的话,我们可以使用它。 EntityFramework可以使用约定的方式来映射实体和数据表。除非你想进行自定义映射,否则你甚至不需要做任何配置。在上例中,我们将实体映射到了不同的表中。默认情况下(按照约定优先于配置的原则,会默认采用约定的方式),Task实体会映射到Tasks表,但在这里我们将它映射到了StsTasks表。相比起使用Data Annotation模式来进行自定义映射,我更喜欢使用Fluent API模式。当然,你可以选择你所喜欢的模式,这里没有特别的限制。 ### 9.1.3 仓储 ABP提供了一个名为EfRepositoryBase的基类,这使得实现仓储变得简单快捷。要实现IRepository接口,你只需要从这个基类进行派生即可。但是更好的做法是,自定义一个派生自EfRepositoryBase的基类,然后在这个基类中添加一些通用的方法。这样做的好处是,所有派生自这个基类的仓储都继承了这些通用方法。 #### 1. 应用程序专用的仓储基类 (Application specific base repository class) 在下面的例子中,我们定义了一个名为SimpleTaskSystem仓储基类,这个类是此应用程序所专用的。 ```csharp // Base class for all repositories in my application // 应用程序中的所有仓储的基类 public class SimpleTaskSystemRepositoryBase<TEntity, TPrimaryKey> : EfRepositoryBase<SimpleTaskSystemDbContext, TEntity, TPrimaryKey> where TEntity : class, IEntity<TPrimaryKey> { public SimpleTaskSystemRepositoryBase(IDbContextProvider<SimpleTaskSystemDbContext> dbContextProvider) : base(dbContextProvider) { } // add common methods for all repositories //添加仓储基类的通用方法 } //A shortcut for entities those have integer Id // 为所有拥有整型Id的实体添加一个快捷方式。 public class SimpleTaskSystemRepositoryBase<TEntity> : SimpleTaskSystemRepositoryBase<TEntity, int> where TEntity : class, IEntity<int> { public SimpleTaskSystemRepositoryBase(IDbContextProvider<SimpleTaskSystemDbContext> dbContextProvider) : base(dbContextProvider) { } //do not add any method here, add to the class above (because this class inherits it) //不 要 在 这 里 添 加 任 何 通 用 方 法 ,通 用 方 法 应 当 被添加到 上 面 的 基 类 中(MyRepositoryBase<TEntity, TPrimaryKey>) (SimpleTaskSystemRepositoryBase)。 } ``` 需要注意的是,我们继承了基类EfRepositoryBase\<SimpleTaskSystemDbContext, TEntity, TPrimaryKey\>。这相当于做了一个声明,它会让ABP在仓储中调用SimpleTaskSystemDbContext。 #### 2. 仓储实现(Implementing a repository) 如果你只想使用预定义的仓储方法的话,你甚至不需要为实体创建仓储类。如下所示: ```csharp public class PersonAppService : IPersonAppService { private readonly IRepository<Person> _personRepository; public PersonAppService(IRepository<Person> personRepository) { _personRepository = personRepository; } public void CreatePerson(CreatePersonInput input) { person = new Person { Name = input.Name, EmailAddress = input.EmailAddress }; _personRepository.Insert(person); } } ``` PersonAppService类采用构造函数注入的方式注入了一个IRepository\<Person\>对象,然后调用了预定义的Insert方法。采用这种方式,你可以方便地注入IRepository\<TEntity\> (或者IRepository\<TEntity, TPrimaryKey\>)对象,然后使用预定义的方法。查看仓储文档以获得预定义方法的列表。 #### 3. 自定义仓储(Custom repositories) 要实现自定义仓储,只要从上面所创建的应用程序专用的仓储基类(SimpleTaskSystemRepositoryBase)派生即可。 假定我们有一个名为Task的实体,它可以被赋予Person实体。Task有一个状态属性(值为新建、已分配、已完成等)。我们需要编写一个自定义方法,这个方法能根据某些条件获取Task列表,并且使得Task的AssisgnedPerson属性可以在一次数据库查询中被加载(使用EntityFramework的贪婪加载方法Include)。如下所示: ``` csharp public interface ITaskRepository : IRepository<Task, long> { List<Task> GetAllWithPeople(int? assignedPersonId, TaskState? state); } public class TaskRepository : SimpleTaskSystemRepositoryBase<Task, long>, ITaskRepository { public TaskRepository(IDbContextProvider<SimpleTaskSystemDbContext> dbContextProvider) : base(dbContextProvider) { } public List<Task> GetAllWithPeople(int? assignedPersonId, TaskState? state) { var query = GetAll(); if (assignedPersonId.HasValue) { query = query.Where(task => task.AssignedPerson.Id == assignedPersonId.Value); } if (state.HasValue) { query = query.Where(task => task.State == state); } return query .OrderByDescending(task => task.CreationTime) .Include(task => task.AssignedPerson) .ToList(); } } ``` 我们首先定义了一个名为ITaskRepository的接口,紧接着定义了一个名为TaskRepository的类来实现它。预定义方法GetAll()返回了一个IQueryable\<Task\>对象,接着我们将参数放入到Where筛选器中,最后调用ToList()来获得一个Task列表。 此外,我们可以通过调用base.Context属性来获得一个DbContext对象,这样一来,你就可以直接使用Entity Framework的所有功能。 仓储类应当在构造函数中获取ISessionProvider对象。通过这种方式,在单元测试的时候,我们可以很容易地注入一个虚拟的DbContext Provider对象;而在运行的时候,ABP会根据配置注入相应的DbContext Provider对象。