40

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

只是添加这是自动映射器的一个问题,因为我正在使用流利的 NHibernate 来构建数据库。

4

6 回答 6

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 中的约定

于 2010-02-26T16:47:55.980 回答
17

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

.WithLengthOf(10000);

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

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

于 2010-02-26T16:34:42.627 回答
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 期间产生特定长度时,现在您只需装饰字符串属性 - 并且只有该属性 - 在您的实体中具有权限!

于 2011-04-10T00:17:09.747 回答
3

One of the consistent way that I found is:

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

in which the VarcharMax and classes are

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; }
    }
}
于 2012-06-28T14:01:46.857 回答
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>
                ))

我的映射覆盖类如下所示:

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

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

于 2013-08-30T21:28:17.397 回答
1

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

于 2011-11-12T20:17:04.743 回答