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

时间:2014-12-05 23:29:15

标签: c# linq entity-framework factory dto

我正在使用WebAPI和Entity Framework构建一个指向大型MSSQL数据库(~200个表)的REST API。数据库非常规范化,因此检索对API的使用者有用的值需要大量深入到表中。

为了将有用的数据返回给消费者,我采用了使用工厂模式构建模型(或DTO)的方法。但是,我注意到即使所有数据都以漂亮的格式返回,但由于启用了延迟加载,因此存在性能问题。简而言之,即使在我只返回我需要的数据时,我也在查询过多的数据。

所以我使用了关闭延迟加载并尝试使用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));

下面是我用来生成模型的工厂方法的示例,其中包括嵌套的工厂方法以及我的相关数据实体的形状。

  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))
        };
    }

是否有处理上述情况的模式或做法?我已经看到了一些允许你传入一系列include语句的方法的例子,但是我想不出一种以优雅,高效,实用的方式处理它的方法。任何输入都将非常感激。

2 个答案:

答案 0 :(得分:0)

我在我的一个方法中这样做,传递一个像这样的字符串数组(它很难看,因为我使用反射来获取属性名称,但希望你能得到这个想法)

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();
} 

您可以使用相同的技术传递lambdas来构建查询

,而不是使用字符串

答案 1 :(得分:0)

您可以使用automapper在实体和DTO之间进行映射,并与Projection一起执行查询并仅加载DTO所需的列。查看http://automapper.org/https://github.com/AutoMapper/AutoMapper/wiki/Queryable-Extensions

希望有所帮助。