实体框架4.0在插入之前自动截断/修剪字符串
Posted
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了实体框架4.0在插入之前自动截断/修剪字符串相关的知识,希望对你有一定的参考价值。
假设我有一个包含Description Description,varchar(100)列的表。如果尝试插入超过100个字符的字符串,插入将失败。
在插入列之前,Entity Framework中是否有一种方法可以自动截断或修剪字符串以适应列?在我的场景中,我真的不在乎字符串是否被截断,我只是想要插入而不是仅仅失败并记录rror。
由于该模型已经知道了长度限制,我认为Entity Framework可能有办法为我做这个。
如果不支持,最好的方法是什么?扩展自动生成的部分类并覆盖On * Changed方法?我宁愿不对长度限制进行硬编码,而是使用已在实体模型中定义的长度限制。我怎么能访问这个?
编辑
我的最终解决方案是实现自动生成实体的On * Changed部分方法。
我使用this method从实体实例获取ObjectContext,然后使用下面的方法提取最大长度,并截断字符串。
这将为您提供列的最大长度..
public int? GetColumnMaxLength(ObjectContext context, string entityTypeName, string columnName)
{
int? result = null;
Type entType = Type.GetType(entityTypeName);
var q = from meta in context.MetadataWorkspace.GetItems(DataSpace.CSpace)
.Where(m => m.BuiltInTypeKind == BuiltInTypeKind.EntityType)
from p in (meta as EntityType).Properties
.Where(p => p.Name == columnName
&& p.TypeUsage.EdmType.Name == "String")
select p;
var queryResult = q.Where(p =>
{
bool match = p.DeclaringType.Name == entityTypeName;
if (!match && entType != null)
{
//Is a fully qualified name....
match = entType.Name == p.DeclaringType.Name;
}
return match;
}).Select(sel => sel.TypeUsage.Facets["MaxLength"].Value);
if (queryResult.Any())
{
result = Convert.ToInt32(queryResult.First());
}
return result;
}
Here's my One-Line Solution
(调用它是一行,实现更多一点)
我从@elbweb那里获取了代码并根据我的目的进行了调整。在我的情况下,我正在解析EDI文件,其中一些文件有15个不同级别的层次结构,我不想明确指定所有15种不同的类型 - 我想要一个适用于所有实体类型的单行程序。
这有点不同,但现在打电话很轻松。这肯定会对性能产生影响,但对我来说这是可以接受的。基本上把它放在你的DbContext类中,然后它是一个单行程来手动调用(或者你可以通过重写SaveChanges来调用它来自动调用它)。
Code in your DbContext:
public class MyContext : DbContext
{
...
public void TruncateAllStringsOnAllEntitiesToDbSize()
{
var objectContext = ((IObjectContextAdapter) this).ObjectContext;
var stringMaxLengthsFromEdmx =
objectContext.MetadataWorkspace
.GetItems(DataSpace.CSpace)
.Where(m => m.BuiltInTypeKind == BuiltInTypeKind.EntityType)
.SelectMany(meta => ((EntityType) meta).Properties
.Where(p => p.TypeUsage.EdmType.Name == "String"))
.Select(d => new
{
MaxLength = d.TypeUsage.Facets["MaxLength"].Value,
PropName = d.Name,
EntityName = d.DeclaringType.Name
})
.Where(d => d.MaxLength is int)
.Select(d => new {d.PropName, d.EntityName, MaxLength = Convert.ToInt32(d.MaxLength)})
.ToList();
var pendingEntities = ChangeTracker.Entries().Where(e => e.State == EntityState.Added || e.State == EntityState.Modified).Select(x => x.Entity).ToList();
foreach (var entityObject in pendingEntities)
{
var relevantFields = stringMaxLengthsFromEdmx.Where(d => d.EntityName == entityObject.GetType().Name).ToList();
foreach (var maxLengthString in relevantFields)
{
var prop = entityObject.GetType().GetProperty(maxLengthString.PropName);
if (prop == null) continue;
var currentValue = prop.GetValue(entityObject);
var propAsString = currentValue as string;
if (propAsString != null && propAsString.Length > maxLengthString.MaxLength)
{
prop.SetValue(entityObject, propAsString.Substring(0, maxLengthString.MaxLength));
}
}
}
}
}
Consumption
try
{
innerContext.TruncateAllStringsOnAllEntitiesToDbSize();
innerContext.SaveChanges();
}
catch (DbEntityValidationException e)
{
foreach (var err in e.EntityValidationErrors)
{
log.Write($"Entity Validation Errors: {string.Join("
", err.ValidationErrors.Select(v => v.PropertyName + "-" + v.ErrorMessage).ToArray())}");
}
throw;
}
在此代码之前,当您尝试插入太大的字符串时,SaveChanges
会触发上面示例中的catch。添加TruncateAllStringsOnAllEntitiesToDbSize
线后,现在效果很好!我确信有一些优化可以进入这个,所以请批评/贡献! :-)
注意:我只在EF 6.1.3上试过这个
我从理查德的答案中获取了一些逻辑,并将其转换为一种方法,根据实际框架对象的最大长度截断它们的所有字符串,如果它们有限的话。
public static void TruncateStringsInEFObject<T>(List<T> entityObjects, ObjectContext context)
{
var stringMaxLengthsFromEdmx = context.MetadataWorkspace.GetItems(DataSpace.CSpace)
.Where(m => m.BuiltInTypeKind == BuiltInTypeKind.EntityType)
.SelectMany(meta => (meta as EntityType).Properties
.Where(p => p.TypeUsage.EdmType.Name == "String"
&& p.DeclaringType.Name == typeof(T).Name))
.Select(d => new {MaxLength = d.TypeUsage.Facets["MaxLength"].Value, d.Name})
.Where(d => d.MaxLength is int)
.Select(d => new {d.Name, MaxLength = Convert.ToInt32(d.MaxLength)})
.ToList();
foreach (var maxLengthString in stringMaxLengthsFromEdmx)
{
var prop = typeof(T).GetProperty(maxLengthString.Name);
if (prop == null) continue;
foreach (var entityObject in entityObjects)
{
var currentValue = prop.GetValue(entityObject);
var propAsString = currentValue as string;
if (propAsString != null && propAsString.Length > maxLengthString.MaxLength)
{
prop.SetValue(entityObject, propAsString.Substring(0, maxLengthString.MaxLength));
}
}
}
我使用了稍微不同的方法,但也使用了On * Changed方法。我正在使用EF使用的.tt文件的精简版生成部分类。相关部分是生成属性的位置。最大长度可用,可用于截断字符串。
foreach (EdmProperty property in
entity.Properties.Where(p => p.DeclaringType == entity
&& p.TypeUsage.EdmType is PrimitiveType))
{
/// If this is a string implements its OnChanged method
if (property.TypeUsage.ToString() != "Edm.String") continue;
int maxLength = 0;
if (property.TypeUsage.Facets["MaxLength"].Value == null) continue;
if (!Int32.TryParse(property.TypeUsage.Facets["MaxLength"].Value.ToString(),
out maxLength)) continue;
if (maxLength == 0) continue;
// Implement the On*Changed method
#>
partial void On<#= property.Name#>Changed() {
<#=code.FieldName(property)#> =#=code.FieldName(property)#>.Substring(0,<#= maxLength #>);
}
<#
}
此方法使用对象属性的属性,因此它适用于EF或其他方案。如果属性具有“StringLength”属性,则它将被截断。
// Truncate any string that is too long.
var entry = new MyObject(); // Entity Framework object
entry.GetType().GetProperties().ToList().ForEach(p =>
{
foreach (StringLengthAttribute attribute in p.GetCustomAttributes(true)
.Where(a => a is StringLengthAttribute).Cast<StringLengthAttribute>())
{
string value = (p.GetValue(entry) ?? "").ToString();
if (value.Length > attribute.MaximumLength)
{
// oops. Its too Long, so truncate it.
p.SetValue(entry, value.Substring(0, attribute.MaximumLength));
}
}
});
使用此示例属性正确测试(由于StringLength)
[Required]
[StringLength(6)] // only 6, for testing
public string Message { get; set; }
以上是关于实体框架4.0在插入之前自动截断/修剪字符串的主要内容,如果未能解决你的问题,请参考以下文章