EF Code First - глобально настроить отображение varchar над nvarchar
У меня есть вопрос, который должен быть легким, но я сам не смог найти ответ.
Я использую EF4 CTP-5 Code First Model с вручную созданным POCOs. Он обрабатывает сравнение строк в сгенерированном 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 выполняется почти сразу.
Ответы
Ответ 1
До 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)
UPDATE: для версии 4.1 были сброшены соглашения с подключаемыми модулями. Проверить мой блог для альтернативного подхода)
Ответ 2
Я продолжил ответ Марка Калса (и сообщение в блоге Diego), чтобы глобально установить все строки для всех объектов как не-Юникод в соответствии с вопросом, вместо того, чтобы называть его вручную для каждого класса. См. Ниже.
/// <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
Здесь - проект Сергей Барский, который расширяет 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; }
}
Прочтите этот и этот для подробностей.
Ответ 4
С помощью помощи блога Диего, чтобы публичные свойства PSCO varchar без использования анотаций:
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);
}