想象一个简单的CRUD数据库应用程序,它使用bigint
/ Int64
作为所有IDENTITY主键的标准类型。
在这种情况下,Foo和Bar参与多对一关系:
class Foo {
Int64 FooId { get; set; }
Int64 ParentBarId { get; set; }
}
class Bar {
Int64 BarId { get; set; }
}
很明显,BarId
和FooId
属性的值虽然属于同一数据类型,但没有域名理由可以比较或相互分配,所以这没有意义:
Foo foo1 = ...
Foo foo2 = ...
Bar bar = ...
foo1.ParentBarId = bar.BarId; // ok...
foo2.ParentBarId = foo1.FooId; // programmer error!
这个问题类似于这个和其他语言中存在的“单位”问题,其中两个值被编译器认为是兼容的(例如,double
值代表一个对象中的英寸,而厘米代表另一个对象),即使他们是不相容的。
我想知道是否存在针对此问题的惯用C#解决方案,即强制执行编译类型数据成员不兼容。
确实存在各种C#单元库(例如https://github.com/objorke/QuantityTypes/tree/master/Source/QuantityTypes/Quantities),它们通过使每个实际数量为struct
并具有内部标准化表示(例如SI)来解决此问题。通过采用这种方法,我的域模型如下所示:
struct FooId { Int64 Value; }
struct BarId { Int64 Value; }
class Foo {
FooId FooId { get; set; }
BarId ParentBarId { get; set; }
}
class Bar {
BarId BarId { get; set; }
}
问题是,这很快变得难以处理,因为您需要取消引用Int64 Value
属性以对其执行任何操作(尽管您可以直接复制该值):
foo1.ParentBarId = bar.BarId; // okay...
foo2.ParentBarId = foo1.FooId; // this is now disallowed by the compiler, good!
databaseClient.GetBar( foo1.ParentBarId.Value ) // but this means extra typing if the `GetBar` method (perhaps generated by EF) expects `Int64` instead of `BarId`.
解决方法是添加implicit
运算符,但由于.NET中的struct
没有继承,因此必须在每个结构类型上手动定义它,这会使输入保存失败。允许隐式转换的另一个问题是,这意味着这成为可能(如果定义了to-Int64
和from-Int64
隐式转换):FooId > Int64 > BarId
- 这无法尝试引入域值安全性。< / p>
假设这些问题是可以克服的,是否可以将其与Entity Framework进行交互?这将要求EF同时支持使用struct
复杂类型(不支持),将1个成员(“1-uple”?)复杂类型展平为持久层的单列值,最后支持这对于主键列(增加了复杂性)。
有没有人有幸使用这种或类似的方法来实现域值的编译时正确性?