¿Cómo utilizar el mapeo automático o mapeo en Fluent Nhibernate?
Nuestro proyecto utilizaba el archivo de configuración XML más tradicional para escribir relaciones de mapeo, pero era demasiado problemático y cada vez que se modificaba una clase o tabla, se debía modificar el archivo XML correspondiente, lo cual era fácil de salir.
Si hay un error, debe haber una omisión y no es fácil encontrar el error, por eso en el segundo proyecto utilizamos Fluent.
El método de mapeo de NHibernate reemplaza la configuración XML. La mayor ventaja de utilizar Fluent NHibernate es que reduce la posibilidad de errores, porque la configuración de Fluent
Nhibernate está escrita en C# y puede detectarse y compilarse de forma inteligente. A diferencia de la configuración XML sin formato, no se sabe si está mal.
Asignación de configuración de clase pública sellada:mapa de clase <Configuración>
{
Asignación de configuración pública()
{
Tabla("Configuración");
id(x = >x.Id," CONFIG_Id")generada por .hilo("100000000");
map( x = & gt;x.ConfigKey, " CONFIG _ KEY ");
map(x = & gt; >
}
}
Pero usar el método de configuración de Fluent
NHibernate todavía requiere escribir código de mapeo, lo que significa que si cambio la clase o DataTable, tengo que cambiarlo en consecuencia.
Archivo de mapeo. Más modificaciones significan más riesgos. Para reducir este riesgo y reducir la carga de trabajo de configuración, el último proyecto utiliza Fluent.
Automapping en NHibernate. Solo necesitamos definir las reglas de mapeo, por lo que no podemos escribir la configuración de mapeo para cada tabla y clase por separado, sino hacerlo automáticamente de acuerdo con las reglas.
Trabajos de topografía y cartografía. De esta forma, al modificar una clase o tabla de datos, solo necesita modificar la clase y la tabla, y no es necesario modificar el archivo de configuración.
Para lograr el mapeo automático, debe definir especificaciones de nomenclatura estrictas y luego escribir reglas de mapeo automático de acuerdo con las especificaciones para lograr el mapeo automático. Por ejemplo, podemos definir las siguientes reglas:
La primera letra de cada palabra en los nombres de clases y campos está en mayúscula, mientras que los nombres de las tablas de la base de datos y los nombres de las columnas están todos en mayúscula, con guiones bajos entre las palabras. (Por ejemplo, la clase CostCenter corresponde a la tabla COST_CENTER)
La clave principal de la clase lleva el nombre de Id, y la clave principal de la tabla lleva el nombre del nombre de la tabla + "_ ID". (Por ejemplo, hay un ID largo virtual público {get settings;}, correspondiente a la columna COST_CENTER_ID en la tabla)
Para una relación de uno a muchos, el nombre de clase de la tabla principal es se utiliza como nombre de atributo y la clave principal de la tabla principal. El nombre de la columna se utiliza como nombre de columna para la columna de clave externa correspondiente en la tabla. (Por ejemplo, una clase corresponde a varios estudiantes y hay ellos en la clase.
ILista virtual pública & ltStudents& gt
Estudiantes {get settings;}, y en el Student clase, Clase debe usarse como nombre de atributo: clase virtual pública.
Clase { get settings })
Para las subclases, se almacenan varios subobjetos en la misma tabla. utilizando la columna "TIPO" como columna discriminante, que se implementa utilizando un nombre de clase como este como identificador único de la subclase.
Para relaciones de muchos a muchos, ordene los nombres de las tablas correspondientes a las dos clases, coloque las más pequeñas primero y luego conecte los dos nombres de las tablas con "_" en el medio. (Por ejemplo, los cursos y los estudiantes tienen una relación de muchos a muchos, por lo que la tabla intermedia generada se denomina CURSO_ESTUDIANTE).
Para las enumeraciones, se utiliza tinyint para almacenarlas en la base de datos y la enumeración se trata como un tipo de usuario en el mapeo automático.
Escribamos las reglas de conversión para el mapeo automático. Primero escriba un método de extensión para String para convertir de CostCenter a COST_CENTER:
Cadena estática ToDatabaseName (esta cadena s)
{
Devuelve expresión regular. Reemplazar(s, @"\B[A-Z]", match = & gt "_" + match.ToString()). ToUpper();
}
Para 1, debe implementar IClassConvention de la siguiente manera:
Convención de nombre de clase de clase pública: convención de iclass
{
Aplicación de anulación virtual pública (instancia IClassInstance)
{
var tableName = instancia. tipo de entidad. nombre de toda la base de datos();
Instancia. Table(tableName);
}
}
Al mismo tiempo, para las columnas, debe utilizar la interfaz IPropertyConvention, que se implementa de la siguiente manera:
Convención de atributo de clase pública: IPropertyConvention
{
Aplicación de anulación pública (instancia de IPropertyInstance)
{
Instancia. Column(instance. name . toda Databasename());
}
}
Para 2, necesitamos implementar la interfaz IIdConvention, usamos la principal. clave del valor de Hilo El método de generación utiliza una tabla HIBERNATE_UNIQUE_KEY para almacenar las canalizaciones de cada tabla. La implementación específica es la siguiente:
Convención de clave primaria de clase pública: convención iid
{
Cadena constante pública NextHiValueColumnName = " VALUE
cadena const pública NHibernateHiLoIdentityTableName = " HIBERNATE _ UNIQUE _ KEY ";
cadena const pública NOMBRE de la columna TABLE = " NOMBRE _ TABLA "
Aplicación de vacío virtual público (instancia IIdentityInstance)<; /p >
{
var nombre de tabla = instancia. nombre de entidad. toda nombre de base de datos(); aquí El nombre de la clave principal es el nombre de la tabla + "_ ID "
If (instance. type == type of (long)) //A continuación, configure el método de generación de clave principal en el método de valor HiLo
{
Instancia.GeneratedBy.HiLo(
NHibernateHiLoIdentityTableName,
NextHiValueColumnName,
"1000000000",
constructor = & gtbuilder. AddParam("dónde", cadena.
Formato("{0} = '{1} '", NombreColumnaTabla, NombreTabla))
}
}
}
<); p>Para el caso de 3, uno a muchos, debe configurar la colección de una parte y las referencias de varias partes, de la siguiente manera:Convención de colección de clase pública: ICollectionConvention
{
Aplicación de anulación pública (instancia de ICollectionInstance)
{
String colName
varentityType = instancia. EntityType
var childType = instancia. ChildType
If (entityType == childType) // Aquí, la asociación uno a muchos se maneja especialmente y PARENT_ID se usa uniformemente como columna de clave externa.
colName = " PARENT _ ID
Otro
{
colName = entidadType.name . toda nombre de base de datos()+" _ ID ";
}
Instancia.clave. Columna(colName);
Instancia.cascade.AllDeleteOrphan();
}
}
Convención de referencia de clase pública: IReferenceConvention
{
public void Apply(instancia imanytone)
{
cadena colName = null
var tipo de referencia = instancia GetUnderlyingSystemType();
var tipo de entidad = instancia.
var nombre de propiedad = instancia. propiedad.
//Autoasociación
if (tipo de referencia == tipo de entidad)
colName = " PADRE _ ID
Otro
colName = nombrePropiedad. toda nombre de base de datos()+" _ ID ";
Ejemplo. Column (colName);
}
}
Para el procesamiento de 4 subclases, debe especificar la clase a distinguir y la columna de distinción, y luego especifique cómo distinguir las subclases de mapas en columnas.
Aquí debe reescribir la clase DefaultAutoMappingConfiguration y especificar la clave principal, la clase distinguible, etc. En esta clase, el código específico es el siguiente:
Configuración de automap de clase pública:DefaultAutomappingConfiguration
{
Anulación pública bool ShouldMap(Type Type)
{
Return (type. IsClass & amp& amp type. namespace . comienza con (" nuestro proyecto. core . Model ") //Especifique la clase que se asignará automáticamente.
}
Anulación pública IsId booleano (miembro miembro)
{
Devuelve miembro. Nombre == "Id//Especifique que el atributo Id en cada clase es la clave principal de la clase.
}
La anulación pública de bool se discrimina (tipo tipo)//Especifique qué clases requieren herencia de subclases y almacene todas sus subclases en una tabla.
{
Tipo de devolución. en (
Tipo (Cliente),
Tipo (Categoría),
Tipo (Multicategoría)
); p> p>
}
Cadena de anulación pública GetDiscriminatorColumn(TypeType)
{
Devuelve "tipo" //Especifica la distinción de subclases Columna; significa que hay una columna llamada TIPO.
}
}
Luego se trata de cómo los valores en DiscriminateColumn se asignan a las subclases correspondientes. Es necesario implementar la interfaz ISubclassConvention, el código es el siguiente:
Convención de subclase de clase pública:isubclassconventi on
{
Aplicación nula pública (instancia ISubclassInstance )
{
Instancia. DiscriminatorValue(instancia. tipo de entidad. Nombre);
}
}
Para 5 muchos a muchos, debe implementar la interfaz IHasManyToManyConvention, donde Se ordenan dos nombres de tablas y luego se unen para representar tablas intermedias. El código específico es el siguiente:
La clase pública tiene manytomanyconvention:ihasmantomynotomayconvention
{
public void Apply(instancia IManyToManyCollectionInstance)
{
var entidadDatabaseName = instancia. tipo de entidad. nombre de toda la base de datos();
var childDatabaseName = instancia. tipo secundario. nombre de la base de datos toda();
var nombre = GetTableName(entityDatabaseName, nombre de la base de datos secundaria); // Ordena dos nombres de tablas y luego los une para formar un nombre de tabla intermedia.
Ejemplo. Tabla(nombre);
Instancia. clave .Columna(entityDatabaseName+" _ ID ");
Instancia. relación . Columna (nombre de la base de datos secundaria+" _ ID ");
}
Cadena privada GetTableName(cadena a, cadena b)
{ p>
var r = sistema. String.CompareOrdinal(a, b);
if(r & gt; 0)
{
Devuelve "{0}_{1}". Fill(b,a);
}
Otro
{
Devuelve "{0}_{1}". Fill(a,b);
}
}
}
Para el procesamiento de 6 enumeraciones, la enumeración debe ser Especifíquelo como UserType e implemente la interfaz IUserTypeConvention.
El código específico es el siguiente:
Convención de enumeración de clase pública: IUserTypeConvention
{
aceptación pública vacía (IAcceptanceCriteria & lt; IPropertyInspector & gt estándar) p>
{
Estándar. expect(x = & gt; x . propiedad . tipo de propiedad . isenum);
}
Aplicación de anulación pública (instancia IPropertyInstance)
{
Instancia. CustomType(instance.property.propertytype);
}
}
Al implementar la interfaz anterior, se puede lograr el mapeo automático en la mayoría de los casos. Finalmente, notifique a FluentNhibernate sobre estas interfaces y permítale usarlas para importar el DomainModel en el ensamblado especificado. El método de implementación específico es el siguiente:
AutoPersistenceModel virtual público Generate(string[]domain assemblies, string[] dalAssemblies)
{
var mapeo = automático cartografía. Component(
new AutoMapConfiguration(), DomainAssembly.Select(Assembly.LoadFrom).ToArray());
foreach(var ignoreBaseType en IgnoredBaseTypes)
{
Mapa. ignore base(ignoredBaseType);
}
foreach(var includeBaseType en incluir tipos base)
{
Map. incluir base(incluir tipo base);
}
Mapeo. convenciones.Setup(get Conventions()); //Especifique la implementación de la interfaz de conversión de mapeo automática.
foreach(var dalAssembly en dal assemblies)
{
Mapa. UseOverridesFromAssembly(assembly.LoadFrom(dal ensamblado));
}
Devolver mapeo;
}
IList público estático & ltType & gtIgnoredBaseTypes = nueva lista & ltType & gt//Las clases enumeradas aquí son clases base y no se asignarán a una tabla específica.
{
Typeof (Entidad) // Este objeto en realidad solo tiene el atributo Id, que es la clase principal de todas las clases que se asignarán.
};
IList estática pública & ltType & gtIncludeBaseTypes = new List & ltType & gt// De forma predeterminada, las clases abstractas no se asignan a tablas, por lo que es necesario especificar estas clases asignaciones a la tabla.
{
Tipo (categoría),
Tipo (varias categorías),
Tipo (cliente)
};
Operación protegida & lticonteventionfinder & gt; GetConventions()
{
return finder = & gt
{ p>
Descubridor.
Agregue <ClassNameConvention>();
buscador. Agregue <PrimaryKeyConvention>();
buscador. Agregue <CollectionConvention>();
Finder. Agregue & ltReferenceConvention & gt();
buscador. Agregue & ltHasManyConvention & gt();
finder. Agregue & ltSubClassNameConvention & gt();
Finder. Agregue & ltSubclassConvention & gt();
finder. Agregue & ltPropertyConvention & gt();
finder. Agregue <EnumConvention>();
buscador. Agregue & ltHasManyToManyConvention & gt();
};
}
Este método devuelve un AutoPersistenceModel que se puede usar para registrarse en NHibernate.