如何更改Entity Framework 6中字符串属性的默认最大长度?

时间:2014-05-15 22:00:01

标签: ef-code-first entity-framework-6.1

默认情况下,实体模型中未明确指定最大长度的字符串属性在数据库中设置为nvarchar(max)。我们希望覆盖此约定,并且如果字符串尚未明确设置,则为字符串提供最大长度nvarchar(100)

我发现了PropertyMaxLengthConvention内置约定,它的描述和文档似乎就是我想要的。但是,要么它不起作用,要么我使用它错了,或者它只是没有按照我的想法去做。

我尝试过简单地添加约定:

modelBuilder.Conventions.Add(new PropertyMaxLengthConvention(100));

然后我想也许默认的一个已经被使用了,所以我尝试先删除它:

modelBuilder.Conventions.Remove<PropertyMaxLengthConvention>();
modelBuilder.Conventions.Add(new PropertyMaxLengthConvention(100));

我甚至尝试在默认值之前和之后明确添加约定:

modelBuilder.Conventions.AddBefore<PropertyMaxLengthConvention>(new PropertyMaxLengthConvention(100));
modelBuilder.Conventions.AddAfter<PropertyMaxLengthConvention>(new PropertyMaxLengthConvention(100));

没有快乐。添加迁移时,列仍会创建为nvarchar(max)

有没有办法使用该约定来做我想要的?如果没有,我可以编写一个自定义约定,将默认字符串属性设置为nvarchar(100),但仍然允许我明确地将它们设置为不同的值,包括maxlength?

3 个答案:

答案 0 :(得分:4)

在追踪上述约定的源代码之后,我发现它只为指定具有固定长度的属性设置默认的最大长度。 (奇妙!)

所以我拿了源代码并修改它来创建我自己的约定。现在,具有未指定最大长度的字符串属性将具有默认的最大长度,而不是nvarchar(max)。唯一的缺点是,似乎没有一种方法可以检测何时明确应用IsMaxLength()配置。因此,如果我有一个我想要创建为nvarchar(max)的列,我就不能使用IsMaxLength()来完成它。

为了解决这个问题,我为StringPropertyConfiguration创建了一个名为ForceMaxLength()的扩展方法,该方法使用HasMaxLength(int.MaxValue)配置属性 - 通常是一个无效值,但我可以在自定义约定中轻松测试。当我检测到它时,我只需将MaxLength设置为null并将IsMaxLength设置为true并让属性配置继续正常。

以下是自定义约定:

using System;
using System.Collections.Generic;
using System.Data.Entity.Core.Metadata.Edm;
using System.Data.Entity.Infrastructure;
using System.Data.Entity.ModelConfiguration.Conventions;

namespace MyProject.CustomConventions
{
    public class CustomPropertyMaxLengthConvention : IConceptualModelConvention<EntityType>, IConceptualModelConvention<ComplexType>
    {
        private const int DefaultLength = 128;
        private readonly int length;
        public CustomPropertyMaxLengthConvention()
            : this(DefaultLength)
        {
        }
        public CustomPropertyMaxLengthConvention(int length)
        {
            if (length <= 0)
            {
                throw new ArgumentOutOfRangeException("length", "Invalid Max Length Size");
            }
            this.length = length;
        }
        public virtual void Apply(EntityType item, DbModel model)
        {
            SetLength(item.DeclaredProperties);
        }
        public virtual void Apply(ComplexType item, DbModel model)
        {
            SetLength(item.Properties);
        }
        private void SetLength(IEnumerable<EdmProperty> properties)
        {
            foreach (EdmProperty current in properties)
            {
                if (current.IsPrimitiveType)
                {
                    if (current.PrimitiveType == PrimitiveType.GetEdmPrimitiveType(PrimitiveTypeKind.String))
                    {
                        SetStringDefaults(current);
                    }
                    if (current.PrimitiveType == PrimitiveType.GetEdmPrimitiveType(PrimitiveTypeKind.Binary))
                    {
                        SetBinaryDefaults(current);
                    }
                }
            }
        }
        private void SetStringDefaults(EdmProperty property)
        {
            if (property.IsUnicode == null)
            {
                property.IsUnicode = true;
            }
            SetBinaryDefaults(property);
        }
        private void SetBinaryDefaults(EdmProperty property)
        {

            if (property.MaxLength == int.MaxValue)
            {
                property.MaxLength = null;
                property.IsMaxLength = true;
            }
            else if (property.MaxLength == null || !property.IsMaxLength)
            {
                property.MaxLength = length;
            }

        }
    }
}

这是扩展方法:

using System.Data.Entity.ModelConfiguration.Configuration;

namespace MyProject.Model.Mapping
{
    public static class MappingExtensions
    {
        public static void ForceMaxLength(this StringPropertyConfiguration obj)
        {
            obj.HasMaxLength(int.MaxValue);
        }
    }
}

以下是如何使用的:

using System.Data.Entity.ModelConfiguration;

namespace MyProject.Model.Mapping
{
    public class MyEntityMap : EntityTypeConfiguration<MyEntity>
    {
        public MyEntityMap()
        {
            Property(v => v.StringValue).ForceMaxLength();
        }
    }
}

答案 1 :(得分:2)

或者只是

public class StringConventions : Convention
{
    public StringConventions()
    {
        this.Properties<string>().Configure(x => x.HasMaxLength(100));
    }
}

答案 2 :(得分:0)

在EF6中,您可以使用自定义代码优先约定,但您还需要有一种方法将nvarchar(max)数据类型指定为字符串属性。所以,我提出了以下解决方案。

/// <summary>
/// Set this attribute to string property to have nvarchar(max) type for db table column.
/// </summary>
[AttributeUsage(AttributeTargets.Property, AllowMultiple = false)]
public sealed class TextAttribute : Attribute
{
}

/// <summary>
/// Changes all string properties without System.ComponentModel.DataAnnotations.StringLength or
/// Text attributes to use string length 16 (i.e nvarchar(16) instead of nvarchar(max) by default).
/// Use TextAttribute to a property to have nvarchar(max) data type.
/// </summary>
public class StringLength16Convention : Convention
{
    public StringLength16Convention()
    {
        Properties<string>()
            .Where(p => !p.GetCustomAttributes(false).OfType<DatabaseGeneratedAttribute>().Any())
            .Configure(p => p.HasMaxLength(16));

        Properties()
            .Where(p => p.GetCustomAttributes(false).OfType<TextAttribute>().Any())
            .Configure(p => p.IsMaxLength());
    }
}

public class CoreContext : DbContext, ICoreContext
{
    protected override void OnModelCreating(DbModelBuilder modelBuilder)
    {
        //Change string length default behavior.
        modelBuilder.Conventions.Add(new StringLength16Convention());
    }
}


public class LogMessage
{
    [Key]
    public Guid Id { get; set; }


    [StringLength(25)] // Explicit data length. Result data type is nvarchar(25)
    public string Computer { get; set; }

    //[StringLength(25)] // Implicit data length. Result data type is nvarchar(16)
    public string AgencyName { get; set; }

    [Text] // Explicit max data length. Result data type is nvarchar(max)
    public string Message { get; set; }
}