存储库模式“ 使用类似集合的接口来访问域对象,在域和数据映射层之间进行调解 ”(Martin Fowler)。

实际上,存储库用于为域对象(实体和值类型)执行数据库操作。通常,每个实体(或聚合根)使用单独的存储库。

默认存储库
在ASP.NET Boilerplate中,存储库类实现 IRepository <TEntity,TPrimaryKey>接口。ABP可以自动为每个实体类型创建默认存储库。您可以直接注入 IRepository <TEntity>(或IRepository <TEntity,TPrimaryKey>)。示例应用程序服务使用存储库将实体插入数据库:

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方法。

自定义存储库
只有在需要为该实体创建自定义存储库方法时,才能为实体创建存储库类。

自定义存储库接口
Person实体的存储库定义如下所示:

public interface IPersonRepository : IRepository<Person>
{

}

IPersonRepository扩展了IRepository <TEntity>。它用于定义主键类型为int(Int32)的实体。如果您的实体的主键不是int,则可以扩展 IRepository <TEntity,TPrimaryKey>接口,如下所示:

public interface IPersonRepository : IRepository<Person, long>
{

}

基本存储库方法
每个存储库都有一些来自IRepository <TEntity>接口的常用方法。我们将在这里调查其中的大多数。

查询
获得单一实体

TEntity Get(TPrimaryKey id);
Task<TEntity> GetAsync(TPrimaryKey id);
TEntity Single(Expression<Func<TEntity, bool>> predicate);
Task<TEntity> SingleAsync(Expression<Func<TEntity, bool>> predicate);
TEntity FirstOrDefault(TPrimaryKey id);
Task<TEntity> FirstOrDefaultAsync(TPrimaryKey id);
TEntity FirstOrDefault(Expression<Func<TEntity, bool>> predicate);
Task<TEntity> FirstOrDefaultAsync(Expression<Func<TEntity, bool>> predicate);
TEntity Load(TPrimaryKey id);

该获取方法来得到一个实体与给定的主键(ID)。如果数据库中没有具有给定Id的实体,则会引发异常。该单方法类似于获取,但需要一个表达式,而不是一个ID。这样,您可以编写lambda表达式来获取实体。示例用法:

var person = _personRepository.Get(42);
var person = _personRepository.Single(p => p.Name == "John");

请注意,如果没有具有给定条件的实体或存在多个实体,则Single方法将引发异常。

FirstOrDefault类似于抛出异常,但 如果没有具有给定Id或表达式的实体,则返回 null。如果给定条件有多个实体,则返回第一个找到的实体。

Load不会从数据库中检索实体,但会为延迟加载创建代理对象。如果您只使用Id属性,则实际上不会检索实体。仅当您访问实体的其他属性时才从数据库中检索它。出于性能原因,可以使用此代替Get。它在NHibernate中实现。如果ORM提供程序未实现它,则Load方法与Get方法的工作方式相同。

获取实体列表

List<TEntity> GetAllList();
Task<List<TEntity>> GetAllListAsync();
List<TEntity> GetAllList(Expression<Func<TEntity, bool>> predicate);
Task<List<TEntity>> GetAllListAsync(Expression<Func<TEntity, bool>> predicate);
IQueryable<TEntity> GetAll();
IQueryable<TEntity> GetAllIncluding(params Expression<Func<TEntity, object>>[] propertySelectors)

GetAllList方法用于检索数据库中的所有实体。它的重载可用于过滤实体。例子:

var allPeople = _personRepository.GetAllList();
var somePeople = _personRepository.GetAllList(person => person.IsActive && person.Age > 42);

该GETALL方法返回一个IQueryable <T>。这样,您可以在其后添加Linq方法。例子:

//Example 1
var query = from person in _personRepository.GetAll()
            where person.IsActive
            orderby person.Name
            select person;
var people = query.ToList();

//Example 2:
List<Person> personList2 = _personRepository.GetAll().Where(p => p.Name.Contains("H")).OrderBy(p => p.Name).Skip(40).Take(20).ToList();

使用GetAll时,几乎所有查询都可以用Linq编写。它甚至可以用在连接表达式中!

GetAllIncluding允许指定要包括在查询结果中的相关数据。在以下示例中,将检索人员并填充其地址属性。

var allPeople = await _personRepository.GetAllIncluding(p => p.Addresses).ToListAsync();

