繁体   English   中英

使用泛型的实体框架相关对象

[英]Entity Framework related objects using generics

在对实体使用Linq时,有关将Include()和Load()用于相关表信息存在很多问题。 我对这个问题有不同的看法。

我的情况:

我有一个表,该表为系统中的每个用户保存许多时间记录,并且我使用存储库模式和泛型进行开发,因此我的所有实体都有一个接口,可用于标准方法调用。 我已关闭了延迟加载,并且自己加载了所有数据。 因此,存储库中用于从具有相关表的表中加载所有记录的代码如下:

public class Repository<T> : IRepository<T> where T : class
{
    protected readonly ApplicationDbContext Context;

    public Repository(IConnectionHelper connection)
    {
        Context = connection.Context;
    }
    public virtual DbSet<T> ObjectSet
    {
        get { return Context.Set<T>(); }
    }  
    public List<T> GetAll(String[] include, Expression<Func<T, bool>> predicate)
    {
        DbQuery<T> outQuery = null;
        foreach (String s in include)
        {
            outQuery = ObjectSet.Include(s);
            outQuery.Load();
        }
        return outQuery.Where(predicate).ToList();
    }
}

该方法的调用如下:

string[] includes = { "User.UserProfile", "CampaignTimeClocks.CampaignRole.Campaign", "Site", "Type" };
DateTime uTcCurrent = GetUtc();
DateTime MinClockinDate = uTcCurrent.AddHours(-10);
List<TimeClock> tcPending = _timeClock.GetAll(includes, x => (x.PendingReview || x.ClockInDate < MinClockinDate && x.ClockOutDate == null) && (x.Site.Id == currentUser.SiteId));

当此方法运行并加载第一个User.Profile表时,它将加载所有时间记录并将其与所有用户相关联,这需要一分钟以上的时间,这太长了,因为最终记录数仅185条记录,但查询的初始负载正在运行27,000 * 560用户,即1500万条记录,并且随着时间的推移,这种情况只会变得更糟。

问题是如何在没有这种负载开销的情况下如何做到这一点,我知道我可以链接包含,但是由于包含的数量将根据所调用的数据的类型和用途而改变,因此我不能简单地对a进行硬编码包含链。

我也尝试过:

List<TimeClock> testLst =  _timeClock.GetAll(x => x.PendingReview || 
     (x.ClockInDate < MinClockinDate && x.ClockOutDate == null))
          .Select(x => new TimeClock{Id = x.Id,
                                     ClockInDate = x.ClockInDate, 
                                     ClockOutDate = x.ClockOutDate,
                                     TotalClockTime = x.TotalClockTime,
                                     Notes = x.Notes, 
                                     PendingReview = x.PendingReview, 
                                     Type = x.Type,
                                     User = x.User, 
                                     CampaignTimeClocks = x.CampaignTimeClocks,
                                     TimeClockAdjustments = x.TimeClockAdjustments,
                                     Site = x.User.Site}).ToList();

这将为我提供User.Profile信息,但Site和Type属性为null。

因此,我对如何在此处加载所需的数据感到迷茫。

非常感谢所有帮助。

你能先得到初始清单吗

List<TimeClock> testLst =  _timeClock.Where(x => x.PendingReview || (x.ClockInDate < MinClockinDate && x.ClockOutDate == null)).ToList();

然后调用以T作为参数的修改后的GetAll()

您所做的每个include最终都会在db中执行联接。 假设您的左表的记录大小非常大,为1024个字节,并且您有许多详细信息,例如1000,并且详细记录的大小仅为100。这将导致左表的信息重复1000次,该信息db将被连接起来,EF必须过滤掉重复的副本以创建您的左实例。

最好不使用include并进行显式加载。 基本上在相同的上下文中执行2个查询。

我有一个这样的例子,与您的例子不同,但我希望您能理解。 它可能比依赖include快多达10倍。 (数据库只能有效地处理有限数量的联接)

var adressen = adresRepository
                .Query(r => r.RelatieId == relatieId)
                .Include(i => i.AdresType)
                .Select().ToList();

var adresids = (from a in adressen select a.AdresId).ToList();
            IRepositoryAsync<Comm> commRepository = unitOfWork.RepositoryAsync<Comm>();

            var comms = commRepository
                .Query(c => adresids.Contains(c.AdresId))
                .Include(i => i.CommType)
                .Select();

对于我使用include的commType和adresType,因为存在1对1的关系,我避免了太多的联接,因此我的多个查询将比使用include的单个查询更快。 我没有在第一个查询中包括Comms来尝试避免第二个查询,关键是在这种情况下,两个查询比单个查询要快。

请注意,我的代码是使用我自己的存储库构建的,因此该代码对您不起作用,但是您可以从中得到启发。

我发现不使用Load()语句即可更有效地执行此操作的方法是将DBQuery更改为IQueryable并链接包含,返回执行的查询结果,以及一起删除DBQuery.Load()。 这将查询的执行时间从秒更改为毫秒。

    public List<T> GetAll(String[] include)
    {
        IQueryable<T> outQuery = ObjectSet;
        foreach (String s in include)
        {
            outQuery = outQuery.Include(s);
        }
        return outQuery.ToList();
    }

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM