EF Code First - глобально установленное отображение varchar над nvarchar - PullRequest
16 голосов
/ 24 февраля 2011

У меня есть простой вопрос, но я не смог найти ответ сам.

Я использую модель EF4 CTP-5 Code First с генерируемыми вручную POCO.Он обрабатывает сравнения строк в сгенерированном SQL как

WHERE N'Value' = Object.Property

. Мне известно, что я могу переопределить эту функцию, используя:

[Column(TypeName = "varchar")]
public string Property {get;set;}

, которая решает проблему для этого единственного вхождения и правильно генерируетSQL как:

WHERE 'Value' = Object.Property

Тем не менее, я имею дело с ОЧЕНЬ большой моделью предметной области, и прохожу каждое строковое поле и настройка TypeName = "varchar" будет очень и очень утомительной.Я хотел бы указать, что EF должен видеть строку как varchar по всем направлениям, так как это стандарт в этой базе данных, а nvarchar является исключением.

Причина, по которой вы хотите исправить это, - эффективность выполнения запроса.Сравнение между varchar и nvarchar очень неэффективно в SQL Server 2k5, где сравнения varchar с varchar выполняются практически сразу.

Ответы [ 4 ]

9 голосов
/ 25 февраля 2011

До EF 4.1 вы могли использовать условные обозначения и добавить следующее соглашение в ModelBuilder:

using System;
using System.Data.Entity.ModelConfiguration.Configuration.Properties.Primitive;
using System.Data.Entity.ModelConfiguration.Conventions.Configuration;
using System.Reflection;

public class MakeAllStringsNonUnicode :
    IConfigurationConvention<PropertyInfo, StringPropertyConfiguration>
{
    public void Apply(PropertyInfo propertyInfo, 
                      Func<StringPropertyConfiguration> configuration)
    {
        configuration().IsUnicode = false;
    }
}

(взято из http://blogs.msdn.com/b/adonet/archive/2011/01/10/ef-feature-ctp5-pluggable-conventions.aspx)


ОБНОВЛЕНИЕ: Сменные соглашения были отброшены для версии 4.1. Проверьте мой блог для альтернативного подхода)

4 голосов
/ 10 сентября 2012

Я расширил ответ Марка Калса (и сообщения в блоге Диего), чтобы глобально установить все строки всех сущностей как не-Юникод согласно вопросу, вместо того, чтобы вызывать его вручную для каждого класса.См. Ниже.

/// <summary>
/// Change the "default" of all string properties for a given entity to varchar instead of nvarchar.
/// </summary>
/// <param name="modelBuilder"></param>
/// <param name="entityType"></param>
protected void SetAllStringPropertiesAsNonUnicode(
    DbModelBuilder modelBuilder,
    Type entityType)
{
    var stringProperties = entityType.GetProperties().Where(
        c => c.PropertyType == typeof(string)
           && c.PropertyType.IsPublic 
           && c.CanWrite
           && !Attribute.IsDefined(c, typeof(NotMappedAttribute)));

    foreach (PropertyInfo propertyInfo in stringProperties)
    {
        dynamic propertyExpression = GetPropertyExpression(propertyInfo);

        MethodInfo entityMethod = typeof(DbModelBuilder).GetMethod("Entity");
        MethodInfo genericEntityMethod = entityMethod.MakeGenericMethod(entityType);
        object entityTypeConfiguration = genericEntityMethod.Invoke(modelBuilder, null);

        MethodInfo propertyMethod = entityTypeConfiguration.GetType().GetMethod(
            "Property", new Type[] { propertyExpression.GetType() });

        StringPropertyConfiguration property = (StringPropertyConfiguration)propertyMethod.Invoke(
            entityTypeConfiguration, new object[] { propertyExpression });
        property.IsUnicode(false);
    }
}

private static LambdaExpression GetPropertyExpression(PropertyInfo propertyInfo)
{
    var parameter = Expression.Parameter(propertyInfo.ReflectedType);
    return Expression.Lambda(Expression.Property(parameter, propertyInfo), parameter);
}

/// <summary>
/// Return an enumerable of all DbSet entity types in "this" context.
/// </summary>
/// <param name="a"></param>
/// <returns></returns>
private IEnumerable<Type> GetEntityTypes()
{
    return this
        .GetType().GetProperties()
        .Where(a => a.CanWrite && a.PropertyType.IsGenericType && a.PropertyType.GetGenericTypeDefinition() == typeof(DbSet<>))
        .Select(a => a.PropertyType.GetGenericArguments().Single());
}

Наконец, вызовите его из вашего OnModelCreating (DbModelBuilder modelBuilder):

foreach (var entityType in GetEntityTypes())
    SetAllStringPropertiesAsNonUnicode(modelBuilder, entityType);
3 голосов
/ 07 февраля 2012

Здесь - это проект Сергей Барский , расширяющий EF для предоставления пользовательских соглашений, в результате которых вы можете создавать пользовательские атрибуты вместо плавного API.

Вот фрагмент кода из здесь , который демонстрирует утилиту в действии. То, что вы не видите здесь - это атрибут десятичной точности и другие.Так что по вашему вопросу, как только вы установите Unicode в false, он должен быть varchar в отличие от nvarchar.

public class Product
{
    public int ProductId { get; set; }
    [Indexed("Main", 0)]
    public string ProductNumber { get; set; }
    [Indexed("Main", 1)]
    [Indexed("Second", direction: IndexDirection.Ascending)]
    [Indexed("Third", direction: IndexDirection.Ascending)]
    public string ProductName { get; set; }
    [String(4, 12, false)] //minLength, maxLength, isUnicode
    public string Instructions { get; set; }
    [Indexed("Third", 1, direction: IndexDirection.Descending)]
    public bool IsActive { get; set; }
    [Default("0")]
    public decimal? Price { get; set; }
    [Default("GetDate()")]
    public DateTime? DateAdded { get; set; }
    [Default("20")]
    public int Count { get; set; }
}

Read this и this для деталей.

1 голос
/ 30 апреля 2012

С помощью блога Диего, чтобы сделать общедоступными свойства VOCAR POCO без использования аннотаций:

    private void SetStringPropertiesAsNonUnicode<e>(DbModelBuilder _modelBuilder) where e:class
    {
        //Indiquem a totes les propietats string que no són unicode per a que es crein com a varchar
        List<PropertyInfo> stringProperties = typeof(e).GetProperties().Where(c => c.PropertyType == typeof(string) && c.PropertyType.IsPublic).ToList();
        foreach (PropertyInfo propertyInfo in stringProperties)
        {
            dynamic propertyExpression = GetPropertyExpression(propertyInfo);
            _modelBuilder.Entity<e>().Property(propertyExpression).IsUnicode(false);
        }
    }

    // Edit: Also stole this from referenced blog post (Scott)
    static LambdaExpression GetPropertyExpression(PropertyInfo propertyInfo)
    {
       var parameter = Expression.Parameter(propertyInfo.ReflectedType);
       return Expression.Lambda(Expression.Property(parameter, propertyInfo), parameter);
    }
...