Переопределить для свободного NHibernate для длинных текстовых строк nvarchar (MAX) не nvarchar (255) - PullRequest
40 голосов
/ 26 февраля 2010

Когда вы устанавливаете строковое значение в беглом NHibernate, оно всегда устанавливает значения DB для Nvarchar (255), мне нужно хранить довольно много длинных строк, которые основаны на пользовательских вводах, а 255 нецелесообразно.

Просто добавлю, что это проблема с автоматом, поскольку я использую свободный NHibernate для создания базы данных.

Ответы [ 6 ]

33 голосов
/ 26 февраля 2010

Добавление этого соглашения установит длину по умолчанию для строковых свойств равной 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.

17 голосов
/ 26 февраля 2010

Установка длины более 4001 сгенерирует NVarchar (MAX) ...

.WithLengthOf(10000);

Подробнее см. Здесь ...

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

6 голосов
/ 10 апреля 2011

С помощью Fluent Nhibernate Automapper можно быстро понять, что готовое поведение для столбцов varchar далеко не идеальное. Сначала вы обнаружите, что каждое строковое свойство было экспортировано как varchar (255), и вам нужно создать столбец, который будет varchar (max). Но в идеале вам не нужно делать каждую строку varchar (макс.), Верно? Таким образом, вы идете по этому проторенному пути, чтобы найти лучший способ контролировать процесс, не нарушая различные элегантные модели в игре ...

Если вы хотите, чтобы получающиеся столбцы 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 голосов
/ 28 июня 2012

Один из последовательных способов, которые я нашел, это:

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; }
    }
}
2 голосов
/ 31 августа 2013

Привет, я наткнулся на этот вопрос, с той же проблемой. У меня есть более безопасный способ сделать это, поскольку я не хочу, чтобы все строковые поля имели 10000 символов по умолчанию.

Сначала я регистрирую беглый nhibernate с некоторыми переопределениями

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

Мой класс переопределения Mapping выглядит следующим образом:

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

Это требуется только для небольшого подмножества сущностей с длинными текстовыми значениями. Может быть, кто-то еще найдет это полезным?

1 голос
/ 13 ноября 2011

Возможно, вы также используете " NHibernate validator ". Если да, Fluent NHibernate автоматически рассмотрит все аннотации данных, связанных с валидатором NHibernate, включая длину строки, не ноль и т. Д.

...