【问题标题】:Setting Foreign keys in Linq to SQL将 Linq 中的外键设置为 SQL
【发布时间】:2009-01-26 13:29:53
【问题描述】:

众所周知,如果实体已经加载,则不能直接在 Linq to SQL 中设置外键 ID。但是,您可以通过实体的外键查找实体,然后使用实体关系将该实体设置为外部实体。 (为了简单起见,我在这里取出了枚举并使用了整数值)。即,如果我有一个加载的约会实体和一个关联的 AppoinmentStatus 实体,我不能这样做:-

ExistingAppointment.AppointmentStatusID = 7

但我可以这样做:-

ExistingAppointment.AppointmentStatus = (From appstat In db.AppointmentStatus _
                                        Where appstat.StatusID = 7 _
                                        Select appstat).Single

我的代码中有这样的东西,我想重构。所以...

我显然可以在这样的模块中使用辅助方法:-


Module Helper
    Public Shared Function GetAppointmentStatus(ByVal AppStatusID As Integer) As AppointmentStatus
        GetAppointmentStatus = (From appstat In db.AppointmentStatus _
                                       Where appstat.AppointmentStatusID = AppStatus _
                                       Select appstat).Single
    End Function
End Module

我什至可以把它变成一个扩展方法,像这样。


Imports System.Runtime.CompilerServices
Module Helper
Extension()> _
    Public Shared Function GetAppointmentStatus(ByVal db as DataClassesDataContext, ByVal AppStatusID As Integer) As AppointmentStatus
        GetAppointmentStatus = (From appstat In db.AppointmentStatus _
                                       Where appstat.AppointmentStatusID = AppStatusID _
                                       Select appstat).Single
    End Function
End Module

我也可以把它放在 Linq to SQL 部分类中,像这样。


Partial Public Class DataClassesDataContext    
    Public Function GetAppointmentStatus(ByVal AppStatusID As Integer) As AppointmentStatus
        GetAppointmentStatus = (From appstat In Me.AppointmentStatus _
                                       Where appstat.AppointmentStatusID = AppStatusID _
                                       Select appstat).Single
    End Function
End Class

此外,我可以将代码放在 Linq to SQL Appointment Entity 部分类中,如下所示:-


Partial Public Class Appointment    
    Public Function GetAppointmentStatus(ByVal db as DataClassesDataContext, ByVal AppStatusID As Integer) As AppointmentStatus
            GetAppointmentStatus = (From appstat In db.AppointmentStatus _
                                       Where appstat.AppointmentStatusID = AppStatusID _
                                       Select appstat).Single
    End Function
End Class

我应该怎么做,为什么,或者有更好的选择吗?

【问题讨论】:

标签: .net vb.net linq-to-sql


【解决方案1】:

对此有两种主要的思想流派:

  1. 将逻辑放入DataContext(部分类,或者如果您手动编写DataContext,则为实际类)。这背后的基本原理是您的 DataContext 已经知道您所有不同的实体,因此这不会产生任何额外的耦合,也不会导致类膨胀。

    当然,缺点是如果你有几百个这样的 API 方法(你可能最终会这样做),那么你的 DataContext 将很快变成一团泥,充满了每个随机查询 API任何程序员都决定投入其中。您可以尝试通过将相关函数分离到同一部分 DataContext 类的不同实例中来清理它,但这实际上只是表面上的改进。

  2. 将逻辑放在存储库类中,即AppointmentRepository。这种方法的两个优点是(a)能够在存储库和 IoC 框架上使用依赖注入,以防您决定更改数据模型,以及(b)您坚持使用 Single Responsibility Principle 的事实 -它实际上使 有意义 方法位于它所在的位置。

    将这些放在存储库中的主要缺点是:(a) 它们可能将您的DataContext 中已经存在的非常相似的逻辑复制为存储过程; (b) 在交易管理方面,他们有一种让人头疼的方式(如果您还使用它们来保存); (c) 当您开始有大量自定义查询返回针对特定操作或报告的特别定制的 DTO 时,您将面临两个糟糕的选择,要么为每个 DTO 创建一个存储库,要么创建一个主所有 DTO 或其中一些松散相关组的“实用程序”存储库。两者最终都是一个相当糟糕的设计。

这些是权衡;只有您可以决定哪个更适合您自己的目的。

绝对建议反对扩展方法方法,因为扩展方法很难发现(您不能只键入方法并让 Intellisense 获取相关参考),当您能够直接修改或扩展(通过部分)原始类时,它们也根本没有必要。

我还建议不要扩展 Appointment 类;我们使用 Linq To SQL 之类的工具的原因之一是,我们可以处理那些不需要知道它们来自何处的 POCO 实体。出于这个原因,我个人非常反对将实体类耦合到它们的DataContext - 依赖关系应该只是单向的。

【讨论】:

    【解决方案2】:

    我通常将这些方法放在DataContext的分部类中,理论上这些方法在某种意义上类似于存储过程,并且存储过程表现为DataContext上的方法。

    无论您决定采用哪种方式,都绝对值得重构,这样您就可以只在一个地方使用此查询并且不要重复自己。这也使您可以选择将您描述的简单方法替换为更复杂的方法,该方法缓存查询的编译版本并重用它,而无需更新该方法的所有调用者。

    【讨论】:

      【解决方案3】:

      AppointmentStatusId 的设置器通常看起来像:(C# 中的代码,但应该很容易获得)

      [Column(Storage="_AppointmentStatusId", DbType="Int")]
      public System.Nullable<int> AppointmentStatusId
      {
          get
          {
              return this._AppointmentStatusId;
          }
          set
          {
              if ((this._AppointmentStatusId != value))
              {
                  if (this._AppointmentStatus.HasLoadedOrAssignedValue)
                  {
                      throw new System.Data.Linq.ForeignKeyReferenceAlreadyHasValueException();
                  }
                  this.OnCapabilityIdChanging(value);
                  this.SendPropertyChanging();
                  this._AppointmentStatusId = value;
                  this.SendPropertyChanged("AppointmentStatusId");
                  this.OnCapabilityIdChanged();
              }
          }
      }
      

      所以,如果我是你,我会在设计器生成的部分文件中添加一个方法 SetAppointmentStatusId(int statusId),并在需要时为其他属性添加更多。您甚至可以选择将属性的设置器设为私有(从设计器中选择属性并按 F4 键以打开 VS 属性窗口以更改可访问性)。
      在这种方法中,我将编写与 setter 相同的代码,只是它不包含此部分:

                  if (this._AppointmentStatus.HasLoadedOrAssignedValue)
                  {
                      throw new System.Data.Linq.ForeignKeyReferenceAlreadyHasValueException();
                  }
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2011-03-29
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2017-01-18
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多