• ABP官方文档翻译 9.2 Entity Framework Core


    Entity Framework Core

    介绍

      Abp.EntityFrameworkCore nuget包用来集成EntityFramework(EF)Core ORM框架。安装这个包之后,我们需要为AbpEntityFrameworkCoreModule添加DependsOn特性。

    DbContext

      EF Core需要定义一个从DbContext继承的类。在ABP中,我们应该从AbpDbContext继承,如下所示:

    public class MyDbContext : AbpDbContext
    {
        public DbSet<Product> Products { get; set; }
    
        public MyDbContext(DbContextOptions<MyDbContext> options)
            : base(options)
        {
        }
    }

      如上所示,构造函数有一个DbContextOptions<T>的参数。

    配置

    在Startup类中

      ConfigureServices方法中使用AddAbpDbContext方法,如下所示:

    services.AddAbpDbContext<MyDbContext>(options =>
    {
        options.DbContextOptions.UseSqlServer(options.ConnectionString);
    });

      对于非web工程,没有Startup类。在这种情况下,我们在模块中使用Configuration.Modules.AbpEfCore().AddDbContext方法来配置DbContext,如下所示:

    Configuration.Modules.AbpEfCore().AddDbContext<MyDbContext>(options =>
    {
        options.DbContextOptions.UseSqlServer(options.ConnectionString);
    });

      我们使用给定的连接字符串并使用Sql Server作为数据库提供者。options.ConnectionString通常为默认的连接字符串(参见下一部分)。但是ABP使用IConnectionStringResolver来决定。所以,这种行为是可以改变的,连接字符串也可以动态决定。无论何时DbContext示例创建时,传递给AddDbContext的action都会执行。所以,你有机会可以根据条件返回不同的连接字符串。

      所以,在什么地方设置默认字符串呢?

    在模块PreInitialize方法中

      你可以在模块的PreInitialize方法中设置默认字符串,如下所示:

    public class MyEfCoreAppModule : AbpModule
    {
        public override void PreInitialize()
        {
            Configuration.DefaultNameOrConnectionString = GetConnectionString("Default");
            ...
        }
    }

      这样,你可以定义GetConnectionString方法从一个配置文件中(一般从appsettings.json文件)返回连接字符串。

    仓储

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

    默认仓储

      Abp.EntityFrameworkCore为所有定义在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提供了一个基类EfCoreRepositoryBase来简单的实现仓储。为了实现IRepository接口,你可以从这个类继承。但是最好创建自己的基础类并扩展EfRepositoryBase。这样,你可以容易的在自己的仓储中添加shared/common方法。下面为SimpleTaskSystem应用所有仓储的基类示例:

    //Base class for all repositories in my application
    public class SimpleTaskSystemRepositoryBase<TEntity, TPrimaryKey> : EfCoreRepositoryBase<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)
    }

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

      默认,给定DbContext(在这个例子中为SimpleTaskSystemDbContext)的所有仓储使用EfCoreRepositoryBase实现。你可以通过在DbContext上添加AutoRepositoryTypes特性来使用自己的仓储基类来取代默认的仓储基类。如下所示:

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

    自定义仓储示例

      为了实现自定义仓储,需要继承我们上面定义的特定仓储基础类。

      假定,我们有一个Task实体,它可以被分配给一个Person(实体)并且任务有一个状态(new,assigned,completed...等等)。我们需要编写一个自定义方法基于一些条件并基于AssisgnedPerson属性预获取person实体,在一个数据库查询语句中来获取任务列表。参见示例代码:

    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并实现了它。GetAll()返回IQueryable<Task>,然后我们使用给定的参数添加一些Where过滤器。最后我们调用ToList()方法来获取Tasks列表。

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

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

    取代默认仓储

      即使你有一个TaskRepository,如上所示,任何类仍然可以注入IRepository<Task,long>并使用它。大多数情况下这没有什么问题。但是,如果在你的自定义仓储中重写了一个基类方法会怎样呢?比如说在你的自定义仓储中重写了Delete方法在删除时添加一个自定义行为。如果一个类注入IRepository<Task,long>并使用默认仓储删除一个任务,你自定义的行为将不会起作用。为了克服这个问题,你可以使用默认的你喜欢的一个取代你的自定义实现,如下所示:

    Configuration.ReplaceService<IRepository<Task, Guid>>(() =>
    {
        IocManager.IocContainer.Register(
            Component.For<IRepository<Task, Guid>, ITaskRepository, TaskRepository>()
                .ImplementedBy<TaskRepository>()
                .LifestyleTransient()
        );
    });

      我们为IRepository<Task,Guid>,ITaskRepository和TaskRepository注册TaskRepository。所以,这些中的任何一个都可以被注入并使用TaskRepository。

    仓储最佳实践

    • 在任何可能的地方使用默认仓储。即使你已经有一个实体的默认仓储(如果你将使用标准仓储方法)也可以使用默认的仓储。
    • 总是在应用程序中为自定义仓储创建仓储基类,如上定义。
    • 如果你想从domain/application中抽象出EF Core,那么在domain层定义自定义仓储的接口(在启动模板中为.Core工程),在.EntityFrameworkCore工程中定义自定义仓储类。

    返回主目录

  • 相关阅读:
    Method总结
    使用CSS的五种方式
    debug js code
    Overload
    fiddler模拟弱网测试
    POJ 1753 Flip Game (IDA*)
    UVA 11400 Lighting System Design(照明系统设计)(dp)
    UVA 12563 Jin Ge Jin Qu hao(劲歌金曲)(01背包+滚动数组)
    UVA 116 Unidirectional TSP (单向TSP)(dp多段图的最短路)
    UVA 1151 Buy or Build (买还是建)(并查集+二进制枚举子集)
  • 原文地址:https://www.cnblogs.com/xajh/p/7153017.html
Copyright © 2020-2023  润新知