简体   繁体   English

实体框架/ LINQ-从大型嵌套实体数据集中返回数据传输对象

[英]Entity Framework/LINQ - Returning data transfer objects from a large nested entity data set

I am using WebAPI and Entity Framework to build a REST API that points to a large MSSQL database (~200 tables). 我正在使用WebAPI和实体框架构建指向大型MSSQL数据库(约200个表)的REST API。 The database is very normalized, so retrieving values that are useful to the consumer of the API requires a lot of drilling down into the tables. 该数据库非常规范化,因此要检索对API使用者有用的值,需要对表进行大量深入研究。

In order to return useful data to the consumer, I have taken the approach of building models (or DTOs) using a factory pattern. 为了将有用的数据返回给消费者,我采用了使用工厂模式构建模型(或DTO)的方法。 However, I noticed that even though all the data was being returned in a nice format, there were performance issues due to lazy loading being enabled. 但是,我注意到即使所有数据都以一种不错的格式返回,但由于启用了延迟加载,仍然存在性能问题。 In short, I was querying too much data even while I was returning only the data I needed. 简而言之,即使我只返回所需的数据,我也查询了太多的数据。

So I resorted to turning off lazy loading and have attempted to grab the data explicitly using Include methods: 因此,我采取了关闭延迟加载的方式,并尝试使用Include方法显式获取数据:

var accessions = db.AccessionParties
  .Include(ap => ap.Accession.AccessionParties.Select(ap2 => ap2.Party))

  .Include(ap => ap.Accession.AccessionParties.Select(ap2 => ap2.AccessionPartyPurposes.Select  (app => app.PartyAccessionPurposeType)))

  .Include(ap => ap.Accession.AccessionAnimals.Select(x => x.AnimalInformationType))

  .Include(ap => ap.Accession.AccessionAnimals.Select(x => x.Specimens.Select(y => y.AccessionTestRequestSpecimens.Select(z => z.AccessionTestRequest.LabTestOffering.TestOffering))))

  .ToList()
  .Select(a => modelFactory.CreateAccessionModel(a.Accession));

Below is an example of the factory method I'm using to generate the model, which includes nested factory methods as well to shape my related data entities. 以下是我用来生成模型的工厂方法的示例,其中包括嵌套的工厂方法以及塑造相关数据实体的形状。

  public AccessionModel CreateAccessionModel(Accession accession)
    {
        return new AccessionModel()
        {
            AccessionKey = accession.AccessionKey,
            SubmittedDate = accession.SubmittedDate,
            Parties = accession.AccessionParties
                                .Select(accessionParty => new { accessionParty = accessionParty, accessionParty.Party })
                                .Select(accessionParty => CreatePartyModel(accessionParty.Party)),
            Animals = accession.AccessionAnimals.Select(accessionAnimal => CreateAccessionAnimalModel(accessionAnimal))
        };
    }

Are there any patterns or practices for handling the above situation? 是否有处理上述情况的模式或做法? I've seen some examples of a method that allows you to pass in an array of include statements, but I cannot think of a way to handle this in an elegant, efficient, pragmatic way. 我已经看到了一些方法的示例,该方法使您可以传递包含语句的数组,但是我无法想到一种以优雅,高效,实用的方式处理此问题的方法。 Any input would be much appreciated. 任何输入将不胜感激。

I do this in one of my methods passing in an array of strings like so (it's ugly because I'm using reflection to get the property names but hopefully you get the idea) 我通过传递这样的字符串数组的方法之一来执行此操作(这很丑陋,因为我正在使用反射来获取属性名称,但希望您能理解)

private IQueryable<T> AddIncludes<T>(IDatabase db) // my Entity context class implements IDatabase
{
    var props = typeof(IDatabase).GetProperties(BindingFlags.Public|BindingFlags.Instance);
    IQueryable<T> ret = null;
    foreach (var prop in props)
    {
        if (prop.PropertyType.Name == "IDbSet`1" &&
        prop.PropertyType.GetGenericArguments()[0] == typeof(T))
        {
            ret = (IQueryable<T>)prop.GetValue(db, null);
            break;
        }
    }
    var includes = GetIncludes((DbContext)db, typeof(T)); // this returns an IEnumerable<string> of the includes
    foreach (string include in includes) // replace string with a lambda
    {
        ret = ret.Include(include); // this is where the includes are added
    }
    return ret;
}

private ICollection<T> LoadObjectGraph<T>(IDatabase db, Func<T, bool> filter)
{
    var queryableWithIncludesAdded = AddIncludes<T>(db);
    return queryableWithIncludesAdded.Where(filter).ToList();
} 

Instead of using strings, you could use the same technique to pass lambdas to build your query 除了使用字符串,您还可以使用相同的技术来传递lambda以构建查询

You can use automapper to map between the entities and the DTOs and together with the Projection you can execute the query and load only the columns your DTO needs. 您可以使用自动映射器在实体和DTO之间进行映射,并与Projection一起执行查询并仅加载DTO所需的列。 Check http://automapper.org/ and https://github.com/AutoMapper/AutoMapper/wiki/Queryable-Extensions 检查http://automapper.org/https://github.com/AutoMapper/AutoMapper/wiki/Queryable-Extensions

Hope that helps. 希望能有所帮助。

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

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