简体   繁体   English

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

[英]Override for fluent NHibernate for long text strings nvarchar(MAX) not nvarchar(255)

When ever you set a string value in fluent NHibernate it alwasy sets the DB vales to Nvarchar(255), I need to store quite a lot of long string which are based on user inputs and 255 is impractical.当你在 fluent NHibernate 中设置一个字符串值时,它总是将 DB 值设置为 Nvarchar(255),我需要存储相当多的基于用户输入的长字符串,而 255 是不切实际的。

Just to add this is an issue with the automapper as I am using fluent NHibernate to build the database.只是补充一下,这是自动映射器的一个问题,因为我正在使用流利的 NHibernate 来构建数据库。

Adding this convention will set the default length for string properties to 10000. As others have noted, this will be a nvarchar(max) column. 添加此约定会将字符串属性的默认长度设置为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);
    }
}

Conventions can be added to an automap configuration like this: 可以将约定添加到自动配置中,如下所示:

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

For more information, see Conventions in the Fluent NHibernate wiki. 有关更多信息,请参阅Fluent NHibernate wiki中的约定

Setting the length to anything over 4001 will generate an NVarchar(MAX)... 将长度设置为4001以上将生成NVarchar(MAX)...

.WithLengthOf(10000);

See here for more detail... 有关详细信息,请参见此处

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

With Fluent Nhibernate Automapper, one quickly realizes that the out-of-the-box behavior for varchar columns is less than ideal. 使用Fluent Nhibernate Automapper,可以快速意识到varchar列的开箱即用行为不太理想。 First you discover that every string property was exported as varchar(255) and you need to make a column to be varchar(max). 首先,您发现每个字符串属性都导出为varchar(255),您需要将列设置为varchar(max)。 But ideally, you wouldn't have to make every string a varchar(max), right? 但理想情况下,您不必将每个字符串都设为varchar(max),对吧? So you head down that well-trodden path of finding the best way to exert control over the process without breaking out of the various elegant patterns at play... 所以,你要找到最好的方法来找到最好的方法来控制整个过程而不会打破各种优雅的模式......

If you want to have your resulting database varchar columns specified at different lengths, you look to convention classes to make it happen. 如果要以不同的长度指定生成的数据库varchar列,可以查看约定类以实现它。 You might try create name-specific conditions or generally use some naming pattern that you detected inside your convention class. 您可以尝试创建特定于名称的条件,或者通常使用在约定类中检测到的某些命名模式。

Neither is ideal. 两者都不理想。 Overloading a name for the purpose of indicating an intended spec in another part of the code is unfortunate- your name should just be a name. 为了在代码的另一部分中指示预期的规范而重载名称是不幸的 - 您的名字应该只是一个名称。 Nor should you have to modify convention code every time you need to add or modify a limited-length class property. 每次需要添加或修改有限长度的类属性时,也不必修改约定代码。 So how can you write a convention class that gives you control and provides that control in a simple and elegant way? 那么如何编写一个可以控制并以简单优雅的方式提供控件的约定类呢?

It'd be sweet if you could just decorate your property like I did for the Body property here: 如果你能像我在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; }
    }
}

If this could work, we'd have the control over each string independently, and we'd be able to specify it directly in our entity. 如果这可行,我们可以独立控制每个字符串,并且我们可以直接在我们的实体中指定它。

Before I start a maelstrom over separation of database from application, let me point out that this is not specifically a database directive (I made a point of not calling the attribute 'Varchar'). 在我开始对应用程序中数据库分离的漩涡之前,让我指出这不是一个特别的数据库指令(我指出不调用属性'Varchar')。 I prefer to characterize this as an augmentation of the System.string, and in my own little universe I'm happy with that. 我更喜欢把它描述为System.string的扩充,而在我自己的小宇宙中,我很高兴。 Bottom line, I want a convenience! 最重要的是,我想要一个方便!

To do this, we need to define the decoration we want to use: 为此,我们需要定义我们想要使用的装饰:

using System;
namespace MyDomain.DBDecorations
{

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

Finally, we need to use a string length convention to use the entity's property decoration. 最后,我们需要使用字符串长度约定来使用实体的属性修饰。 This part may not seem pretty, but it does the job, and the good news is that you won't have to look at it again! 这部分可能看起来不太漂亮,但它确实起作用了,好消息是你不必再看一遍了!

StringColumnLengthConvention.cs: 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);
        }
    }
}

Add this convention to your automapping configuration and there you have it- whenever you want a specific length to result during ExportSchema, now you can just decorate the string property -and only that property- right in your entity! 将此约定添加到您的自动化配置中,只要您想在ExportSchema期间生成特定长度,现在您只需装饰字符串属性 - 并且只能在您的实体中装饰该属性!

One of the consistent way that I found is: 我找到的一致方法是:

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

in which the VarcharMax and classes are 其中包含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; }
    }
}

Hi I came across this Question, with the same problem. 嗨,我遇到了这个问题,同样的问题。 I have an slightly safer way of doing it as I don't want all string fields to have 10000 chars by default. 我有一个更安全的方法,因为我不希望所有字符串字段默认有10000个字符。

Firstly I register fluent nhibernate with some overrides 首先,我注册流利的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 override class looks like this: 我的Mapping覆盖类如下所示:

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

This is only required for small subset of entities with long text values. 这仅适用于具有长文本值的小实体子集。 Maybe some else will find this useful? 也许其他人会发现这有用吗?

Probably you are using " NHibernate validator " as well. 可能你也在使用“ NHibernate验证器 ”。 If yes, Fluent NHibernate will consider all of the NHibernate validator related data annotations automatically, including string length, not null, etc. 如果是,Fluent NHibernate将自动考虑所有NHibernate验证器相关数据注释,包括字符串长度,非空等。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM