No he sido lo suficientemente valiente como para intentar usar NHibernate con el sistema de tipos de F #, pero podría ayudar mirar desde la perspectiva de lo que realmente genera el compilador de F #.
Si mira su Unión discriminada en reflector, en realidad hay tres clases generadas (y más si cuenta los proxies de depuración privados).
public abstract class RequestInfo : IStructuralEquatable, IComparable, IStructuralComparable
La primera clase, RequestInfo, es abstracta, y en realidad es implementada por los otros tipos en la unión.
// 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);
}
así que cuando lo hace:
let r=Id(5)
let s=Name("bob")
r y s son ejemplos de _ID y _NAME, respectivamente.
Así que la respuesta a su pregunta es probable que la respuesta a una de las siguientes preguntas:
- ¿Cómo puedo asignar a una clase abstracta en nhibernate?
- ¿Cómo puedo hacer que NHibernate use un método de fábrica?
- ¿Cómo puedo crear un mapa de Nhibernate para objetos inmutables?
- ¿Cómo implemento un tipo personalizado en NHibernate (presumiblemente con IUserType)?
Desafortunadamente, no soy lo suficientemente inteligente para dar una respuesta coherente a ninguno de ellos, pero estoy seguro de que alguien más ha hecho al menos una de estas tres soluciones.
Me gustaría pensar que puede utilizar los mismos métodos utilizados para las estrategias de herencia, utilizando, por ejemplo, una columna discriminatoria, pero me temo que la falta de un constructor predeterminado lo hace problemático. Entonces, me inclino a pensar que usar un tipo personalizado es la solución.
Después de algún tocar el violín, he aquí una (posiblemente con errores y o roto) Tipo de usuario personalizada:
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
Este código de seguridad podría ser más genérico, y probablemente no debería estar en la capa de dominio real, pero pensé que sería útil apuñalar una implementación de F # de IUserType.
Su archivo de asignación sería entonces hacer algo como:
<property name="IdOrName" type="MyNamespace.RequestInfoUserType, MyAssembly" >
<column name="Type"/>
<column name="Id"/>
<column name="Name"/>
</property>
Es probable que pueda salir de allí sin una columna para el "Tipo", con una ligera inclinación al código UserType personalizado.
No sé cómo funcionan estos tipos de usuarios personalizados con consultas/ICriteria, ya que en realidad no he trabajado mucho con tipos de usuarios personalizados.
Pregunta muy interesante. Soy bastante fluido tanto en NHibernate como en F #, abordaré esto cuando tenga algo de tiempo –
Al menos en mi experiencia, tuve dificultades para usar NHibernate con F #, principalmente porque NHibernate requiere que los objetos tengan un constructor predeterminado (que es con frecuencia no es el caso para tipos de unión, tipos de registros y la mayoría de las clases), y se basa en la mutabilidad para asignar valores a las clases (lo cual es frecuentemente molesto de tratar en las definiciones de clase F #). La mayoría de las veces, obtengo la base de datos para jugar con F # lanzando mi propio mirco-ORM. – Juliet
Sí, en nuestro último proyecto simplemente nos apegamos a los tipos de objetos antiguos simples para que puedan jugar muy bien con NHibernate. Esta vez me gustaría diversificarme un poco y probar algunos modelos más realistas. – MichaelGG