Entity Framework 4.0 автоматически обрезает / обрезает строку перед вставкой - PullRequest
13 голосов
/ 22 февраля 2011

Предположим, у меня есть таблица с колонкой Description, varchar (100). Если попытаться вставить строку длиной более 100 символов, вставка завершится неудачей.

Есть ли способ в Entity Framework автоматически обрезать или обрезать строку, чтобы она помещалась в столбец перед вставкой в ​​столбец? В моем сценарии мне все равно, будет ли строка обрезана, я просто хочу, чтобы она была вставлена, а не просто заканчивалась ошибкой и регистрировала ошибку.

Поскольку модель уже знает ограничения по длине, я подумал, что Entity Framework мог бы сделать это для меня.

Если это не поддерживается, каков наилучший способ сделать это? Расширить автоматически созданные частичные классы и переопределить методы On * Changed? Я бы предпочел не жестко кодировать пределы длины, а использовать ограничения длины, уже определенные в модели объекта. Как я могу получить доступ к этому?

Редактировать

Мое окончательное решение состояло в том, чтобы реализовать частичный метод On * Changed автоматически сгенерированного объекта.

Я использовал этот метод для получения ObjectContext из экземпляра сущности, а затем использовал приведенный ниже метод для извлечения максимальной длины и усечения строки.

Ответы [ 5 ]

8 голосов
/ 22 февраля 2011

Это даст вам максимальную длину столбца ..

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;
    }
6 голосов
/ 11 мая 2017

Вот мое однолинейное решение

(вызов одной строки, реализация немного больше)

Я взял код у @elbweb и адаптировал его для своих целей. В моем случае я выполнял синтаксический анализ файлов EDI, некоторые из которых имели 15 различных уровней иерархии, и я не хотел явно указывать все 15 различных типов - я хотел, чтобы одна строка работала для всех типов сущностей.

Это немного по-другому, но теперь звонить безболезненно. Это определенно сказывается на производительности, но для меня это приемлемо. По сути, поместите это внутри вашего класса DbContext, а затем вызовите его вручную в одну строку (или вы можете автоматически вызвать его, переопределив SaveChanges для его вызова).

Код в вашем 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));
                }
            }
        }
    }
}

Потребление

try
{
    innerContext.TruncateAllStringsOnAllEntitiesToDbSize();
    innerContext.SaveChanges();
}
catch (DbEntityValidationException e)
{
    foreach (var err in e.EntityValidationErrors)
    {
        log.Write($"Entity Validation Errors: {string.Join("\r\n", err.ValidationErrors.Select(v => v.PropertyName + "-" + v.ErrorMessage).ToArray())}");
    }
    throw;
}

Перед этим кодом SaveChanges вызовет перехват в моем примере выше, когда вы попытаетесь вставить слишком большую строку. После добавления строки TruncateAllStringsOnAllEntitiesToDbSize теперь она отлично работает! Я уверен, что есть некоторые оптимизации, которые могут пойти на это, так что, пожалуйста, критикуйте / помогайте! : -)

Примечание: я пробовал это только на EF 6.1.3

2 голосов
/ 14 ноября 2016

Я взял некоторую логику из ответа Ричарда и превратил ее в метод для усечения всех строк объекта платформы сущности на основе их максимальной длины, если они ограничены.

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));
            }
        }
    }
1 голос
/ 18 апреля 2014

Я использовал немного другую тактику, но также использовал методы On * Changed.Я генерирую частичные классы, используя урезанную версию файла .tt, используемого EF.Соответствующий раздел, где свойства создаются.Максимальная длина доступна и может использоваться для усечения строки.

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

        } 
        <#
    } 
0 голосов
/ 29 апреля 2019

Этот подход использует атрибуты свойств объекта, поэтому он работает с 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; }
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...