2011-02-22 37 views
11

Giả sử tôi có một bảng với cột Mô tả, varchar (100). Nếu cố gắng chèn một chuỗi với hơn 100 ký tự, chèn sẽ thất bại.Entity Framework 4.0 Tự động cắt bớt/cắt chuỗi trước khi chèn

Có cách nào trong Khung thực thể tự động cắt bớt hoặc cắt chuỗi để vừa với cột trước khi chèn vào cột không? Trong kịch bản của tôi, tôi thực sự không quan tâm liệu chuỗi có bị cắt ngắn hay không, tôi chỉ muốn nó được chèn thay vì chỉ thất bại và ghi lại lỗi.

Vì mô hình đã biết giới hạn độ dài, tôi đã nghĩ có thể có một cách để Khung thực thể làm điều này cho tôi.

Nếu điều này không được hỗ trợ, cách tốt nhất để làm điều này là gì? Mở rộng các lớp một phần được tạo tự động và ghi đè các phương thức On * Changed? Tôi không muốn mã hóa cứng các giới hạn độ dài, mà là sử dụng các giới hạn độ dài đã được xác định trong mô hình thực thể. Làm thế nào tôi có thể truy cập vào điều này?

Sửa

giải pháp cuối cùng của tôi là thực hiện On * Thay đổi phương pháp một phần của thực thể được tạo tự động.

Tôi đã sử dụng this method để nhận ObjectContext từ thể hiện đối tượng và sau đó sử dụng phương thức dưới đây để trích xuất độ dài tối đa và cắt bớt chuỗi.

Trả lời

7

này sẽ cung cấp cho bạn độ dài tối đa của một cột ..

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

tôi đã sử dụng một chiến thuật hơi khác nhau, mà còn sử dụng các phương pháp On * Thay đổi. Tôi đang tạo ra các lớp học một phần bằng cách sử dụng một phiên bản rút gọn của tập tin .tt được sử dụng bởi EF. Phần có liên quan là nơi các thuộc tính được tạo. Độ dài tối đa có sẵn và có thể được sử dụng để cắt xén chuỗi.

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

Tôi lấy một số logic từ câu trả lời của Richard và biến nó thành một phương pháp để cắt ngắn tất cả các chuỗi của đối tượng khung thực thể dựa trên độ dài tối đa của chúng.

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

Dưới đây là Một-Line Giải pháp của tôi

(gọi nó là một dòng, việc thực hiện là nhiều hơn một chút)

tôi lấy mã từ @elbweb và áp dụng nó cho mục đích của tôi. Trong trường hợp của tôi, tôi đã phân tích cú pháp các tệp EDI, một số trong đó có 15 cấp độ khác nhau cho cấu trúc phân cấp và tôi không muốn chỉ định rõ ràng tất cả 15 loại khác nhau - tôi muốn một lớp lót hoạt động cho tất cả các loại thực thể.

Có một chút khác biệt nhưng giờ đây không khó để gọi. Có chắc chắn một hiệu suất hit trên này, nhưng nó chấp nhận được cho tôi. Về cơ bản, hãy đặt điều này bên trong lớp DbContext của bạn và sau đó nó là một lớp lót để tự gọi (hoặc bạn có thể tự động gọi nó bằng cách ghi đè các SaveChanges để gọi nó).

Mã trong DbContext của bạn:

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

tiêu thụ

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

Trước khi mã này, SaveChanges sẽ kích hoạt các catch trong ví dụ của tôi ở trên khi bạn đã cố gắng chèn một chuỗi đó là quá lớn. Sau khi thêm dòng TruncateAllStringsOnAllEntitiesToDbSize, nó hoạt động tốt ngay bây giờ! Tôi chắc chắn có một số tối ưu hóa có thể đi vào điều này, do đó, xin vui lòng phê bình/đóng góp! :-)

Lưu ý: Tôi đã chỉ cố gắng này trên EF 6.1.3

+0

Nó hoạt động, nhưng kỳ lạ đủ, chỉ cho một số cột. Có vẻ như EF không lấy được chiều dài cột từ các cột tùy ý trong trường hợp của tôi. –

+0

@ReuelRibeiro Nếu bạn có thể tạo các bước repro và đặt nó vào một câu hỏi mới, tôi sẵn sàng xem xét xem tôi có thể sửa chữa nó cho bạn hay không. Tôi đã không có vấn đề với mã này mặc dù gần đây tôi đã ngừng sử dụng nó ngay bây giờ mà Entity Framework Extensions có chức năng này được tích hợp sẵn và chúng tôi sử dụng nó ngay bây giờ. – Jaxidian

Các vấn đề liên quan