为长文本string重写stream畅的NHibernate nvarchar(MAX)不是nvarchar(255)

当你在stream利的NHibernate中设置一个string值时,它将数据库值设置为Nvarchar(255),我需要存储相当多的基于用户input的长string,并且255是不切实际的。

只是添加这是automapper问题,因为我使用stream利的NHibernate来build立数据库。

添加这个约定会将string属性的默认长度设置为10000.正如其他人所指出的那样,这将是一个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); } } 

约定可以添加到像这样的自动映射configuration:

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

有关更多信息,请参阅Fluent NHibernate wiki中的约定 。

设置长度超过4001将产生一个NVarchar(MAX)…

 .WithLengthOf(10000); 

在这里看到更多的细节…

http://serialseb.blogspot.com/2009/01/fluent-nhibernate-and-nvarcharmax.html

使用Fluent Nhibernate Automapper,人们很快意识到varchar列的开箱即用行为并不理想。 首先,您会发现每个string属性都以varchar(255)forms导出,您需要将某个列设置为varchar(max)。 但理想情况下,你不必使每个string都是varchar(max),对吗? 所以,你要find最好的方式来控制整个过程,而不是在游戏中突破各种优雅的模式。

如果您希望以不同的长度指定您的结果数据库varchar列,则可以使用convention类来实现它。 您可以尝试创build名称特定的条件,或者通常使用您在会议类中检测到的一些命名模式。

两者都不是理想的。 为了在代码的另一部分中指明预期的规范而重载一个名字是不幸的 – 你的名字应该只是一个名字。 每次需要添加或修改有限长度的类属性时,也不应该修改惯例代码。 那么如何编写一个可以控制的公约类,并以简单而优雅的方式提供控制呢?

如果你可以像我这里的Body属性那样装饰你的财产,这将是甜蜜的:

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

如果这可以工作,我们将独立控制每个string,我们可以直接在我们的实体中指定它。

在我从一个应用程序的数据库分离开始创build一个漩涡之前,让我指出这不是一个特定的数据库指令(我没有调用属性“Varchar”)。 我更喜欢将其描述为System.string的扩展,在我自己的小宇宙中,我对此感到满意。 底线,我想要一个方便!

要做到这一点,我们需要定义我们想要使用的装饰:

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

最后,我们需要使用string长度约定来使用实体的属性装饰。 这个部分看起来可能不太好,但这样做是好的,好消息是你不必再看一眼!

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

把这个惯例添加到你的自动映射configuration中,并且你有它,只要你想在ExportSchema期间产生一个特定的长度,现在你可以装饰string属性 – 只有那个属性 – 在你的实体中!

我发现的一个一致的方法是:

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

其中VarcharMax和类是

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

嗨,我遇到了这个问题,同样的问题。 我有一个更安全的做法,因为我不希望所有string字段默认情况下都有10000个字符。

首先我注册stream利的nhibernate与一些覆盖

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

我的映射覆盖类看起来像这样:

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

这只对具有长文本值的实体的小子集是必需的。 也许其他人会觉得这有用吗?

也许你也在使用“ NHibernatevalidation器 ”。 如果是的话,Fluent NHibernate会自动考虑所有与NHibernatevalidation器相关的数据注解,包括string长度,非空等。

Interesting Posts