EntityFramework集成

  ABP可以使用ORM框架,它内置集成EntityFramework。本文档将讲解ABP如何使用EntityFramework。假定你对EntityFramework已经有了初级水平。

Nuget包

  在ABP中使用Abp.EntityFramework nuget包扩展了EntityFramework。需要将它添加到工程中。最好在一个单独的程序集(dll)中实现EntityFramework并在此程序集中引用Abp.EntityFramework包。

DbContext

  如你所知,为了使用EntityFramework,你需要在应用中定义一个DbContext。一个实例DBContext如下所示:

public class SimpleTaskSystemDbContext : AbpDbContext
{
public virtual IDbSet<Person> People { get; set; }
public virtual IDbSet<Task> Tasks { get; set; } public SimpleTaskSystemDbContext()
: base("Default")
{ } public SimpleTaskSystemDbContext(string nameOrConnectionString)
: base(nameOrConnectionString)
{ } public SimpleTaskSystemDbContext(DbConnection existingConnection)
: base(existingConnection, false)
{ } public SimpleTaskSystemDbContext(DbConnection existingConnection, bool contextOwnsConnection)
: base(existingConnection, contextOwnsConnection)
{ } protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
base.OnModelCreating(modelBuilder); modelBuilder.Entity<Person>().ToTable("StsPeople");
modelBuilder.Entity<Task>().ToTable("StsTasks").HasOptional(t => t.AssignedPerson);
}
}

  这是一个正规的DbContext类,除此之外它遵循以下规则:

  • AbpDbContext集成而不是DbContext。
  • 需要有构造函数如上面的示例(构造函数参数命名也需要一样)。说明:
    • 默认构造函数传递“Default”到基类作为连接字符串。所以,在web.config/app.config文件中有一个名为“Default”的连接字符串。ABP不使用个这构造函数,但是EF命令行迁移工具和工具命令(如 update-database)使用它。
    • nameOrConnectiongString参数的构造函数,ABP用来在运行时传递连接名称或字符串。
    • existingConnection参数的构造函数用于单元测试,ABP不直接使用。
    • existingConnectioncontxtOwnsConnection的构造函数,当使用DbContextEfTransactionStrategy时(参见下面的事务管理部分),ABP用于单数据库多dbcontext的场景来共享同一个连接、事务。

  EntityFramework使用常规的方式将类映射到数据库表。除非自定义了功能,你不需要做任何配置。在这个例子中,我们将实体映射到不同的表。Task实体默认映射到Tasks表。但是我们可以改变它映射到StsTasks表。比起使用数据注解,我更喜欢使用流配置。你可以选择自己喜欢的方式。

仓储

  仓储用来从高层抽象数据访问。参见仓储文档了解更多。

默认仓储

  Abp.EntityFramework为定义在DbContext中的所有实体实现默认仓储。你不必创建仓储类来使用预定义的仓储方法。示例:

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>)并使用预定义方法。

自定义仓储

  如果标准的仓储方法不能满足,你可以为你的实体创建自定义仓储类。

应用特定的基础仓储类

  ABP提供了一个基础类EfRepositoryBase来实现仓储。为了实现IRepository接口,可以将你的仓储类从这个类集成。但是最好创建自己的基类并扩展EfRepositoryBase。从而,你可以在自己的仓储中添加shared/common方法或重写已经存在的方法。下面是SimpleTaskSystem应用的所有仓储基类的示例:

//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
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)
}

  注意,我们从EfRepositoryBase<SimpleTaskSystemDbContext,TEntity,TPrimaryKey>继承。这表明ABP在我们的仓储中使用SimpleTaskSystemDbContext。

  默认,对于所有给定DbContext(在这个例子中为SimpleTaskDbContext)的仓储都使用EfRepositoryBase实现。你可以在你的DbContext中添加AutoRepository特性来使用自己的仓储基类,如下:

