Как проверить метаданные модели EF во время выполнения? - PullRequest
3 голосов
/ 05 марта 2012

Мне нужно сделать несколько обрезок перед сохранением различных полей в нашей базе данных. Мы десериализовываем xml из другого приложения в сущности EF и затем вставляем их. В xml есть несколько полей, которые превышают 4000 символов, и вместо того, чтобы использовать тип данных TEXT, мы бы хотели их обрезать.

Я думал о том, чтобы проверить MetadataWorkspace и DbChangeTracker внутри MyDbContext.SaveChanges(), чтобы найти любые nvarchar(4000) свойства сущности и обрезать любые строковые значения, которые длиннее 4000. Но я понятия не имею, как к этому подойти. Я не мог найти соответствующую документацию. Я видел несколько связанных вопросов , но ни один не вдавался в подробности или предоставил примеры кода.

Вот что у меня так далеко:

public override int SaveChanges()
{
    //TODO: trim strings longer than 4000 where type is nvarchar(max)
    MetadataWorkspace metadataWorkspace = 
        ((IObjectContextAdapter) this).ObjectContext.MetadataWorkspace;
    ReadOnlyCollection<EdmType> edmTypes = 
        metadataWorkspace.GetItems<EdmType>(DataSpace.OSpace);

    return base.SaveChanges();
}

Решение

Вот мое решение, основанное на ответе @ GertArnold.

// get varchar(max) properties
var entityTypes = metadataWorkspace.GetItems<EntityType>(DataSpace.CSpace);
var properties = entityTypes.SelectMany(type => type.Properties)
    .Where(property => property.TypeUsage.EdmType.Name == "String"
           && property.TypeUsage.Facets["MaxLength"].Value.ToString() == "Max"
           // special case for XML columns
           && property.Name != "Xml")
    .Select(
        property =>
            Type.GetType(property.DeclaringType.FullName)
            .GetProperty(property.Name));

// trim varchar(max) properties > 4000
foreach (var entry in ChangeTracker.Entries())
{
    var entity = entry.Entity;
    var entryProperties = 
            properties.Where(prop => prop.DeclaringType == entity.GetType());
    foreach (var entryProperty in entryProperties)
    {
        var value = 
            ((string) entryProperty.GetValue(entity, null) ?? String.Empty);
        if (value.Length > 4000)
        {
            entryProperty.SetValue(entity, value.Substring(0, 4000), null);
        }
    }
}

1 Ответ

5 голосов
/ 06 марта 2012

Вы можете найти свойства по этому коду:

var varchars = context.MetadataWorkspace.GetItemCollection(DataSpace.CSpace)
    .Where(gi => gi.BuiltInTypeKind == BuiltInTypeKind.EntityType)
    .Cast<EntityType>()
    .SelectMany(entityType => entityType.Properties
        .Where(edmProperty => edmProperty.TypeUsage.EdmType.Name == "String")
        .Where(edmProperty => (int)(edmProperty.TypeUsage.Facets["MaxLength"]
            .Value) >= 4000))
    .ToList();

Хитрость заключается в том, чтобы извлечь типы сущностей из модели с помощью BuiltInTypeKind.EntityType и привести их к EntityType, чтобы получить доступ к Properties. EdmProperty имеет свойство DeclaringType, которое показывает, к какому объекту они принадлежат.

...