关于IQueryable
从存储库方法调用GetAll()时,必须存在打开的数据库连接。这是因为IQueryable 的延迟执行。除非您调用ToList()方法或在foreach循环中使用IQueryable (或以某种方式访问​​查询的项目),否则它不会执行数据库查询。因此,当您调用ToList()方法时,数据库连接必须处于活动状态。对于Web应用程序,在大多数情况下您不必担心,因为默认情况下MVC控制器方法是工作单元,并且数据库连接可用于整个请求。请参阅UnitOfWork文档以更好地理解它。
自定义退货价值
还有一种方法可以提供IQueryable的功能,可以在一个工作单元中使用。

T Query<T>(Func<IQueryable<TEntity>, T> queryMethod);

Query方法接受一个接收IQueryable 的lambda(或方法)并返回任何类型的对象。例:

var people = _personRepository.Query(q => q.Where(p => p.Name.Contains("H")).OrderBy(p => p.Name).ToList());

由于给定的lamda(或方法)在存储库方法内执行,因此在数据库连接可用时执行。您可以返回实体列表,单个实体或投影或执行查询的其他内容。

插入
IRepository接口定义了将实体插入数据库的方法:

TEntity Insert(TEntity entity);
Task<TEntity> InsertAsync(TEntity entity);
TPrimaryKey InsertAndGetId(TEntity entity);
Task<TPrimaryKey> InsertAndGetIdAsync(TEntity entity);
TEntity InsertOrUpdate(TEntity entity);
Task<TEntity> InsertOrUpdateAsync(TEntity entity);
TPrimaryKey InsertOrUpdateAndGetId(TEntity entity);
Task<TPrimaryKey> InsertOrUpdateAndGetIdAsync(TEntity entity);

的插入方法简单地在一个数据库中插入新实体一个和返回相同的插入实体。该InsertAndGetId方法返回一个新插入的实体的ID。如果Id是自动递增并且您需要新插入实体的Id,这将非常有用。该InsertOrUpdate方法插入或通过检查其标识的值更新给定的实体。最后,InsertOrUpdateAndGetId方法在插入或更新实体后返回实体的Id。

更新
IRepository接口定义了更新数据库中现有实体的方法。它需要更新实体并返回相同的实体对象。

TEntity Update(TEntity entity);
Task<TEntity> UpdateAsync(TEntity entity);

大多数情况下,您不需要显式调用Update方法,因为工作单元系统会在工作单元完成时自动保存所有更改。有关详细信息,请参阅工作单元文档。

删除
IRepository接口定义了从数据库中删除现有实体的方法

void Delete(TEntity entity);
Task DeleteAsync(TEntity entity);
void Delete(TPrimaryKey id);
Task DeleteAsync(TPrimaryKey id);
void Delete(Expression<Func<TEntity, bool>> predicate);
Task DeleteAsync(Expression<Func<TEntity, bool>> predicate);

第一种方法接受现有实体,第二种方法接受要删除的实体的Id。最后一个接受条件以删除适合给定条件的所有实体。请注意,可以从数据库中检索与给定谓词匹配的所有实体,然后将其删除(基于存储库实现)。所以要小心使用!如果具有给定条件的实体太多,则可能会导致性能问题。

其他
IRepository还提供了获取表中实体计数的方法。

int Count();
Task<int> CountAsync();
int Count(Expression<Func<TEntity, bool>> predicate);
Task<int> CountAsync(Expression<Func<TEntity, bool>> predicate);
long LongCount();
Task<long> LongCountAsync();
long LongCount(Expression<Func<TEntity, bool>> predicate);
Task<long> LongCountAsync(Expression<Func<TEntity, bool>> predicate);

关于异步方法
ASP.NET Boilerplate支持异步编程模型。存储库方法具有异步版本。这是一个使用异步模型的示例应用程序服务方法:

public class PersonAppService : AbpWpfDemoAppServiceBase, IPersonAppService
{
    private readonly IRepository<Person> _personRepository;

    public PersonAppService(IRepository<Person> personRepository)
    {
        _personRepository = personRepository;
    }

    public async Task<GetPeopleOutput> GetAllPeople()
    {
        var people = await _personRepository.GetAllListAsync();

        return new GetPeopleOutput
        {
            People = Mapper.Map<List<PersonDto>>(people)
        };
    }
}

GetAllPeople方法是异步的,并将GetAllListAsync与await关键字一起使用。

所有ORM框架可能都不支持Async。它受EntityFramework支持。如果不支持,Async存储库方法将同步工作。例如,InsertAsync的工作方式与EntityFramework中的Insert相同,因为在工作单元完成之前,EF不会将新实体写入数据库(也就是DbContext.SaveChanges)。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值