[AutoRepositoryTypes(
typeof(IRepository<>),
typeof(IRepository<,>),
typeof(SimpleTaskSystemEfRepositoryBase<>),
typeof(SimpleTaskSystemEfRepositoryBase<,>)
)]
public class SimpleTaskSystemDbContext : AbpDbContext
{
...
}

自定义仓储示例

  为了实现自定义仓储,在你的应用中继承一个以上我们定义的基础仓储类。

  假定,我们有一个Task实体,它可以分配给一个Person(实体)并Task有一个State(new,assigned,completed...等等)。我们需要编写一个自定义方法使用一些条件和AssisgnedPerson属性来预获取Tasks的列表。参见下面的示例代码:

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();
}
}

  我们首先定义了ITaskRepostory接口并实现了它。GetAll()返回IQueryable<Task>,然后我们可以使用给定的参数添加一些Where过滤器。最后我们调用ToList()来获取Tasks列表。

  你也可以在仓储方法中使用Context对象引用你自己的DbContext,然后直接使用Entity Framework APIs。

  注意:对于分层应用,在domain/core层定义自定义仓储接口,在EntityFramework工程中实现它们。从而,你可以在任何工程中注入这个接口而不用引用EF。

仓储最佳实践

  • 在可能的地方使用默认仓储。即使你有一个实体的自定义仓储(如果你将使用标准的仓储方法),也可以使用默认的仓储。
  • 总是为自定义仓储创建仓储基类,如上面定义的那样。
  • 领域层定义自定义仓储的接口,如果你想将EF从你的domain/application中抽象出来,在EntityFramework工程中自定义仓储类。

事务管理

  ABP有内置的工作单元系统来管理数据库连接和事务。Entity Framework有不同的事务管理方式。ABP默认使用TransactionScope方式,但是也有DbContext事务API的内置实现。如果你想切换到DbContext事务API,可以在模块的PreInitialize方法中配置它:

Configuration.ReplaceService<IEfTransactionStrategy, DbContextEfTransactionStrategy>(DependencyLifeStyle.Transient);

  记住,在代码中添加"using Abp.Configuration.Startup"以便能使用ReplaceService的泛型扩展方法。

  另外,如在DbContext部分所描述的那样,你的DbContext需要有构造函数。

数据存储

  因为ABP内置集成EntityFramework,那么它就可以使用EntityFramework所支持的数据存储。我们免费的启动模板设计为使用Sql Server,但是你可以修改他们以便使用不同的数据存储。

  例如,如果你想使用MySql,请参见这个文档

返回主目录

