实体框架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在插入之前自动截断/修剪字符串的主要内容,如果未能解决你的问题,请参考以下文章

实体框架代码首先截断我的小数

实体框架、SQL CE 4.0 和 DB 测试自动化

修剪字符串中的额外字符(截断字符串)

实体框架核心中的截断表

实体框架:在插入新记录之前检查记录是不是存在

在数据库插入之前修剪空格