c# - Linq-to-SQL 加载 1 :1 Relations in a single query

标签 c# linq linq-to-sql

我们有几个具有多个 1:1 关系的类用于快速连接,虽然这对于用于表格显示的匿名类型来说效果很好,但我不确定如何在单个 linq 查询中完全填充该类型。

我们拥有这些属性是因为它是 1:1 关闭,或者我们不想通过子集合查询以找到每个显示器的“主要”,我们通过在保存时设置这些主要 ID 来承担成本。

本文上下文的精简示例:

public class Contact
{
  public long Id { get; set; }

  public EntitySet<Address> Addresses { get; set; }
  public EntityRef<Address> PrimaryAddress { get; set; }
  public long? PrimaryAddressId { get; set; }

  public EntitySet<Email> Emails { get; set; }
  public EntityRef<Email> PrimaryEmail { get; set; }
  public long? PrimaryEmailId { get; set; }

  public string FirstName { get; set; }
  public string LastName { get; set; }
}

public class Address
{
  public long Id { get; set; }
  public EntitySet<Contact> Contacts { get; set; }

  public bool IsPrimary { get; set; }
  public string Street1 { get; set; }
  public string Street2 { get; set; }
  public string City { get; set; }
  public string State { get; set; }
  public string Country { get; set; }
}

public class Email
{
  public long Id { get; set; }
  public EntitySet<Contact> Contacts { get; set; }

  public bool IsPrimary { get; set; }
  public string Address { get; set; }
}

问题是在显示联系人列表时,PrimaryAddressPrimaryEmail 必须延迟加载。如果我们执行 DataLoadOptions,它也不会产生预期的效果,因为它是 1:1,例如:

var DB = new DataContext();
var dlo = new DataLoadOptions();
dlo.LoadWith<Contact>(c => c.PrimaryAddress);
dlo.LoadWith<Contact>(c => c.PrimaryEmail);
DB.LoadOptions = dlo;

var result = from c in DB.Contacts select c;
result.ToList();

上面的代码产生了一个INNER JOIN,因为它把它当作父关系,它不考虑可为空的 FK 关系并左连接 1:1 属性。所需的查询类似于:

Select t1.*, t.2*, t3.*
From Contact t1
Left Join Address t2 On t1.PrimayAddressId = t2.Id
Left Join Email On t1.PrimaryEmailId = t3.Id

有没有一种方法可以做到这一点并获得一个填充了这些可空的 1:1 属性的 IQueryable,甚至是一个列表?由于其他限制,我们需要类型为 Contact,因此匿名类型将不起作用。对选项非常开放,任何东西都比延迟加载 n*(number of 1:1s)+1 查询我们显示的行数要好。

最佳答案

更新: 终于开始更新这个了,devart 的人已经修复了更高版本中的行为以完美地工作。根本不需要 DataLoadOptions,只需使用表格外的字段即可,例如:

var DB = new DataContext();
var result = from c in DB.Contacts
             select new {
               c.Id
               c.FirstName,
               c.LastName,
               Address = c.PrimaryAddress.Street1 + " " + c.PrimaryAddress.Street2 //...
               Email = c.PrimaryEmail.Address
             };

这正确地对相关的 AddressEmail 表执行单个左外连接。现在修复是特定于获取此匿名类型的情况...但他们还修复了我们 确实 需要它的 DataLoadOptions 行为,正确关闭外键现在输入。希望此更新对旧版本的其他人有所帮助...我强烈建议升级,自 5.35 以来的版本中有许多新的增强功能(许多让生活更容易)。


原文:
我们最终得到的是一种不同的方法。这可能是 devart: dotConnect for Oracle 的特定行为 提供商(从 5.35.62 版开始,如果此行为发生变化,我会尝试更新此问题)。

var DB = new DataContext();
var result = from c in DB.Contacts
             select new {
               c.Id
               c.FirstName,
               c.LastName,
               Address = new AddressLite { 
                               Street1 = c.PrimaryAddress.Street1, 
                               Street2 = c.PrimaryAddress.Street2, 
                               City = c.PrimaryAddress.City,
                               State = go.PrimaryAddress.State,
                               Country = go.PrimaryAddress.Country },
               Email = c.PrimaryEmail.Address
             };
result.ToList();

这会导致单个查询。在选择中调用子对象时,例如c.PrimaryAddress 不会 导致发生连接(导致大量select ... from address where id = n 延迟加载,我们正在显示的每行表格数据一个),但是在其上调用一个属性,例如c.PrimaryAddress.Street1 DOES 在查询查询的地址表中导致正确的左连接。上面的 linq 仅适用于 linq-to-sql,它会因 linq-to-entities 上的空引用而失败,但是......在我们处理的情况下这很好。


优点:

  • 单个查询,生成地址和电子邮件的左连接
  • 用于地址的轻量级对象和用于电子邮件的字符串(它们在实际项目中都有一些反向引用 EntiySet,这使得它们比简单的表格显示需求所必需的更昂贵)
  • 快速/干净,上面的查询比手动连接我们正在做的每个子表要简单得多,代码更简洁。
  • 性能,较重对象的创建非常成功,从电子邮件更改为字符串,地址更改为 AddressLite 以及(在整个项目中)电话更改为 PhoneLite 导致仅显示表格数据的页面从 300-500 毫秒缩短到 50 -100 毫秒。

坏处:

  • 匿名类型,在某些情况下我们需要一个强类型,必须创建这些类型(即使 ReSharper 完成此任务的速度一样快)会增加很多困惑。
  • 由于我们无法修改和保存匿名类型,或者我们在没有大量注释工作的情况下创建的任何类型,如果模型对此有任何更改,则必须更新这些注释工作。 (因为没有生成这些类)

关于c# - Linq-to-SQL 加载 1 :1 Relations in a single query,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/1753309/

相关文章:

c# - 如何从 JQuery 调用 Controller 方法?

c# - 如何访问 IQueryable<T> 对象中的连续元素?

c# - ASP.NET LINQ 错误无法创建类型为 'System.Collections.Generic.List` 1[System.Int32] 的查询结果'

c# - Linq 类的实例,但未将其连接到数据库

c# - DoFixture 错误 : Trying to follow fitSharp tutorial

c# - 有没有好的C#逆向工具?

c# - 是否可以在 Windows 控制台应用程序中使用 savefiledialog()

c# - SortedDictionary 按键的相反顺序

asp.net-mvc - 将序列化的 JQuery 可排序结果提交到数据库的最佳方法是什么?

c# - 使用 Linq 查询作为数据源的 ASP.NET GridView 分页