【问题标题】:Standardizing and Abstracting IQueryable construction with Linq to Entities使用 Linq to Entities 标准化和抽象 IQueryable 构造
【发布时间】:2015-03-17 16:28:40
【问题描述】:

TL;DR;在大型数据访问层中标准化和抽象 IQueryable 构造的最佳方法是什么?是否可以接受或鼓励扩展?

背景

我们使用带有存储库模式的 Entity Framework 6 作为我们的数据访问层。

为了使我们的数据调用更高效,我们最近开始使用一些结构化的数据传输对象来强制我们只从数据库中提取必要的数据。

例如:我们有一个仪表板,它使用实体映射数据库表的 500 个属性中的 15 个来创建配置文件的分页摘要。

我们直接从SELECT 语句中进行转换,而不是在转换中提取完整实体和映射:

//This is a simplified representation
public List<PersonDashboardDTO> GetPeopleByRangeForDashboard(int start, int length)
{
var returnPeople = new List<PersonDashboardDTO>();

IQueryable<PersonForDashboardDTO> People = databaseContext.Profile
    .Where(x => !x.IsDeleted)
    .OrderByDescending(x => x.LastName)
    .Skip(start).Take(length)
    .Select(y => new PersonForDashboardDTO
    {
        Name = String.Concat(y.FirstName, " ", y.LastName),
        Company = y.CompanyContact.Select(x => x.Company.Name).FirstOrDefault(),
        SummaryAddress = y.Address.AddressLine1,
        City = y.Address.City,
        IsEmailOK = y.Notifications.CanSendEmail,
    });

     returnPeople.AddRange(People);
     return returnPeople;
}

虽然这是一个简单的示例,但其中一些 SELECT 映射包含 150 多个属性,并且一遍又一遍地简单地复制和粘贴它违背了我的每一点。

这似乎也是有道理的,因为 IQueryable 在被强制转换为另一个对象 (like .ToList(), or List.AddRange(IQueryable&lt;&gt;) 之前不会执行,因此我们可以创建方法来以更结构化的方式抽象数据访问调用。

我不确定正确的模式是什么,但这是我的想法:

提案:扩展方法

public static IQueryable<PersonDashboardDTO> MapToPersonDashboardDTO(this IQueryable<Profile> profile)
{
    return profile.Select(y => new PersonDashboardDTO
    {
        Name = String.Concat(y.FirstName, " ", y.LastName),
        Company = y.CompanyContact.Select(x => x.Company.Name).FirstOrDefault(),
        SummaryAddress = y.Address.AddressLine1,
        City = y.Address.City,
        IsEmailOK = y.Notifications.CanSendEmail
    });
}

public static IQueryable<Profile> IsNotDeleted(this IQueryable<Profile> profile)
{
    return profile.Where(x => !x.IsDeleted);
}

public static IQueryable<Profile> OrderedByLastName(this IQueryable<Profile> profile)
{
    return profile.OrderByDescending(x => x.LastName);
}

public static IQueryable<Profile> TakeRange(this IQueryable<Profile> profile, int start, int length)
{
    return profile.Skip(start).Take(length);
}

示例实施

public List<PersonDashboardDTO> GetPeopleByRangeForDashboard(int start, int length)
{
    var returnPeople = new List<PersonDashboardDTO>();

    IQueryable<PersonDashboardDTO> People = databaseContext.Profile
        .IsNotDeleted()
        .OrderedByLastName()
        .TakeRange(start, length)
        .MapToPersonDashboardDTO();

    returnPeople.AddRange(People);

    return returnPeople;
}

总结

这是一种可接受且可用的模式,可广泛用于标准化我们使用 EF6 进行的查询吗?这似乎是一条不错的路,但我在这里找不到太多标准和做法,希望能有一些新鲜的眼光。

【问题讨论】:

  • 如果它对您有用,并且对您有帮助,那就太好了,请继续努力。如果您在实施其中一种方法时遇到特定问题,那么 就是您应该询问的问题。
  • @Servy 我认为它会起作用,但是关于正式抽象 LINQ To Entity Queries 的信息很少,这让我停下来。我不知道是否有理由不更多地使用它,但我想把它扔给社区看看是否有人有充分的理由赞成或反对。
  • 如果你想知道它是否有效试试。如果有效,很好,如果无效,请解释问题所在,我们可以帮助您。
  • 如果我正在寻找更多架构建议@Servy,我应该在哪里发帖?编程击落了它,所以我不知道在哪里发布这样的东西。
  • 程序员并没有说它跑题了。他们告诉你不要越过帖子(因为你不应该)。它不是关于 SO 的话题。

标签: c# linq entity-framework linq-to-entities entity-framework-6


【解决方案1】:

对我来说似乎是合理的,但我会做出以下更改:

public IQueryable<PersonDashboardDTO> GetPeopleByRangeForDashboard(int start, int length)
{
    return databaseContext.Profile
        .IsNotDeleted()
        .OrderedByLastName()
        .TakeRange(start, length)
        .MapToPersonDashboardDTO();
}

在大多数情况下,DAL 没有理由将结果转换为 List 而不仅仅是返回 IQueryable,如果您的应用程序不需要对象的某些字段,它可以/应该在枚举之前重新转换为不太完整的对象它。在大多数情况下,这将导致更快的数据库访问,在某些情况下相当显着。特别是如果没有使用像地址这样的字段,那么数据库可以删除连接。

public static IQueryable<T> TakeRange<T>(this IQueryable<T> profile, int start, int length)
{
    return profile.Skip(start).Take(length);
}

没有理由将此附加到仅返回配置文件的查询。这适用于任何 IQueryable。

【讨论】:

  • 仅供参考 - 我们不会尝试从 DB -> DAL -> Controller -> Presentation 来扁平化我们的对象模型。这就是我们的实现。我们有一个被返回的对象,它没有被展平,控制器将请求基本对象以及一个位标志字段,该字段描述它需要的对象的哪些部分到一个全局函数中,然后应用适当的 .Includes 以便为所有必要的字段发出一个 db 请求。然后控制器将基础对象转换为一个更简单的对象,然后再使用。
  • 类似_db.GetSomething(Something.Address | Something.City).Select(x=&gt;new SimpleSomething{x.Name,x.Address,x.City}).OrderBy(...).ToList();
猜你喜欢
  • 1970-01-01
  • 2011-04-08
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2010-10-24
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多