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

Posted

技术标签:

【中文标题】为长文本字符串覆盖流利的 NHibernate nvarchar(MAX) 而不是 nvarchar(255)【英文标题】:Override for fluent NHibernate for long text strings nvarchar(MAX) not nvarchar(255) 【发布时间】:2011-01-21 12:50:43 【问题描述】:

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

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

【问题讨论】:

【参考方案1】:

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

【讨论】:

正是我要找的,哦,对于其他任何人,您将此行添加到 utoPersistenceModelGenerator 文件中的 GetConventions() 方法 c.Add();【参考方案2】:

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

.WithLengthOf(10000);

详情请看这里...

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

【讨论】:

这看起来适用于单个字符串值,但我需要这样做以接近 300 个数据字段,所以我正在寻找一种简单的方法来覆盖每个 nvarchar(255) 并设置它作为 nvarchar(Max)【参考方案3】:

使用 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 期间产生特定长度时,现在您只需在实体中装饰字符串属性 - 并且只有该属性!

【讨论】:

我喜欢这种方式,因此我们可以为字符串属性设置特定的最大长度,但我看到有人说它破坏了 POCO,但我不在乎。我认为您可以通过使用AttributePropertyConvention&lt;T&gt; 来改善这一点,并且应该只使用一行代码leng = ((MyDomain.DBDecorations.StringLength)(myCustomAttrs[0])).Length; 【参考方案4】:

我发现的一种一致方式是:

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; 
    

【讨论】:

【参考方案5】:

您好,我遇到了这个问题,也遇到了同样的问题。我有一种稍微更安全的方法,因为我不希望所有字符串字段默认都有 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);
       

这仅适用于具有长文本值的一小部分实体。也许其他人会觉得这很有用?

【讨论】:

【参考方案6】:

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

【讨论】:

以上是关于为长文本字符串覆盖流利的 NHibernate nvarchar(MAX) 而不是 nvarchar(255)的主要内容,如果未能解决你的问题,请参考以下文章

流利的 NHibernate 多对多创建附加表

使用流利的 nhibernate 映射枚举

每个子类保存的流利 NHibernate 表

流利的NHibernate中的层次零对一关系

使用流利的nhibernate映射枚举

流利的nhibernate映射多对多配置错误