ICode9

精准搜索请尝试: 精确搜索
首页 > 编程语言> 文章详细

C#-IRepository和工作单元与急切的加载

2019-10-25 01:09:25  阅读:356  来源: 互联网

标签:eager-loading entity-framework-6 c generic-repository


我知道有一个工作单元在抽象(DbContext)之上有一个抽象,并且肯定是一种反模式,或者至少没有必要.

我有以下问题:

我有一个通用的IRepository像这样:

public interface IGenericRepository<TEntity> where TEntity : class
{
    IEnumerable<TEntity> Get(
        Expression<Func<TEntity, bool>> filter = null,
        Func<IQueryable<TEntity>, IOrderedQueryable<TEntity>> orderBy = null,
        string includeProperties = "");

    TEntity GetByID(object id);

    void Insert(TEntity entity);

    void Delete(object id);

    void Delete(TEntity entityToDelete);

    void Update(TEntity entityToUpdate);
}

这是此接口的实现:

public class GenericRepository<TEntity> : IGenericRepository<TEntity> where TEntity : class
{
    internal GymHelperContext context;
    internal DbSet<TEntity> dbSet;

    public GenericRepository(GymHelperContext context)
    {
        this.context = context;
        dbSet = context.Set<TEntity>();
    }

    public virtual IEnumerable<TEntity> Get(
        Expression<Func<TEntity, bool>> filter = null,
        Func<IQueryable<TEntity>, IOrderedQueryable<TEntity>> orderBy = null,
        string includeProperties = "")
    {
        IQueryable<TEntity> query = dbSet;

        if (filter != null)
        {
            query = query.Where(filter);
        }

        foreach (var includeProperty in includeProperties.Split
            (new char[] { ',' }, StringSplitOptions.RemoveEmptyEntries))
        {
            query = query.Include(includeProperty);
        }

        if (orderBy != null)
        {
            return orderBy(query).ToList();
        }
        else
        {
            return query.ToList();
        }
    }

    public virtual TEntity GetByID(object id)
    {
        return dbSet.Find(id);
    }

    public virtual void Insert(TEntity entity)
    {
        dbSet.Add(entity);
    }

    public virtual void Delete(object id)
    {
        TEntity entityToDelete = dbSet.Find(id);
        Delete(entityToDelete);
    }

    public virtual void Delete(TEntity entityToDelete)
    {
        if (context.Entry(entityToDelete).State == EntityState.Detached)
        {
            dbSet.Attach(entityToDelete);
        }
        dbSet.Remove(entityToDelete);
    }

    public virtual void Update(TEntity entityToUpdate)
    {
        dbSet.Attach(entityToUpdate);
        context.Entry(entityToUpdate).State = EntityState.Modified;
    }
}

我有一个名为Facade的实例,它实例化了Mapper和UnitOfWork,如下所示:

public class MuscleFacade
    {
        private readonly UnitOfWork _unitOfWork = new UnitOfWork();
        private readonly MuscleMapping _muscleMapping = new MuscleMapping();

        public MuscleFacade()
        {

        }

        public IEnumerable<MuscleViewModel> GetAllMuscles()
        {
            var source = _unitOfWork.MuscleRepository
                .Get()
                .ToList();

            var result = source.Select(x => _muscleMapping.MuscleToModel(x));

            return result;
        }

        public GymViewModel GetGymViewModel()
        {
            GymViewModel gymViewModel = new GymViewModel
            {
                ListOfMuscles = GetAllMuscles().ToList()
            };

            return gymViewModel;
        }

    }

MuscleFacade类是我使用Autofac在控制器上注入的类,我在其构造函数中注入了IMuscleFacade.

现在的事情是,我的MuscleTypeViewModel有一个MuscleViewModel列表,这些模型与对应的Domain类映射,并且在这种特殊情况下,MuscleType有很多Muscle(例如:Arm拥有二头肌,三头肌等),因此我将导航属性放在每个其中的一个,像这样:

public class MuscleType : IEntity
{
    public int Id { get; set; }

    [StringLength(100)]
    public string MuscleTypeName { get; set; }

    public ICollection<Muscle> Muscles { get; set; }
}

public class Muscle : IEntity
{
    public int Id { get; set; }

    [StringLength(100)]
    public string MuscleName { get; set; }

    public int MuscleTypeId { get; set; }

    public MuscleType MuscleType { get; set; }
}

现在让我们再次在Facade中查看GetAllMuscles方法:

public IEnumerable<MuscleViewModel> GetAllMuscles()
{
    var source = _unitOfWork.MuscleRepository
        .Get()
        .ToList();

    var result = source.Select(x => _muscleMapping.MuscleToModel(x));

    return result;
}

如果我想Eager-Load MuscleType,该如何更改Get()以接收和表达Func而不是字符串呢?

解决方法:

您可以定义一个包含您的包含定义的帮助程序类:

abstract class IncludeDefinition<TEntity>
{
    public abstract IQueryable<TEntity> Include(IQueryable<TEntity> entities);
}

class IncludeDefinition<TEntity, TProperty> : IncludeDefinition<TEntity>
{
    public IncludeDefinition(Expression<Func<TEntity, TProperty>> includeEx)
    {
        _includeEx = includeEx;
    }

