2011-02-22 13 views
11

Załóżmy, że mam tabelę z kolumną Opis, varchar (100). Jeśli spróbujesz wstawić ciąg znaków zawierający więcej niż 100 znaków, wstawienie się nie powiedzie.Entity Framework 4.0 automatycznie obcina/przycina łańcuch przed wstawieniem

Czy istnieje sposób w Entity Framework do automatycznego obcinania lub przycinania łańcucha w celu dopasowania do kolumny przed wstawieniem do kolumny? W moim scenariuszu, naprawdę nie obchodzi mnie, czy ciąg jest obcinany, po prostu chcę go wstawić, a nie tylko awarię i rejestrowanie rror.

Ponieważ model zna już ograniczenia długości, myślałem, że może to być metoda dla Entity Framework, aby to zrobić dla mnie.

Jeśli to nie jest obsługiwane, jaki jest najlepszy sposób na zrobienie tego? Rozszerz automatycznie generowane klasy częściowe i nadpisaj metody On * Changed? Wolałbym nie zakodować na sztywno limitów długości, ale raczej użyć limitów długości już zdefiniowanych w modelu encji. Jak mogę uzyskać dostęp do tego?

Edit

Moje ostateczne rozwiązanie było wdrożenie ON * Zmieniono częściowe sposobu wygenerowany automatycznie podmiotu.

Użyłem this method pobierania obiektu ObjectContext z instancji jednostki, a następnie użyłem poniższej metody do wyodrębnienia maksymalnej długości i obcięcia łańcucha.

Odpowiedz

7

To daje maksymalną długość kolumny ..

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; 
    } 
1

użyłem nieco inną taktykę, ale również wykorzystując On * zmieniono metod. Generuję klasy częściowe przy użyciu uproszczonej wersji pliku .tt używanego przez EF. Odpowiednia sekcja to miejsce, w którym generowane są właściwości. Maksymalna długość jest dostępna i może być użyta do obcięcia napisu.

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

     } 
     <# 
    } 
2

Wziąłem niektóre z logiką odpowiedź Richarda i przekształcił go w sposób obciąć wszystkie ciągi obiektu Entity Framework na podstawie ich długości max, jeśli są one ograniczone.

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)); 
      } 
     } 
    } 
4

Oto mój jeden-Line Rozwiązanie

(powołując jest jedna linia, realizacja jest trochę więcej)

wziąłem kod z @elbweb i dostosować go do swoich potrzeb. W moim przypadku analizowałem pliki EDI, z których niektóre miały 15 różnych poziomów w hierarchii i nie chciałem jednoznacznie określać wszystkich 15 różnych typów - chciałem jednolinijkowego, który działał dla wszystkich typów jednostek.

Jest trochę inaczej, ale bezboleśnie zadzwonić. Z pewnością jest to uderzenie wydajności, ale jest to dla mnie do zaakceptowania. Zasadniczo umieść to w klasie DbContext, a następnie wywołaj je ręcznie (lub możesz automatycznie wywołać to przez zastąpienie SaveChanges, aby go wywołać).

kod w swoim 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)); 
       } 
      } 
     } 
    } 
} 

Zużycie

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; 
} 

Przed tym kodzie SaveChanges spowodowałaby zaczep w moim przykładzie powyżej, kiedy próbował wstawiania znaków, który był zbyt duży. Po dodaniu linii TruncateAllStringsOnAllEntitiesToDbSize działa teraz świetnie! Jestem pewien, że istnieją pewne optymalizacje, które mogą się do tego przyczynić, więc prosimy o krytykę/udział! :-)

Uwaga: Próbowałem tylko to na EF 6.1.3

+0

To działa, ale o dziwo, tylko dla niektórych kolumn. Wygląda na to, że EF nie może pobrać kolumny z arbitralnych kolumn w moim przypadku. –

+0

@ReuelRibeiro Jeśli możesz utworzyć kroki repro i umieścić je w nowym pytaniu, z przyjemnością sprawdzę, czy mogę to naprawić. Nie miałem żadnych problemów z tym kodem, chociaż ostatnio przestałem go używać teraz, gdy Entity Framework Extensions ma wbudowaną tę funkcję i używamy go teraz. – Jaxidian

Powiązane problemy