2011-02-22 7 views
11

説明、varchar(100)という列のテーブルがあるとします。 100文字を超える文字列を挿入しようとすると、挿入が失敗します。Entity Framework 4.0挿入前の文字列を自動的に切り捨てる/トリムする

列に挿入する前に列に収まるように文字列を自動的に切り捨てるかトリムする方法はありますか?私のシナリオでは、文字列が切り捨てられているかどうかは気にしません。ただ失敗してログに記録するのではなく、挿入するだけです。

モデルには既に長さの制限が分かっているため、Entity Frameworkでこれを行う方法があると思っていました。

これがサポートされていない場合、これを行うにはどうすればよいですか?自動生成された部分クラスを拡張し、On * Changedメソッドをオーバーライドしますか?私は、長さの制限をハードコーディングするのではなく、むしろエンティティモデルで既に定義されている長さの制限を使用することを好みます。どうすればこれにアクセスできますか?

編集

は私の最終的な解決策は、自動生成されたエンティティのオン*変更部分メソッドを実装することでした。

私はthis methodを使用して、エンティティインスタンスからObjectContextを取得し、以下のメソッドを使用して最大長を抽出し、その文字列を切り捨てました。

答えて

7

これは..あなたに

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

の列の最大長を与えるだろう、私は少し異なるタックを使用するだけでなく、オン*変更方法を利用します。 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 #>); 

     } 
     <# 
    } 
2

私はリチャードの答えからロジックの一部を取り、彼らは限られている場合、彼らの最大の長さに基づいて、エンティティフレームワークオブジェクトのすべての文字列を切り捨てる方法にそれを回しました。ここで

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

は私が@elbwebからコードを取り、自分の目的のためにそれを適応し、私のワンラインのソリューション

(それが1行で呼び出し、実装はもう少しです)

です。私の場合は、EDIファイルを解析していましたが、その中には15階層の階層があり、15種類すべてを明示的に指定したくなかったため、すべてのエンティティタイプで機能する1つのライナーが必要でした。

これは少し異なりますが、今は電話するのが無難です。確かにこれにパフォーマンスヒットがありますが、私にとっては受け入れられます。基本的にDbContextクラスの内部に配置し、手動で呼び出すための1ライナーです(または、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

+0

でこれを試してみました。 私のケースでは、EFが任意の列から列の長さを取得できないようです。 –

+0

@ReuelRibeiroもしあなたが再現ステップを作成して新しい質問に入れることができたら、私はそれをあなたのために修正できるかどうかを見て喜んで見ていきます。私はこのコードに問題はありませんでしたが、Entity Framework Extensionsにはこの機能が組み込まれています。 – Jaxidian

関連する問題