    private readonly Expression<Func<TEntity, TProperty>> _includeEx;

    public override IQueryable<TEntity> Include(IQueryable<TEntity> entities)
    {
        return entities.Include(_includeEx);
    }
}

然后在您的Get方法中使用IncludeDefinition

public IEnumerable<Muscle> Get(params IncludeDefinition<Muscle>[] includes)
{
    IQueryable<Muscle> muscles = ...;
    foreach (var item in includes)
    {
        muscles = item.Include(muscles);
    }
    return muscles.ToList();
}

并调用方法

_unitOfWork.MuscleRepository
    .Get(new IncludeDefinition<Muscle, MuscleType>(m => m.MuscleType));

// Include as many as you wish
_unitOfWork.MuscleRepository
    .Get(new IncludeDefinition<Muscle, MuscleType>(m => m.MuscleType),
         new IncludeDefinition<Muscle, SomeOtherRelatedEntity>(m => m.SomeOtherProperty));

这里的编辑提供了一种“只是包含”而不是编写复杂语法的方法.

创建一个新接口IQueryRepository,该接口支持不带显式包含和包含的Get,从此接口派生IGenericRepository:

public interface IQueryRepository<TEntity>
     where TEntity : class
{
    IEnumerable<TEntity> Get(
        Expression<Func<TEntity, bool>> filter = null,
        Func<IQueryable<TEntity>, IOrderedQueryable<TEntity>> orderBy = null);

    IQueryRepository<TEntity> Include<TProperty>(Expression<Func<TEntity, TProperty>> referenceExpression);
}

public interface IGenericRepository<TEntity> : IQueryRepository<TEntity>
     where TEntity : class
{
    IEnumerable<TEntity> Get(
        Expression<Func<TEntity, bool>> filter = null,
        Func<IQueryable<TEntity>, IOrderedQueryable<TEntity>> orderBy = null,
        params IncludeDefinition<TEntity>[] include);

    // other methods like GetByID, Add, Update...
}

更新GenericRepository定义-它使用我最初介绍的IncludeDefinition方法,并在调用Include时返回GenericQueryRepositoryHelper.

public class GenericRepository<TEntity> : IGenericRepository<TEntity> where TEntity : class
{
    internal DbSet<TEntity> dbSet;


    public IEnumerable<TEntity> Get(Expression<Func<TEntity, bool>> filter = null, Func<IQueryable<TEntity>, IOrderedQueryable<TEntity>> orderBy = null)
    {
        return Get(filter, orderBy, new IncludeDefinition<TEntity>[0]);
    }

    public IEnumerable<TEntity> Get(Expression<Func<TEntity, bool>> filter = null, Func<IQueryable<TEntity>, IOrderedQueryable<TEntity>> orderBy = null, params IncludeDefinition<TEntity>[] includes)
    {
        IQueryable<TEntity> query = dbSet;

        foreach (var item in includes)
        {
            query = item.Include(query);
        }

        if (filter != null)
        {
            query = query.Where(filter);
        }

        if (orderBy != null)
        {
            return orderBy(query).ToList();
        }
        else
        {
            return query.ToList();
        }
    }

    public IQueryRepository<TEntity> Include<TProperty>(Expression<Func<TEntity, TProperty>> referenceExpression)
    {
        return new GenericQueryRepositoryHelper<TEntity>(this, new IncludeDefinition<TEntity, TProperty>(referenceExpression));
    }

    // other methods like GetByID, Add, Update...
}

实现GenericQueryRepositoryHelper来存储包含并在调用Get时应用它们

public class GenericQueryRepositoryHelper<TEntity> : IQueryRepository<TEntity>
    where TEntity : class
{
    private readonly IList<IncludeDefinition<TEntity>> _includeDefinitions;
    private readonly IGenericRepository<TEntity> _repository;

    internal GenericQueryRepositoryHelper(IGenericRepository<TEntity> repository, IncludeDefinition<TEntity> includeDefinition)
    {
        _repository = repository;
        _includeDefinitions = new List<IncludeDefinition<TEntity>> { includeDefinition };
    }

    public IEnumerable<TEntity> Get(Expression<Func<TEntity, bool>> filter = null, Func<IQueryable<TEntity>, IOrderedQueryable<TEntity>> orderBy = null)
    {
        return _repository.Get(filter, orderBy, _includeDefinitions.ToArray());
    }

    public IQueryRepository<TEntity> Include<TProperty>(Expression<Func<TEntity, TProperty>> referenceExpression)
    {
        _includeDefinitions.Add(new IncludeDefinition<TEntity, TProperty>(referenceExpression));
        return this;
    }
}

快乐查询包括:

var repo = new GenericRepository<Muscle>(...);
repo.Include(x => x.MuscleType)
    .Include(x => x.MuscleType.Muscles)
    .Get(x => x.MuscleName == "Test", x => x.OrderBy(m => m.MuscleName));

标签:eager-loading,entity-framework-6,c,generic-repository
来源: https://codeday.me/bug/20191024/1924524.html

本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享;
2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关;
3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关;
4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除;
5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。

专注分享技术,共同学习,共同进步。侵权联系[81616952@qq.com]

Copyright (C)ICode9.com, All Rights Reserved.

ICode9版权所有