Linq中设置外键SQLLinq、SQL

2023-09-03 02:39:00 作者:还是、不懂你

这是众所周知,你不能直接在LINQ的设置外键ID,以SQL,如果已经被加载的实体。然而,你可以看一下实体通过它的外键,然后设置实体使用实体关系的外国实体。 (我已经采取了枚举这里为简便起见使用的整数值)。即如果我有一个装约会实体和相关AppoinmentStatus实体我不能做到这一点: -

  ExistingAppointment.AppointmentStatusID = 7
 

不过,我可以做到这一点: -

  ExistingAppointment.AppointmentStatus =(从appstat在db.AppointmentStatus _
                                        其中appstat.StatusID = 7 _
                                        选择appstat)。单
 

我有这样的乱抛垃圾我的code的事情,我想重构。所以......

我能明显在这样的模块,使用一个辅助方法: -

 
模块辅助
    公共共享功能GetAppointmentStatus(BYVAL AppStatusID为整数)作为AppointmentStatus
        GetAppointmentStatus =(从appstat在db.AppointmentStatus _
                                       其中appstat.AppointmentStatusID = AppStatus _
                                       选择appstat)。单
    端功能
前端模块
 
LINQ to XML 结合 LINQ to SQL 使用

我甚至可以使它成为一个扩展方法,是这样的。

 
进口System.Runtime.CompilerServices
模块辅助
扩展()> _
    公共共享功能GetAppointmentStatus(BYVAL数据库作为DataClassesDataContext,BYVAL AppStatusID为整数)作为AppointmentStatus
        GetAppointmentStatus =(从appstat在db.AppointmentStatus _
                                       其中appstat.AppointmentStatusID = AppStatusID _
                                       选择appstat)。单
    端功能
前端模块
 

我还可以把这个中的LINQ to SQL分部类,像这样的。

 
部分公共类DataClassesDataContext
    公共职能GetAppointmentStatus(BYVAL AppStatusID为整数)作为AppointmentStatus
        GetAppointmentStatus =(从appstat在Me.AppointmentStatus _
                                       其中appstat.AppointmentStatusID = AppStatusID _
                                       选择appstat)。单
    端功能
末级
 

另外我可以把code在LINQ到SQL约会实体部分类是这样的: -

 
部分公共类预约
    公共职能GetAppointmentStatus(BYVAL数据库作为DataClassesDataContext,BYVAL AppStatusID为整数)作为AppointmentStatus
            GetAppointmentStatus =(从appstat在db.AppointmentStatus _
                                       其中appstat.AppointmentStatusID = AppStatusID _
                                       选择appstat)。单
    端功能
末级
 

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

解决方案

有思想的两大流派在此:

把逻辑在的DataContext (局部类,或者实际的类,如果你code你的的DataContext 手)。这样做的理由是,你的的DataContext 已经知道所有不同的实体,所以这是不创造任何额外的连接,而不是导致类臃肿。

缺点,当然是,如果你有这些API方法几百(你可能会最终),那么你的的DataContext 将很快开始变成一球泥,充满了每一个随机查询API的程序员决定扔在,你可以尝试通过分离相关功能集成到不同的情况下,相同的部分的DataContext 来打扫一下一流的,但是这真的只是一个象征性的改善。

把逻辑存储库类,即一个 AppointmentRepository 。两个优势,这种方法是:(a)使用依赖注入的资源库和IoC框架,如果你决定改变你的数据模型的能力,和(b)的事实,你坚持的的单一职责原则 - 它实际上使的感觉的的方法是它在哪里

主要缺点在仓库将这些包括:(a)他们可能会重复非常相似的逻辑,这是已经在你的的DataContext 作为存储过程; (二)有创建头痛,当涉及到事务管理(如果你还用它来保存)的一种方式; (三)当你开始有大量返回专门定制的DTO具体业务或报告定制查询,留给你无论是创建一个存储库为每个DTO,或创建一个主两照出选择实用工具信息库所有的DTO或某些松散相关的一群人。无论最终是一个相当糟糕的设计。

这些都是权衡;只有你可以决定哪些是你自己的目的更好。

我的将会的绝对提醒的对的扩展法的方法,因为扩展方法都难以发现(你不能只是输入法,并有智能感知拿起相关的参考),而且他们也根本没有必要的时候,你必须直接修改或扩展(通过谐音)原始类的能力。

我也建议不要延长预约类;我们使用的工具,如LINQ to SQL的一个原因是,所以我们可以用POCO实体的不的需要了解他们来自何处的任何处理。出于这个原因,我个人很反对的实体类来连接它们的的DataContext - 的依赖应该是单向的。

It's well known that you cannot set foreign key IDs directly in Linq to SQL if the entities have already been loaded. You can however look up the entity by it's foreign key and then set the entity as the foreign entity using the entity relationship. (I've taken out the enum here and used integer values for simplicity). i.e. If I have a loaded Appointment entity and an associated AppoinmentStatus Entity I can't do this:-

ExistingAppointment.AppointmentStatusID = 7

But I can do this:-

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

I have this kind of thing littering my code and I'd like to refactor. So...

I could obviously use a helper method in a module like this:-


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

I could even make this into an extension method, like this.


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

I could also put this in the Linq to SQL partial class, like this.


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

Further I could put the code in the Linq to SQL Appointment Entity partial class like this:-


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

Which should I do and why, or is there a better alternative?

解决方案

There are two main schools of thought on this:

Put the logic in the DataContext (partial class, or actual class if you code your DataContext by hand). The rationale behind this is that your DataContext already knows about all of your different entities, so this isn't creating any additional coupling and isn't leading to class bloat.

The disadvantage, of course, is that if you have a few hundred of these API methods (and you probably will, eventually) then your DataContext will quickly start turning into a ball of mud, filled with every random query API any programmer decides to throw in. You can try to clean this up by separating related functions into different instances of the same partial DataContext class, but that's really only a cosmetic improvement.

Put the logic in a repository class, i.e. an AppointmentRepository. Two advantages to this approach are (a) the ability to use dependency injection on the repository and an IoC framework, in case you decide to change your data model, and (b) the fact that you're sticking to the Single Responsibility Principle - it actually makes sense for the method to be where it is.

The main disadvantages to putting these in repositories are: (a) They may be duplicating very similar logic that's already in your DataContext as Stored Procedures; (b) they have a way of creating headaches when it comes to transaction management (if you also use them to save); and (c) when you start to have a lot of custom queries that return specially-tailored DTOs for specific operations or reports, you're left with the two crummy choices of either creating one repository for each and every DTO, or creating one master "utility" repository for all the DTOs or some loosely-related group of them. Both end up being a rather poor design.

Those are the tradeoffs; only you can decide which is better for your own purposes.

I would definitely advise against the extension-method approach, as extension methods are difficult to discover (you can't just type the method and have Intellisense pick up the relevant reference), and they're also simply not necessary when you have the ability to directly modify or extend (via partials) the original class.

I'd also advise against extending the Appointment class; one of the reasons we use tools like Linq To SQL is so we can deal with POCO entities that don't need to know anything about where they came from. For this reason I personally am very much against the coupling of entity classes to their DataContext - the dependency should be only one-way.