c# entity-framework-4 code-first ef-code-first

c# - Código de EF primero: establezca globalmente la asignación varchar sobre nvarchar



entity-framework-4 code-first (4)

Antes de EF 4.1, podría usar convenciones y agregar la siguiente convención a su 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; } }

(Tomado de http://blogs.msdn.com/b/adonet/archive/2011/01/10/ef-feature-ctp5-pluggable-conventions.aspx )

ACTUALIZACIÓN: Se eliminaron las convenciones conectables para la versión 4.1. Revisa mi blog para un enfoque alternativo)

Tengo lo que debería ser una pregunta fácil, pero no he podido encontrar la respuesta por mí mismo.

Estoy utilizando el primer modelo de código EF4 CTP-5 con POCO generados a mano. Está procesando comparaciones de cadenas en el SQL generado como

WHERE N''Value'' = Object.Property

Soy consciente de que puedo anular esta funcionalidad usando:

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

Lo que soluciona el problema para esa única ocurrencia y genera correctamente el SQL como:

WHERE ''Value'' = Object.Property

Sin embargo, estoy tratando con un modelo de dominio MUY grande y al revisar cada campo de cadena y configurar TypeName = "varchar" va a ser muy tedioso. Me gustaría especificar que EF debería ver la cadena como varchar en todos los ámbitos, ya que ese es el estándar en esta base de datos y nvarchar es el caso de excepción.

El razonamiento para querer corregir esto es la eficiencia de ejecución de consultas. La comparación entre varchar y nvarchar es muy ineficiente en SQL Server 2k5, donde las comparaciones de varchar a varchar se ejecutan casi de inmediato.


Con la ayuda del blog de Diego, hacer las propiedades públicas de una varchar POCO sin usar anotaciones es:

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); }


Extendí la respuesta de Marc Cals (y la publicación del blog de Diego) para establecer globalmente todas las cadenas en todas las entidades como no-unicode según la pregunta, en lugar de tener que llamarlas manualmente por clase. Vea abajo.

/// <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()); }

Finalmente, llámelo desde su OnModelCreating (DbModelBuilder modelBuilder):

foreach (var entityType in GetEntityTypes()) SetAllStringPropertiesAsNonUnicode(modelBuilder, entityType);


Here hay un proyecto de Sergey Barskiy que extiende EF para permitir convenciones personalizadas, que como resultado, puede hacer atributos personalizados en lugar de la API fluida.

Aquí hay un fragmento de código de here que demuestra la utilidad en acción. Lo que no ves aquí es el atributo de precisión decimal y otros. Entonces, según tu pregunta, una vez que configuras Unicode como falso, debería ser varchar en lugar de 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; } }

Lea this y this para más detalles.