C# 长文本字符串nvarchar(最大值)而非nvarchar(255)的fluent NHibernate重写

C# 长文本字符串nvarchar(最大值)而非nvarchar(255)的fluent NHibernate重写,c#,nhibernate,fluent-nhibernate,automapping,sharp-architecture,C#,Nhibernate,Fluent Nhibernate,Automapping,Sharp Architecture,当您在fluent NHibernate中设置字符串值时,它总是将DB vales设置为Nvarchar(255),我需要存储大量基于用户输入的长字符串,255是不切实际的 这是automapper的一个问题,因为我正在使用fluent NHibernate构建数据库 将长度设置为任何超过4001的值都将生成NVarchar(最大值) 请参阅此处了解更多详细信息 添加此约定将字符串属性的默认长度设置为10000。正如其他人所指出的,这将是一个nvarchar(max)列 公共类StringCol

当您在fluent NHibernate中设置字符串值时,它总是将DB vales设置为Nvarchar(255),我需要存储大量基于用户输入的长字符串,255是不切实际的


这是automapper的一个问题,因为我正在使用fluent NHibernate构建数据库

将长度设置为任何超过4001的值都将生成NVarchar(最大值)

请参阅此处了解更多详细信息


添加此约定将字符串属性的默认长度设置为10000。正如其他人所指出的,这将是一个nvarchar(max)列

公共类StringColumnLength约定:IPropertyConvention、IPropertyConvention接受
{
公共无效接受(IAcceptanceCriteria标准)
{
Expect(x=>x.Type==typeof(string)).Expect(x=>x.Length==0);
}
公共无效应用(IPropertyInstance实例)
{
例如:长度(10000);
}
}
可以将约定添加到自动映射配置中,如下所示:

Fluently.Configure()
    .Mappings( m =>
        m.AutoMappings.Add( AutoMap.AssemblyOf<Foo>()
        .Conventions.Add<StringColumnLengthConvention >()))
public class MyMappingOverride : IAutoMappingOverride<MyClass> {
       public void Override(AutoMapping<MyClass> mapping) {
           mapping.Map(x => x.LongName).Length(765);
       }
}
流利。配置()
.Mappings(m=>
m、 AutoMappings.Add(AutoMap.AssemblyOf())
.Conventions.Add())

有关更多信息,请参见Fluent NHibernate wiki。

使用Fluent NHibernate Automapper,您很快就会意识到varchar列的开箱即用行为并不理想。首先,您发现每个字符串属性都被导出为varchar(255),您需要将一个列设置为varchar(max)。但理想情况下,您不必将每个字符串都设置为varchar(max),对吗?因此,你走上了一条成功的道路,找到了在不打破各种优雅模式的情况下控制过程的最佳方法

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

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

如果你能像我一样装饰你的房子,那就太好了:

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; }
    }
}
如果可以,我们可以独立控制每个字符串,并且可以直接在实体中指定它。

在我开始讨论数据库和应用程序的分离之前,请允许我指出,这并不是一个具体的数据库指令(我强调不要调用属性“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;
        }
    }
}
最后,我们需要使用字符串长度约定来使用实体的属性装饰。这部分看起来可能不漂亮,但它确实起作用了,好消息是你不必再看它了

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);
        }
    }
}
使用系统反射;
使用FluentNHibernate.约定;
使用FluentNHibernate.Conventions.Acceptance标准;
使用FluentNHibernate.Conventions.Inspection;
使用FluentNHibernate.Conventions.Instances;
命名空间映射
{
公共类StringColumnLength约定:IPropertyConvention、IPropertyConvention接受
{
public void Accept(IAcceptanceCriteria criteria criteria){criteria.Expect(x=>x.Type==typeof(string)).Expect(x=>x.Length==0);}
公共无效应用(IPropertyInstance实例)
{
int leng=255;
MemberInfo[]myMemberInfos=((PropertyInstance)(instance)).EntityType.GetMember(instance.Name);
如果(myMemberInfos.Length>0)
{
对象[]myCustomAttrs=myMemberInfos[0]。GetCustomAttributes(false);
如果(myCustomAttrs.Length>0)
{
如果(myCustomAttrs[0]是MyDomain.DbDections.StringLength)
{
leng=((MyDomain.dbdecordentials.StringLength)(myCustomAttrs[0])).Length;
}
}
}
实例.长度(leng);
}
}
}

将此约定添加到自动映射配置中,您就有了它—无论何时您希望在ExportSchema期间产生特定的长度,现在您只需在实体中修饰string属性—并且只修饰该属性—权限

您可能也在使用“”。如果是的话,流畅的NHiBiNATE会自动考虑所有的NHiBurnValter相关的数据注释,包括字符串长度,而不是NULL等等。

< P> >我发现的一个一致的方式是:

Map(x => x.LongText, "LongText").CustomType<VarcharMax>().Nullable();
Map(x=>x.LongText,“LongText”).CustomType().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; }
    }
}
公共类VarcharMax:BaseImmutableUserType
{
公共重写对象NullSafeGet(IDataReader rs,字符串[]名称,对象所有者)
{
return(string)NHibernateUtil.string.NullSafeGet(rs,names[0]);
}
public override void NullSafeSet(IDbCommand cmd,对象值,int索引)
{
//更改参数的大小
((IDbDataParameter)cmd.Parameters[index]).Size=int.MaxValue;
NHibernateUtil.String.NullSafeSet(cmd,value,index);
}
公共重写SqlType[]SqlTypes
{
获取{returnnew[]{newsqltype(DbType.String)};}
}
}
公共抽象类BaseImmutableUserType:NHibernate.UserTypes.IUserType
{
公共抽象对象NullSafeGet(IDataReader rs,字符串[]名称,对象所有者
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; }
    }
}
...//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);
       }
}