Domanda

Mi chiedo se esiste un modo relativamente semplice di estendere NHibernate per supportare l'unione discriminata di F #. Non solo un singolo IUserType o ICompositeUserType, ma qualcosa di generico che posso riutilizzare indipendentemente dal contenuto effettivo del DU.

Ad esempio, supponiamo che io abbia una proprietà chiamata RequestInfo, che è un'unione definita come:

type RequestInfo =  
    | Id of int
    | Name of string

Questo viene compilato in una classe RequestInfo astratta, con sottoclassi concrete Id e Name. Posso ottenere tutte queste informazioni bene con la riflessione F #. In questo caso, potrei memorizzarlo nel database con " RequestInfo_Tag " ;, " RequestInfo_Id " ;, " RequestInfo_Name " ;.

Dato che sono un principiante di NHibernate, che tipo di problemi incontrerò cercando di seguire questo approccio? I casi più complessi saranno impossibili da affrontare? Ad esempio, che dire dei sindacati discriminati nidificati? Esiste un modo in cui posso " distribuire " la lettura del resto del sindacato su un altro ICompositeUserType?

Ancora più importante, questo rovinerà le mie capacità di interrogazione? Significato, dovrò conoscere i nomi delle colonne effettive nel DB; Non sarò in grado di fare Criteria.Eq (SomeDiscUnion) e tutto risolto?

Non sto cercando un codice di " fornire codice " rispondo, solo qualche consiglio generale se vale la pena andare dopo (e alcuni suggerimenti su come), o se dovrei semplicemente ripensare il mio modello.

Grazie!

P.S. Non essere scortese, ma se la tua risposta consiste in "usa C #", non è molto utile.

È stato utile?

Soluzione

Non sono stato abbastanza coraggioso da provare a usare NHibernate con il sistema di tipi di F #, ma potrebbe aiutare a guardare dal punto di vista di ciò che è effettivamente generato dal compilatore F #.

Se guardi la tua Unione Discriminata in riflettore, in realtà ci sono tre classi generate (e più se conti i proxy di debug privati).

public abstract class RequestInfo : IStructuralEquatable, IComparable, IStructuralComparable

La prima classe, RequestInfo, è astratta ed è effettivamente implementata dagli altri tipi nell'unione.

 // 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);
    }

quindi quando lo fai:

 let r=Id(5)
 let s=Name("bob")

r e s sono istanze di _Id e _Name, rispettivamente.

Quindi la risposta alla tua domanda è probabilmente la risposta a una delle seguenti domande:

  • Come posso mappare a una classe astratta in nhibernate?
  • Come posso fare in modo che NHibernate usi un metodo factory?
  • Come posso creare una mappa Nhibernate su oggetti immutabili?
  • Come posso implementare un tipo personalizzato in NHibernate (presumibilmente con IUserType).

Sfortunatamente, non sono abbastanza esperto da darti una risposta coerente a nessuno di questi, ma sono sicuro che qualcun altro qui ha fatto almeno una di queste tre soluzioni.

Mi piacerebbe pensare che puoi usare gli stessi metodi usati per le strategie di ereditarietà, usando, ad esempio, una colonna discriminante, ma temo che la mancanza di un costruttore predefinito lo renda problematico. Quindi sono propenso a pensare che l'uso di un tipo personalizzato sia la soluzione.

Dopo un po 'di armeggi, ecco un tipo di utente personalizzato (possibilmente difettoso o rotto):

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

Questo codice potrebbe sicuramente essere reso più generico e probabilmente non dovrebbe trovarsi nel tuo attuale livello di dominio, ma ho pensato che sarebbe stato utile prendere una piega in un'implementazione F # di IUserType.

Il tuo file di mappatura farebbe quindi qualcosa del tipo:

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

Probabilmente puoi andartene senza una colonna per " Digita " con una leggera modifica al codice UserType personalizzato.

Non so come funzionano questi tipi di utenti personalizzati con le query / ICriteria, dato che non ho mai lavorato con tipi di utenti personalizzati molto prima.

Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top