【问题标题】:Using generics with entities对实体使用泛型
【发布时间】:2017-10-01 11:50:10
【问题描述】:

PersonBusiness.GetQuery 方法中,PersonEntity 遍布代码,还有许多其他实体类型将类似地实现此方法。

我想在 PersonBusiness 中使用泛型参数来减少对特定实体类型的使用,因为会有这样的实现与其他实体一起使用,我想防止使用其他类型而不是预期的实体类型。但不能成功或满足泛型参数使用的版本。

如果更有意义的话,我也想使用接口而不是具体的类。

public class Entities: DbContext
{
    public virtual DbSet<PersonEntity> PersonSet { get; set; }
}
public class PersonEntity
{
    public int Id { get; set; }
    public string FullName { get; set; }
}
public class BaseBusiness
{
    public Entities Db => new Entities();
}

public abstract class BaseBusiness<T> : BaseBusiness where T : class
{
    public IQueryable<T> GetQuery<TKey>(Expression<Func<T, bool>> where,
        Expression<Func<T, TKey>> orderBy)
    {
        IQueryable<T> query = Db.Set<T>();
        if (where != null)
            query = query.Where(where);
        if (orderBy != null)
            query = query.OrderBy(orderBy);

        return query;
    }

    public abstract IQueryable<T> ApplyDefaultOrderyBy(IQueryable<T> query);

    public IQueryable<T> GetQuery(IQueryable<T> query, string orderBy, Func<IQueryable<T>, IQueryable<T>> defaultOrderBy = null)
    {
        if (orderBy != null)
            query = query.OrderBy(orderBy);
        else
            query = defaultOrderBy != null ? defaultOrderBy(query) : ApplyDefaultOrderyBy(query);

        return query;
    }
}

public class PersonBusiness : BaseBusiness<PersonEntity>
{
    public IQueryable<PersonEntity> GetQuery(string orderBy, int? groupId)
    {
        IQueryable<PersonEntity> query = Db.PersonSet;

        Func<IQueryable<PersonEntity>, IQueryable<PersonEntity>> defaultOrderBy = null;
        if (groupId.HasValue)
        {
            query = query.Where(d => d.Id == groupId);
        }
        else
        {
            defaultOrderBy = q => q.OrderBy(d => d.Id).ThenBy(d => d.FullName);
        }
        return GetQuery(query, orderBy, defaultOrderBy);
    }
    public override IQueryable<PersonEntity> ApplyDefaultOrderyBy(IQueryable<PersonEntity> query)
    {
        return query.OrderBy(q => q.FullName);
    }
}

【问题讨论】:

  • 您到底遇到了什么问题?
  • 无法编译,IQueryable 和 IQueryable 不兼容。施法解决了它,但在这种情况下有很多施法,所以它闻起来很臭。我认为它应该设计不同。
  • 您的代码看起来像存储库反模式。考虑不同的设计。您的BaseBusiness&lt;T&gt; 课程要解决什么问题?它究竟封装了什么?
  • 这些 GetQuery 和 ApplyDefault 函数似乎没有任何价值。对于可以通过 1 行 Linq 语句直接针对 DbSet 执行的操作,该逻辑基本上是多余的。如果我是你,我会考虑完全忽略它们,而是尝试直接针对 DbSet 编写 Linq,看看会是什么样子。我敢打赌你最终会从你的代码中删除这些“GetQuery”结构。
  • 您的问题过于宽泛、不清楚且基于选项,无论是否有赏金。

标签: c# entity-framework linq oop generics


【解决方案1】:

我将 PersonEntity 提取到接口中,并使用类型约束要求 T 实现 IPersonEntity。我不明白你在做什么,但似乎你想使用类型约束,只是以前没有听说过。

https://msdn.microsoft.com/en-us/library/d5x73970.aspx

