c# nhibernate fluent-nhibernate automapping sharp-architecture

c# - Anular el NHibernate con fluidez para cadenas de texto largas nvarchar(MAX) no nvarchar(255)



fluent-nhibernate automapping (6)

Cuando estableces un valor de cadena en NHibernate con fluidez, siempre establece los valores de DB en Nvarchar (255), necesito almacenar una gran cantidad de cadenas largas basadas en las entradas del usuario y 255 no es práctico.

Solo para agregar, este es un problema con el Automapper ya que estoy usando NHibernate con fluidez para construir la base de datos.



Con Fluent Nhibernate Automapper, uno se da cuenta rápidamente de que el comportamiento fuera de la caja para columnas varchar es menos que ideal. Primero descubre que cada propiedad de cadena se exportó como varchar (255) y necesita hacer que una columna sea varchar (max). Pero idealmente, no tendrías que hacer que cada cadena sea varchar (max), ¿verdad? De modo que puede seguir ese camino bien recorrido para encontrar la mejor manera de ejercer control sobre el proceso sin romper los diversos patrones elegantes en juego ...

Si desea que las columnas varchar de la base de datos resultantes se especifiquen en diferentes longitudes, busque en las clases de convenciones para que suceda. Puede tratar de crear condiciones específicas de nombre o, en general, usar algún patrón de denominación que haya detectado dentro de su clase de convención.

Ninguno de los dos es ideal. Sobrecargar un nombre con el propósito de indicar una especificación prevista en otra parte del código es desafortunado; su nombre debería ser simplemente un nombre. Tampoco debe modificar el código de convención cada vez que necesite agregar o modificar una propiedad de clase de longitud limitada. Entonces, ¿cómo puedes escribir una clase de convención que te dé el control y te brinde ese control de una manera simple y elegante?

Sería bueno si pudieras decorar tu propiedad como lo hice con la propiedad Body aquí:

using System; using MyDomain.DBDecorations; namespace MyDomain.Entities { [Serializable] public class Message { public virtual string MessageId { get; set; } [StringLength(4000)] public virtual string Body { get; set; } } }

Si esto pudiera funcionar, tendríamos el control sobre cada cadena de manera independiente, y podríamos especificarla directamente en nuestra entidad.

Antes de comenzar una vorágine sobre la separación de la base de datos de la aplicación, permítanme señalar que esta no es específicamente una directiva de base de datos (me referí a no llamar al atributo ''Varchar''). Prefiero caracterizar esto como un aumento de System.string, y en mi pequeño universo estoy contento con eso. En resumen, quiero una conveniencia!

Para hacer esto, necesitamos definir la decoración que queremos usar:

using System; namespace MyDomain.DBDecorations { [AttributeUsage(AttributeTargets.Property)] public class StringLength : System.Attribute { public int Length = 0; public StringLength(int taggedStrLength) { Length = taggedStrLength; } } }

Finalmente, necesitamos usar una convención de longitud de cadena para usar la decoración de propiedad de la entidad. Esta parte puede no parecer bonita, pero cumple su función, ¡y la buena noticia es que no tendrá que volver a mirarla!

StringColumnLengthConvention.cs:

using System.Reflection; using FluentNHibernate.Conventions; using FluentNHibernate.Conventions.AcceptanceCriteria; using FluentNHibernate.Conventions.Inspections; using FluentNHibernate.Conventions.Instances; namespace MyMappings { public class StringColumnLengthConvention : IPropertyConvention, IPropertyConventionAcceptance { public void Accept(IAcceptanceCriteria<IPropertyInspector> criteria) { criteria.Expect(x => x.Type == typeof(string)).Expect(x => x.Length == 0); } public void Apply(IPropertyInstance instance) { int leng = 255; MemberInfo[] myMemberInfos = ((PropertyInstance)(instance)).EntityType.GetMember(instance.Name); if (myMemberInfos.Length > 0) { object[] myCustomAttrs = myMemberInfos[0].GetCustomAttributes(false); if (myCustomAttrs.Length > 0) { if (myCustomAttrs[0] is MyDomain.DBDecorations.StringLength) { leng = ((MyDomain.DBDecorations.StringLength)(myCustomAttrs[0])).Length; } } } instance.Length(leng); } } }

