简体   繁体   English

一对一使用ICollection和Entity Framework,以及如何提高性能

[英]Using ICollection with Entity Framework one to many, and how to improve performance

I recently started to work with EF and MVC in .Net Web Applications, and I've encountered an issue, and wanted to see what your thoughts were and whether you would be able to point me in the right direction. 我最近开始在.Net Web应用程序中使用EF和MVC,但遇到了一个问题,想查看您的想法,以及是否能够为我指明正确的方向。

So, I've got two classes: DataGroup and DataElements. 因此,我有两个类:DataGroup和DataElements。 A DataElement can be in one DataGroup or none. 一个DataElement可以在一个DataGroup中,也可以不在一个DataGroup中。 I've used fluent API in the DataContext to achieve this: 我在DataContext中使用了流利的API来实现此目的:

    modelBuilder.Entity<DataGroup>()
            .HasMany(dg => dg.DataElements)
            .WithOptional() 
            .HasForeignKey(de => de.DataGroup_Id);

In my DataGroup class I've got the following: 在我的DataGroup类中,我有以下内容:

    public virtual ICollection<DataElement> DataElements { get; set; }

    [NotMapped]
    public ICollection<DataElement> RecentDataElements //returns the last 15 data elements
    {
        get
        {
            return DataElements.OrderByDescending(de => de.GeneratedDateTime).Take(15).Reverse().ToList();
        }
    }

    [NotMapped]
    public DataElement LatestDataElement //returns the latest data element (if any)
    {
        return DataElements.OrderByDescending(de => de.GeneratedDateTime).FirstOrDefault();
    }

So the issue I have with the code above is that when I call LatestDataElement or RecentDataElements, I end up waiting quite a bit. 因此,上面的代码存在的问题是,当我调用LatestDataElement或LatestDataElements时,最终要等待很多时间。

I believe this is because the DataElements property in the DataGroup class is an ICollection and RecentDataElements and LatestDataElement end up causing EF to load all the DataElements into memory before sorting and returning a sub-set (there could be thousands of DataElements in a DataGroup). 我认为这是因为DataGroup类中的DataElements属性是一个ICollection,最终导致EF在排序和返回子集之前(一个DataGroup中可能有成千上万个DataElement)将EF将所有DataElement加载到内存中。

Is there a way to make this more efficient? 有没有办法使它更有效?

I've toyed with the idea of querying the datacontext directly rather than using the DataElements property, but I wanted to see if there were any other options I should consider. 我喜欢直接查询数据上下文而不是使用DataElements属性的想法,但是我想看看是否还有其他应考虑的选项。 I've been told by colleagues that it would be bad practice to put the datacontext in a model (whether they're right or wrong is a different issue). 同事们告诉我,将数据上下文放入模型中是不好的做法(无论是对还是错是另一个问题)。

Thank you for your help and advice. 感谢您的帮助和建议。 It's much appreciated :) 非常感谢:)

In short: No . 简而言之:

As the name of the NotMapped attribute already suggests, this is a property linq-to-entities has no knowledge of whatsoever and it's impossible to populate it by filtering on the database. 正如NotMapped属性的名称所暗示的那样,这是一个linq-to-entities属性,不知道任何内容,因此无法通过对数据库进行过滤来填充它。 What I would do in your place is remove the Notmapped properties from your model all together and create a viewmodel: 我要在您的位置执行的操作是从模型中一起删除Notmapped属性,并创建一个viewmodel:

public class DataGroupViewModel
{
    public DataGroup DataGroup {get; set;}

    public ICollection<DataGroup> RecentDataElements {get; set;}
}

And use projection in your query to populate this view: 并在您的查询中使用投影来填充此视图:

var result = ctx.DataGroups.Where(...).Select(d => new DataGroupViewModel
{
    DataGroup = d;
    RecentDataElements = d.DataElements.OrderByDescending(de => de.GeneratedDateTime)
       .FirstOrDefault();
}

This of course forces you to create another model but it's the cleanest and fastest way to do it. 当然,这会迫使您创建另一个模型,但这是最干净,最快的方法。 Also your colleagues are right, it is a bad way to do db calls in your model, a model is a container for data, nothing more than that, it shouldn't have logic inside it to query the database. 您的同事也是对的,在模型中执行db调用一种不好的方法,模型是用于存储数据的容器,仅此而已,它内部不应具有查询数据库的逻辑。

To make it a bit cleaner you can write some extension methods you can reuse to get the models: 为了使它更简洁一点,您可以编写一些扩展方法,这些方法可以重复使用以获取模型:

Func<IQueryable<DataGroup>,IEnumerable<DataGroupViewModel>> GetDataGroupDTO = 
   d => d.Select(dt => new DataGroupViewModel 
   {
      DataGroup = dt;
      RecentDataElements = dt.DataElements.OrderByDescending(de => de.GeneratedDateTime)
       .FirstOrDefault();
   }

Then you can write your query more clean: 然后,您可以更干净地编写查询:

IEnumerable<DataGroupViewModel> result = ctx.DataGroups.Where(...).GetDataGroupDTO();

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

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