Дискриминационные союзы в NHibernate

Мне интересно, есть ли относительно простой способ расширить NHibernate для поддержки F # дискриминационного объединения. Не только один IUserType или ICompositeUserType, но и нечто общее, что я могу повторно использовать независимо от фактического содержимого DU.

Например, предположим, что у меня есть свойство RequestInfo, которое является объединением, определенным как:

type RequestInfo =  
    | Id of int
    | Name of string

Скомпилируется в абстрактный класс RequestInfo, с конкретными подклассами Id и Name. Я могу получить всю эту информацию просто отлично с отражением F #. В этом случае я мог бы сохранить его в базе данных с помощью "RequestInfo_Tag", "RequestInfo_Id", "RequestInfo_Name".

Как я новичок в NHibernate, какие проблемы я буду пытаться использовать для этого подхода? С более сложными делами будет невозможно справиться? Например, как насчет вложенных дискриминационных союзов? Есть ли способ "отменить" чтение остальной части соединения на другой ICompositeUserType?

Что еще более важно, это испортит мои запросы? Смысл, мне нужно знать фактические имена столбцов в БД; Я не смогу выполнить Criteria.Eq(SomeDiscUnion) и все это разобрался?

Я не ищу полный ответ "предоставить код", просто некоторые общие советы, если это стоит того, что стоит (и некоторые указатели на то, как), или если я просто переосмыслил свою модель.

Спасибо!

P.S. Не грубить, но если ваш ответ состоит из "use С#", это не очень полезно.

Ответы

Ответ 1

Я не был достаточно храбр, чтобы попробовать использовать NHibernate с системой типа F #, но это может помочь взглянуть с точки зрения того, что на самом деле сгенерировано компилятором F #.

Если вы посмотрите на свой Дискриминационный союз в отражателе, на самом деле есть три генерируемых класса (и больше, если вы считаете частные прокси-серверы отладки).

public abstract class RequestInfo : IStructuralEquatable, IComparable, IStructuralComparable

Первый класс, RequestInfo, является абстрактным и фактически реализуется другими типами в объединении.

 // Nested Types
    [Serializable, DebuggerTypeProxy(typeof([email protected])), 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([email protected])), 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 использовать метод factory?
  • Как создать карту Nhibernate для неизменяемых объектов?
  • Как реализовать собственный тип в NHibernate (предположительно с IUserType).

К сожалению, я недостаточно разбираюсь, чтобы дать вам последовательный ответ на любой из этих вопросов, но я уверен, что кто-то еще здесь сделал хотя бы одно из этих трех решений.

Мне бы хотелось подумать, что вы можете использовать те же методы, которые использовались для стратегий наследования, используя, например, столбец дискриминатора, но я боюсь, что отсутствие конструктора по умолчанию делает это проблематичным. Поэтому я склонен думать, что использование настраиваемого типа - это решение.

После некоторого ворчания, здесь (возможно, багги и/или сломанный) пользовательский тип пользователя:

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

Этот код, безусловно, может быть более общим и, вероятно, не должен быть в вашем реальном доменном слое, но я подумал, что было бы полезно взять удар в реализации FU IUserType.

Затем ваш файл сопоставления сделает следующее:

<property name="IdOrName" type="MyNamespace.RequestInfoUserType, MyAssembly"  >
  <column name="Type"/>
  <column name="Id"/>
  <column name="Name"/>
</property>

Вероятно, вы можете уйти без столбца для "Тип" с небольшой настройкой на пользовательский код UserType.

Я не знаю, как эти пользовательские типы пользователей работают с запросами /ICriteria, поскольку я до сих пор не работал с пользовательскими типами пользователей.