我有一个简单的问题,但我一直无法找到答案。
我正在使用EF4 CTP-5 Code First Model和手工生成的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的比较几乎立即执行。
答案 0 :(得分:9)
在EF 4.1之前,您可以使用约定并将以下约定添加到模型构建器中:
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版本的可插入约定被删除。 Check my blog替代方法)
答案 1 :(得分:4)
我扩展了Marc Cals的回答(以及Diego的博客文章),根据问题将所有实体上的所有字符串全局设置为非unicode,而不是必须每个类手动调用它。见下文。
/// <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模型构建器)调用它:
foreach (var entityType in GetEntityTypes())
SetAllStringPropertiesAsNonUnicode(modelBuilder, entityType);
答案 2 :(得分:3)
Here是来自Sergey Barskiy的项目,它扩展了EF以允许自定义约定,因此,您可以创建自定义属性而不是流畅的API。
以下是来自here的代码段,演示了实用程序的实际效果。
您在此处看不到的内容是小数精度属性和其他属性。所以根据你的问题,一旦你将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; }
}
答案 3 :(得分:1)
借助Diego的博客帮助,在不使用anotations的情况下制作POCO 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);
}
答案 4 :(得分:1)
对于希望在 EF Core(v3 及更高版本)中执行此操作的任何人,实现此目的的快速方法是通过 ModelBuilder.Model
属性;它提供了对模型中所有实体和属性的轻松访问。
“基本”实现如下:
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
// Apply configurations via regular modelBuilder code-first calls
// ...
// ...
// Override the configurations to force Unicode to false
var entities = modelBuilder.Model.GetEntityTypes();
foreach (var entity in entities)
{
foreach (var property in entity.GetProperties())
{
property.SetIsUnicode(false);
}
}
}
EF Core 愉快地忽略了对非字符串属性的 SetIsUnicode
调用,因此您甚至不必检查属性类型(但如果它让您感觉更好,您可以轻松地检查 :)
对于那些喜欢更明确一点的人来说,在 GetProperties()
调用中添加 where 子句就行了:
...
var stringProperties = entity.GetProperties()
.Where(e=> e.ClrType == typeof(string));
foreach (var property in stringProperties)
{
property.SetIsUnicode(false);
}
...