Agregue esta convención a su configuración de automatización y allí la tiene; cuando quiera que se obtenga una longitud específica durante ExportSchema, ahora puede simplemente decorar la propiedad de cadena -y solo esa propiedad- directamente en su entidad.


Probablemente también esté utilizando el " validador NHibernate ". En caso afirmativo, Fluent NHibernate considerará todas las anotaciones de datos relacionadas con el validador NHibernate de forma automática, incluida la longitud de la cadena, no nula, etc.


Una de las maneras consistentes que encontré es:

Map(x => x.LongText, "LongText").CustomType<VarcharMax>().Nullable();

en el que VarcharMax y las clases son

public class VarcharMax : BaseImmutableUserType<String> { public override object NullSafeGet(IDataReader rs, string[] names, object owner) { return (string)NHibernateUtil.String.NullSafeGet(rs, names[0]); } public override void NullSafeSet(IDbCommand cmd, object value, int index) { //Change the size of the parameter ((IDbDataParameter)cmd.Parameters[index]).Size = int.MaxValue; NHibernateUtil.String.NullSafeSet(cmd, value, index); } public override SqlType[] SqlTypes { get { return new[] { new SqlType(DbType.String) }; } } } public abstract class BaseImmutableUserType<T> : NHibernate.UserTypes.IUserType { public abstract object NullSafeGet(IDataReader rs, string[] names, object owner); public abstract void NullSafeSet(IDbCommand cmd, object value, int index); public abstract SqlType[] SqlTypes { get; } public new bool Equals(object x, object y) { if (ReferenceEquals(x, y)) { return true; } if (x == null || y == null) { return false; } return x.Equals(y); } public int GetHashCode(object x) { return x.GetHashCode(); } public object DeepCopy(object value) { return value; } public object Replace(object original, object target, object owner) { return original; } public object Assemble(object cached, object owner) { return DeepCopy(cached); } public object Disassemble(object value) { return DeepCopy(value); } public Type ReturnedType { get { return typeof(T); } } public bool IsMutable { get { return false; } } }


Agregar esta convención establecerá la longitud predeterminada para las propiedades de cadena en 10000. Como otros han notado, esta será una columna nvarchar (max).

public class StringColumnLengthConvention : IPropertyConvention, IPropertyConventionAcceptance { public void Accept(IAcceptanceCriteria<IPropertyInspector> criteria) { criteria.Expect(x => x.Type == typeof(string)).Expect(x => x.Length == 0); } public void Apply(IPropertyInstance instance) { instance.Length(10000); } }

Las convenciones se pueden agregar a una configuración de automap como esta:

Fluently.Configure() .Mappings( m => m.AutoMappings.Add( AutoMap.AssemblyOf<Foo>() .Conventions.Add<StringColumnLengthConvention >()))

Para obtener más información, consulte Convenciones en la wiki fluida de NHibernate.


Hola, me encontré con esta pregunta, con el mismo problema. Tengo una forma un poco más segura de hacerlo ya que no quiero que todos los campos de cadena tengan 10000 caracteres por defecto.

Primero, registro el nhibernate con fluidez con algunas anulaciones

...//snip ....Mappings(m => m.AutoMappings.Add( AutoMap.AssemblyOf<Account>() //Use my mapping overrides here .UseOverridesFromAssemblyOf<MyMappingOverride>() .Conventions.Add(new MyConventions()).IgnoreBase<Entity> ))

Mi clase de anulación de asignación tiene este aspecto:

public class MyMappingOverride : IAutoMappingOverride<MyClass> { public void Override(AutoMapping<MyClass> mapping) { mapping.Map(x => x.LongName).Length(765); } }

Esto solo se requiere para subconjuntos pequeños de entidades con valores de texto largos. ¿Tal vez algún otro lo encuentre útil?