public interface IPersonEntity
{
    int Id { get; set; }
    string FullName { get; set; }
}
public class PersonBusiness<T> where T : IPersonEntity
{
    public IQueryable<T> GetQuery(string orderBy, int? groupId)
    {
        IQueryable<T> query = Db.PersonSet;
        Func<IQueryable<T>, IQueryable<T>> defaultOrderBy = null;
        if (groupId.HasValue)
        {
            query = query.Where(d => d.Id == groupId);
        }
        else
        {
            defaultOrderBy = q => q.OrderBy(d => d.Id).ThenBy(d => d.FullName);
        }
        return GetQuery(query, orderBy, defaultOrderBy);
    }
    public IQueryable<T> ApplyDefaultOrderyBy(IQueryable<T> query)
    {
        return query.OrderBy(q => q.FullName);
    }
    public IQueryable<T> GetQuery(IQueryable<T> query, string orderBy, Func<IQueryable<T>, IQueryable<T>> defaultOrderBy = null)
    {
        if (orderBy != null)
            query = query.OrderBy(orderBy);
        else
            query = defaultOrderBy != null ? defaultOrderBy(query) : ApplyDefaultOrderyBy(query);
        return query;
    }
}

【讨论】:

  • 它无法编译,因为约束不用于类型推断
  • 其实我并没有尝试编译代码。当你不使用类型推断时它是否有效?
  • 无法编译。
【解决方案2】:

尽管我对这篇文章发表了评论,但我想我会在这里给出一个可以编译的答案。由于我不了解用例,因此很难推断出适当的解决方案。这里的代码永远不会少

public class PersonBusiness<T> : BaseBusiness<T> where T: PersonEntity
{
    public IQueryable<T> GetQuery(string orderBy, int? groupId)
    {
        IQueryable<T> query = Db.Set<T>();

        Func<IQueryable<T>, IQueryable<T>> defaultOrderBy = null;
        if (groupId.HasValue)
        {
            query = query.Where(d => d.Id == groupId);
        }
        else
        {
            defaultOrderBy = q => q.OrderBy(d => d.Id).ThenBy(d => d.FullName);
        }
        return GetQuery(query, orderBy, defaultOrderBy);
    }
    public override IQueryable<T> ApplyDefaultOrderyBy(IQueryable<T> query)
    {
        return query.OrderBy(q => q.FullName);
    }
}

根据@Ivan Stoev 的评论,更新为使用 DbSet 支持 Cast。

【讨论】:

  • @lockedscope 您在 cmets 中多次使用了type inference这个词。但是该术语适用于泛型方法,而在这里您是在泛型类中。因此,所有方法都与 T 类型相关联,您必须在调用它们之前解决它,例如预期用途类似于var pb = new PersonBusiness&lt;PersonEntity&gt;(); pb.GetQuery(...)。因此,您的设计中不涉及类型推断。那么约束呢,它们确实允许您访问TIdFullName 属性。接口和基类都可以工作。
  • 所以 Jeffrey 的代码肯定可以编译(如果它是你所追求的,那就是另一回事了,你的问题还不清楚)。我建议的唯一更改是使用DbSet&lt;T&gt;() 而不是Db.PersonSet.Cast&lt;T&gt;()
  • 对不起,我搞混了,它实际上可以编译。
  • 已更新以包含来自@IvanStoev 的建议
【解决方案3】:

作为 Dai 的 cmets。这种方法打破了存储库模式。我的解决方案是创建一个通用存储库,正如 Julie Lerman 在ef architecture 中解释的那样

模式实现的帖子在ef-code-first-you-on-repository

这个 sn-p 可以提供帮助。

public abstract class BaseRepository<T> : IBaseRepository<T> where T : class
{
        public DbContext Context { get; protected set; }

        public BaseRepository()
        {
            this.Context = new AppDbContext();
        }

        public BaseRepository(AppDbContext context)
        {
            this.Context = context;
        }

        public IQueryable<T> AllIncluding(params Expression<Func<T, object>>[] includeProperties)
        {
            var query = Context.Set<T>().AsQueryable();
            foreach (var includeProperty in includeProperties)
            {
                query = query.Include(includeProperty);
            }

            return query;
        }

        public DbEntityEntry<T> Entry(T entity)
        {
            return this.Context.Entry(entity);
        }

        public virtual void InsertOrUpdate(params T[] entities)
        {
            foreach (var entity in entities)
            {
                this.Context.Entry(entity).State = EntityState.Added;
            }
        }

        public void SaveChanges()
        {
            this.Context.SetCurrentStateToEntities();
            this.Context.SaveChanges();

            this.Context.SetToUndefined();
        }
    }

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2013-06-24
    • 1970-01-01
    • 1970-01-01
    • 2011-11-17
    • 1970-01-01
    • 2011-08-29
    • 1970-01-01
    • 2020-03-15
    相关资源
    最近更新 更多