NHibernate中的歧视工会

NHibernate中的歧视工会,nhibernate,f#,nhibernate-mapping,Nhibernate,F#,Nhibernate Mapping,我想知道是否有相对简单的方法来扩展NHibernate以支持F#的歧视性联盟。不仅仅是一个IUserType或ICompositeUserType,而是一些我可以重复使用的通用类型,而不管DU的实际内容如何 例如,假设我有一个名为RequestInfo的属性,它是一个定义为: type RequestInfo = | Id of int | Name of string 这将编译成一个抽象的RequestInfo类,带有具体的子类Id和名称。我可以通过F#reflectio

我想知道是否有相对简单的方法来扩展NHibernate以支持F#的歧视性联盟。不仅仅是一个IUserType或ICompositeUserType,而是一些我可以重复使用的通用类型,而不管DU的实际内容如何

例如,假设我有一个名为RequestInfo的属性,它是一个定义为:

type RequestInfo =  
    | Id of int
    | Name of string
这将编译成一个抽象的RequestInfo类,带有具体的子类Id和名称。我可以通过F#reflection很好地获得所有这些信息。在本例中,我可以使用“RequestInfo\u标记”、“RequestInfo\u Id”、“RequestInfo\u名称”将其存储在数据库中

作为一名NHibernate新手,我在尝试采用这种方法时会遇到什么样的问题?更复杂的案件是否不可能处理?例如,嵌套的有歧视的联合呢?是否有一种方法可以将对联合体其余部分的读取“传递”给另一个ICompositeUserType

更重要的是,这会影响我的查询能力吗?也就是说,我必须知道数据库中的实际列名;我不能做Criteria.Eq(SomeDiscUnion)并把它全部整理好

我不是在寻找一个完整的“提供代码”的答案,只是一些一般性的建议,如果这是值得遵循的(以及一些关于如何提供代码的指针),或者如果我应该重新思考我的模型

谢谢


请注意,不要粗鲁,但如果你的答案是“使用C#”,那就没什么帮助了

我没有足够的勇气尝试将NHibernate与F#的类型系统一起使用,但从F#编译器实际生成的内容的角度来看可能会有所帮助

如果您查看reflector中的区分并集,实际上生成了三个类(如果将私有调试代理计算在内,则会生成更多类)

第一个类RequestInfo是抽象的,实际上由联合体中的其他类型实现

 // 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);
    }
所以当你这样做的时候:

 let r=Id(5)
 let s=Name("bob")
r和s分别是_Id和_Name的实例

因此,您的问题的答案可能是以下问题之一的答案:

  • 如何映射到nhibernate中的抽象类
  • 如何使NHibernate使用工厂方法
  • 如何创建映射到不可变对象的Nhibernate
  • 如何在NHibernate中实现自定义类型(可能是使用IUserType)
不幸的是,我没有足够的悟性给你一个连贯的答案,但我相信这里的其他人至少做过这三个解决方案中的一个

我认为您可以使用与继承策略相同的方法,例如使用鉴别器列,但我担心缺少默认构造函数会导致此问题。所以我倾向于认为使用自定义类型是解决方案

经过一些修改后,这里有一个(可能有bug或损坏)自定义用户类型:

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
type RequestInfo=
|int的Id
|字符串名称
将RequestInfoUserType()键入为self=
IUserType与的接口
成员x.IsMutable=false
成员x.ReturnedType=类型
成员x.SqlTypes=[|NHibernate.SqlTypes.SqlType(Data.DbType.String);NHibernate.SqlTypes.SqlType(Data.DbType.Int32);NHibernate.SqlTypes.SqlType(Data.DbType.String)|]
成员x.DeepCopy(obj)=obj//不可变对象不需要深度副本
member x.Replace(original、target、owner)=target//这可能没问题
成员x.Assemble(缓存,所有者)=(x:>IUserType).DeepCopy(缓存)
成员x.decompose(值)=(x:>IUserType).DeepCopy(值)
成员x.NullSafeGet(rs、名称、所有者)=
//我们将使用列作为类型鉴别器,并假设第一个映射列是int,第二个是字符串。
设t,id,name=rs.GetString(0),rs.GetInt32(1),rs.GetString(2)
匹配
|“I”->Id(Id):>System.Object
|“N”->Name(Name):>System.Object
|->null
成员x.NullSafeSet(cmd、值、索引)=
匹配值
| :? 请求信息->
让记录=值:?>RequestInfo
将记录与
|Id(i)->

cmd.Parameters.Item(0)是一个非常有趣的问题。我对NHibernate和F#都相当熟练,我会在有时间的时候解决这个问题至少在我的经验中,我很难将NHibernate与F#一起使用,主要是因为NHibernate要求对象有一个默认构造函数(对于联合类型、记录类型和大多数类来说通常不是这种情况),而且它依赖于为类赋值的易变性(这在F#类定义中常常很烦人)。通常情况下,我通过滚动我自己的mirco-ORM让数据库与F#一起玩。是的,在我们的上一个项目中,我们只是坚持使用普通的旧对象类型,以便它们与NHibernate很好地玩。这一次我想扩展一点,尝试一些更现实的模型。如果你没有得到一个好的答案,我认为一个针对NHibernate的F#API将是一个很棒的项目,可以从Codeplex开始:)是的,是的:)。我不介意把事情搞清楚,我只是不知道足够多的NHibernate来选择正确的路径。是的,映射单个实例应该相对容易。但是当一个DU包含另一个DU或其他什么的时候呢?我想我需要一些高级的复合用户类型,它是递归的,这样在所有情况下都能完全正确地映射。我还担心这在执行标准时是如何表现出来的。我也不确定我会如何处理这一点,但我怀疑您可以使用一个更深入的模式匹配结构,类似于我上面的示例。
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
<property name="IdOrName" type="MyNamespace.RequestInfoUserType, MyAssembly"  >
  <column name="Type"/>
  <column name="Id"/>
  <column name="Name"/>
</property>