ABP官方文档翻译 9.1 EntityFramework集成的更多相关文章

  1. ABP官方文档翻译 7.2 Hangfire集成

    Hangfire集成 介绍 ASP.NET Core集成 ASP.NET MVC 5.x集成 面板授权 介绍 Hangfire是一个综合的后台job管理器.你可以 把它集成到ABP,用来取代默认的后台 ...

  2. ABP官方文档翻译 9.3 NHibernate集成

    NHibernate集成 Nuget包 配置 实体映射 仓储 默认实现 自定义仓储 应用程序特定基础仓储类 ABP可以使用任何ORM框架,它内置集成NHibernate.此文档将讲解ABP如何使用NH ...

  3. ABP官方文档翻译 8.2 SignalR集成

    SignalR集成 介绍 安装 服务器端 客户端 建立连接 內建特征 通知 在线客户端 PascalCase与CamelCase对比 你的SignalR代码 介绍 ABP中的Abp.Web.Signa ...

  4. ABP官方文档翻译 7.3 Quartz集成

    Quartz集成 介绍 安装 创建Jobs 计划安排Jobs 更多 介绍 Quartz是一个全功能的.开源的job计划安排系统,可以用在小的apps也可以用于大型的企业系统.Abp.Quartz包简化 ...

  5. ABP官方文档翻译 5.4 SwaggerUI集成

    SwaggerUI集成 介绍 ASP.NET Core 安装Nuget包 配置 测试 ASP.NET 5.x 安装Nuget包 配置 测试 介绍 在它的网站上:“...使用Swagger可用的API, ...

  6. ABP官方文档翻译 5.3 OData集成

    OData集成 介绍 安装 安装Nuget包 设置模块依赖 配置实体 创建控制器 配置 示例 获取实体列表 Request Response 获取单个实体 Request Response 使用导航属 ...

  7. ABP官方文档翻译 1.6 OWIN集成

    OWIN集成 安装 使用 如果在应用程序里既使用ASP.NET MVC也使用ASP.NET Web API,需要在工程里安装Abp.Owin包. 安装 添加Abp.Owin包到主工程里(一般是web工 ...

  8. ABP官方文档翻译 10.1 ABP Nuget包

    ABP Nuget包 Packages Abp Abp.AspNetCore Abp.Web.Common Abp.Web Abp.Web.Mvc Abp.Web.Api Abp.Web.Api.OD ...

  9. ABP官方文档翻译 0.0 ABP官方文档翻译目录

    一直想学习ABP,但囿于工作比较忙,没有合适的契机,当然最重要的还是自己懒.不知不觉从毕业到参加工作七年了,没留下点儿什么,总感觉很遗憾,所以今天终于卯足劲鼓起勇气开始写博客.有些事能做的很好,但要跟 ...

随机推荐

  1. RabbitMQ 学习开发笔记

    基本概念 ConnectionFactory.Connection.Channel ConnectionFactory.Connection.Channel,这三个都是RabbitMQ对外提供的API ...

  2. codechef [snackdown2017 Onsite Final] Fusing Weapons

    传送门 题目描述 大厨最近迷上了一款勇者斗恶龙的游戏. 游戏每局开始前,会有 N 件武器摆成一圈.每件武器有一个整数的等级.大厨可以选择两件 相邻的等级相同(不妨设同为 A 级)的武器,将它们合成.这 ...

  3. isupper()函数

    isupper()函数可以用来判断字符c是否为大写英文字母! 原型:extern int isupper(int c); 头文件:ctype.h 功能:判断字符c是否为大写英文字母 说明:当参数c为大 ...

  4. UVAlive 3708 Graveyard(最优化问题)

    题目描述: 在周长10000的圆上,初始等距的放置着n个雕塑,现在新加入m个雕塑,要使得这n+m个雕塑仍然等距,问原来n个雕塑要移动的距离总和的最小值. 原题地址: http://acm.hust.e ...

  5. HDU 2084 数塔(简单DP入门)

    数塔 Time Limit: 1000/1000 MS (Java/Others)    Memory Limit: 32768/32768 K (Java/Others) Total Submiss ...

  6. hihoCoder #1043 : 完全背包(板子题)

    #1043 : 完全背包 时间限制:20000ms 单点时限:1000ms 内存限制:256MB 描述 且说之前的故事里,小Hi和小Ho费劲心思终于拿到了茫茫多的奖券!而现在,终于到了小Ho领取奖励的 ...

  7. c++extern关键字详解

    1 基本解释 :extern可以置于变量或者函数 前,以标示变量或者函数的定义在别的文件中 ,提示编译器遇到此变量和函数时在其他模块中寻找其定义 .此外extern也可用来进行链接指定. 也就是说ex ...

  8. [国嵌攻略][068][tftp网络协议实现]

    IP协议结构 UDP协议结构 TFTP协议结构 TFTP端口 读写请求端口: 69 其他请求端口:1024~65535 主程序 /*********************************** ...

  9. js时间戳与时间日期间相互转换

    今天在工作中要将获取到的时间转换为时间戳,一时间竟不知道怎么用,于是不得不去查询资料,这里特地做个笔记. 1.将日期转换为时间戳. 要将日期转换为时间戳,首先得先获取到日期,这里可以直接指定日期,或者 ...

  10. c++---天梯赛---查验身份证

    ★题目: ★题目分析:本题要求输入一个数字n,随后n行输入n个身份证号码.之后进行进一步的判断把错误的身份证号码输出.如果全部正确输出All passed. ★思路方法: ①按题目要求输入. ②对前1 ...