为长文本字符串覆盖流畅的NHibernate nvarchar(MAX)不是nvarchar(255)

时间:2010-02-26 16:32:05

标签: c# nhibernate fluent-nhibernate automapping sharp-architecture

当您在流畅的NHibernate中设置字符串值时,它会将DB值设置为Nvarchar(255),我需要存储相当多的基于用户输入的长字符串,而255是不切实际的。

添加这个是自动播放器的一个问题,因为我使用流畅的NHibernate来构建数据库。

6 个答案:

答案 0 :(得分:33)

添加此约定会将字符串属性的默认长度设置为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);
    }
}

可以将惯例添加到这样的自动配置中:

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

有关详细信息,请参阅Fluent NHibernate wiki中的Conventions

答案 1 :(得分:17)

将长度设置为超过4001的任何值将生成NVarchar(MAX)...

.WithLengthOf(10000);

请点击此处了解更多详情...

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

答案 2 :(得分:6)

使用Fluent Nhibernate Automapper,可以快速意识到varchar列的开箱即用行为不太理想。首先,您发现每个字符串属性都导出为varchar(255),您需要将列设置为varchar(max)。但理想情况下,您不必将每个字符串都设为varchar(max),对吧?所以你要找到最好的方法来找到最好的方法来控制整个过程而不会打破各种优雅的模式...

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

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

如果你可以装饰你的财产,就像我在这里为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; }
    }
}

如果这可行,我们可以独立控制每个字符串,并且我们可以直接在我们的实体中指定它。

在我开始对数据库与应用程序分离的漩涡之前,让我指出这不是一个特别的数据库指令(我指出不调用属性'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);
        }
    }
}

将此约定添加到您的自动配置中,只要您想在ExportSchema期间获得特定长度,现在您只需装饰字符串属性 - 并且只能在您的实体中装饰该属性!

答案 3 :(得分:3)

我找到的一致方法是:

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

答案 4 :(得分:2)

您好我遇到了这个问题,同样的问题。我有一个稍微更安全的方式,因为我不希望所有字符串字段默认都有10000个字符。

首先,我使用一些覆盖来注册流利的nhibernate

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

My Mapping覆盖类如下所示:

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

只有具有长文本值的小型实体子集才需要这样做。也许其他人会发现这有用吗?

答案 5 :(得分:1)

也可能你正在使用“NHibernate validator”。如果是,Fluent NHibernate会自动考虑所有NHibernate验证器相关的数据注释,包括字符串长度,非空等等。