Mic*_*lGG 15 nhibernate f# nhibernate-mapping
我想知道是否有任何相对简单的方法来扩展NHibernate以支持F#的歧视联盟.不仅仅是单个IUserType或ICompositeUserType,而是一些通用的东西,无论DU的实际内容如何,我都可以重复使用.
例如,假设我有一个名为RequestInfo的属性,它是一个定义为的联合:
type RequestInfo =
| Id of int
| Name of string
Run Code Online (Sandbox Code Playgroud)
这将编译为一个抽象的RequestInfo类,具有具体的子类Id和Name.我可以通过F#反射得到所有这些信息.在这种情况下,我可以使用"RequestInfo_Tag","RequestInfo_Id","RequestInfo_Name"将其存储在数据库中.
因为我是NHibernate的新手,我会遇到什么样的问题试图遵循这种方法?更复杂的案件是不可能处理的?例如,嵌套的歧视联盟呢?有没有办法可以将其他联盟的读取"移交"到另一个ICompositeUserType?
更重要的是,这会搞砸我的查询功能吗?意思是,我是否必须知道DB中的实际列名; 我将无法执行Criteria.Eq(SomeDiscUnion)并将其全部整理出来?
我不是在寻找一个完整的"提供代码"的答案,只是一些一般的建议,如果这是值得追求的(以及如何),或者我应该重新考虑我的模型.
谢谢!
PS不要太粗鲁,但如果你的答案是"使用C#",那就不是很有帮助了.
我没有勇敢尝试将NHibernate与F#的类型系统一起使用,但从F#编译器实际生成的角度来看可能会有所帮助.
如果你在反射器中查看Discriminated Union,实际上会生成三个类(如果计算私有调试代理,则会有更多类).
public abstract class RequestInfo : IStructuralEquatable, IComparable, IStructuralComparable
Run Code Online (Sandbox Code Playgroud)
第一个类RequestInfo是抽象的,实际上是由union中的其他类型实现的.
// Nested Types
[Serializable, DebuggerTypeProxy(typeof(Program.RequestInfo._Id@DebugTypeProxy)), DebuggerDisplay("{__DebugDisplay()}")]
public class _Id : Program.RequestInfo
{
// Fields
[DebuggerBrowsable(DebuggerBrowsableState.Never), CompilerGenerated, DebuggerNonUserCode]
public readonly int id1;
// Methods
[CompilerGenerated, DebuggerNonUserCode]
public _Id(int id1);
}
[Serializable, DebuggerTypeProxy(typeof(Program.RequestInfo._Name@DebugTypeProxy)), DebuggerDisplay("{__DebugDisplay()}")]
public class _Name : Program.RequestInfo
{
// Fields
[DebuggerBrowsable(DebuggerBrowsableState.Never), CompilerGenerated, DebuggerNonUserCode]
public readonly string name1;
// Methods
[CompilerGenerated, DebuggerNonUserCode]
public _Name(string name1);
}
Run Code Online (Sandbox Code Playgroud)
所以当你这样做时:
let r=Id(5)
let s=Name("bob")
Run Code Online (Sandbox Code Playgroud)
r和s分别是_Id和_Name的实例.
因此,您的问题的答案可能是以下问题之一的答案:
不幸的是,我不够精明,不能给你一个连贯的答案,但我相信这里的其他人至少完成了这三个解决方案中的一个.
我想你可以使用与继承策略相同的方法,例如使用鉴别器列,但我担心缺少默认构造函数会使这个问题出现.所以我倾向于认为使用自定义类型是解决方案.
在一些摆弄之后,这里有一个(可能是错误的或破坏的)自定义用户类型:
type RequestInfo =
| Id of int
| Name of string
type RequestInfoUserType() as self =
interface IUserType with
member x.IsMutable = false
member x.ReturnedType = typeof<RequestInfo>
member x.SqlTypes = [| NHibernate.SqlTypes.SqlType(Data.DbType.String); NHibernate.SqlTypes.SqlType(Data.DbType.Int32); NHibernate.SqlTypes.SqlType(Data.DbType.String) |]
member x.DeepCopy(obj) = obj //Immutable objects shouldn't need a deep copy
member x.Replace(original,target,owner) = target // this might be ok
member x.Assemble(cached, owner) = (x :> IUserType).DeepCopy(cached)
member x.Disassemble(value) = (x :> IUserType).DeepCopy(value)
member x.NullSafeGet(rs, names, owner)=
// we'll use a column as a type discriminator, and assume the first mapped column is an int, and the second is a string.
let t,id,name = rs.GetString(0),rs.GetInt32(1),rs.GetString(2)
match t with
| "I" -> Id(id) :> System.Object
| "N" -> Name(name) :> System.Object
| _ -> null
member x.NullSafeSet(cmd, value, index)=
match value with
| :? RequestInfo ->
let record = value :?> RequestInfo
match record with
| Id(i) ->
cmd.Parameters.Item(0) <- "I"
cmd.Parameters.Item(1) <- i
| Name(n) ->
cmd.Parameters.Item(0) <- "N"
cmd.Parameters.Item(2) <- n
| _ -> raise (new ArgumentException("Unexpected type"))
member x.GetHashCode(obj) = obj.GetHashCode()
member x.Equals(a,b) =
if (Object.ReferenceEquals(a,b)) then
true
else
if (a=null && b=null) then
false
else
a.Equals(b)
end
Run Code Online (Sandbox Code Playgroud)
这个代码肯定会更通用,并且可能不应该在你的实际域层中,但我认为对IUserType的F#实现进行一次尝试会很有用.
您的映射文件将执行以下操作:
<property name="IdOrName" type="MyNamespace.RequestInfoUserType, MyAssembly" >
<column name="Type"/>
<column name="Id"/>
<column name="Name"/>
</property>
Run Code Online (Sandbox Code Playgroud)
您可能会在没有"Type"列的情况下离开,只需稍微调整自定义UserType代码即可.
我不知道这些自定义用户类型如何使用查询/ ICriteria,因为我以前没有真正使用过自定义用户类型.
| 归档时间: |
|
| 查看次数: |
1390 次 |
| 最近记录: |