2016-10-08 45 views
2

私は誰でもこれを手伝ってくれることを願っています。私はTPC(コンクリートクラスごとのテーブル)とEF6を使用します。リビジョンを保存するテーブルはたくさんあります。つまり、テーブル内のすべての行が同じIDを持ちますが、複数のバージョンがあることを意味します。 1つのバージョンが現在のバージョン(たとえば、)です。 テーブルの学生サンプル:エンティティフレームワークキャストはサポートされていません

Id Version  Name  CurrentVersion 
1 1   Murtin  false 
1 2   Martin  true 
2 1   Reinold  true 

私のモデルでは継承を使用しています。すべてのモデルのベースは、私がコースの学生の

abstract class RevisionBase : EntityBase, IRevisionBase 
{ 
    [Key] 
    [Column(Order = 1)] 
    public int Version { get; set; } 

    [Required] 
    public bool CurrentVersion { get; set; } 
} 

が今私のテーブル私はで動作する次のベースクラスに、使用したセーブ改訂されているすべてのテーブルについて

abstract class EntityBase: IEntityBase 
{ 
    [Key] 
    [Column(Order = 0)] 
    public int Id { get; set; } 
} 

をEntityBaseさ

class Student: RevisionBase 
{ 
    [Required] 
    public string Name{ get; set; } 
} 

次のコードは簡単で作業が簡単です

new DBContext().Student.FirstOrDefault(s=>s.Id=5 && s.CurrentVersion=true) 

均一なデータ処理のために、私は常に同じ基本メソッドを使用してロードします。だから私は一様にエラー処理とログを持っています。この一般的なgetメソッドは、バージョニングされたテーブルの場合は常に現在のバージョンをロードする必要があります。

private TEntity GeneralGet<TEntity>(int id) where TEntity : EntityBase 
    { 
     using (var ctx = GetContext()) 
     { 
      if (typeof(IRevisionBase).IsAssignableFrom(typeof(TEntity))) 
      { 
       var allResultsId = ctx.Set<TEntity>().Where(l => l.Id == id); 
       var result = allResultsId.ToList().Cast<IRevisionBase>().FirstOrDefault(r => r.CurrentVersion); 
       return result as TEntity; 
      } 
      else  // ansonsten, bei nicht versionierten Objekten 
      { 
       var result = ctx.Set<TEntity>().FirstOrDefault(l => l.Id == id); 
       return result; 
      } 
     } 
    } 

これは完全に機能していますが、残念ながら同じIDのすべての行をロードしてメモリ内の現在のバージョンをフィルタリングする必要があります。 (ToListは、指定されたIDを持つすべての行をロードします。)

これ以上の方法はありますか?

私はそこにこの "ToList"を望んでいません。私が必要とするのは、現在のバージョンをチェックするためのキャストか、メソッドチェーンの方法とハードコードされたフィルタ文字列を組み合わせることです。 (いいじゃないけどこれよりずっと良い)このテーブルにはCurrentVersionという列があることは分かっている。すべてのレコードをロードする前にこれをチェックしたいと思います。

答えて

1

EFはキャスト(および一般的にはインターフェイス)のようなものではなく、C#では「キャスト」タイプが許可されていません。

あなたが依頼していることはまだ可能です。私は、少なくとも2つ(おそらく完璧ではないが、作業)のオプションを参照してください。

static IQueryable<TEntity> WhereCurrentVersion<TEntity>(IQueryable<TEntity> source) 
    where TEntity : class, IRevisionBase 
{ 
    return source.Where(e => e.CurrentVersion); 
} 

と反射を介して、またはそれを動的に呼び出す:

(1)は、このような一般的な制約メソッドを作成します。動的

private TEntity GeneralGet<TEntity>(int id) where TEntity : EntityBase 
{ 
    using (var ctx = GetContext()) 
    { 
     var result = ctx.Set<TEntity>().Where(e => e.Id == id); 
     if (result is IQueryable<IRevisionBase>) 
      result = WhereCurrentVersion((dynamic)result); 
     return result.FirstOrDefault(); 
    } 
} 

(2)ビルドフィルタ式:

private TEntity GeneralGet<TEntity>(int id) where TEntity : EntityBase 
{ 
    using (var ctx = GetContext()) 
    { 
     var result = ctx.Set<TEntity>().Where(e => e.Id == id); 
     if (result is IQueryable<IRevisionBase>) 
     { 
      var parameter = Expression.Parameter(typeof(TEntity), "e"); 
      var predicate = Expression.Lambda<Func<TEntity, bool>>(
       Expression.Property(parameter, nameof(IRevisionBase.CurrentVersion)), 
       parameter); 
      result = result.Where(predicate); 
     } 
     return result.FirstOrDefault(); 
    } 
} 
+1

大きなイワン、ありがとう。私は最初のバージョンをチェックして、それは実際に動作しています。 1つのSQL文だけがDBに送信されます。このSQL文には、両方の条件が含まれています。今私は自分自身のために第二の方法を学ぶことを試みます... – Dosihris

+0

うわー、第二の方法も働いています!残念ながら私は一度だけ投票できます! – Dosihris

+0

あなたは歓迎されています。喜んで助けました:) –

0

プロパティを含むモデルを作成し、キャスト拡張メソッドを使用して適切な型にキャストできます。次に、単純なLINQを行います。

public static T Cast<T>(this Object myobj) 
{ 
    Type objectType = myobj.GetType(); 
    var dx = myobj.GetType().GetProperties(); 
    Type target = typeof(T); 
    var x = Activator.CreateInstance(target, false); 
    var z = from source in objectType.GetMembers().ToList() 
      where source.MemberType == MemberTypes.Property 
      select source; 
    var d = from source in target.GetMembers().ToList() 
      where source.MemberType == MemberTypes.Property 
      select source; 
    List<MemberInfo> members = d.Where(memberInfo => d.Select(c => c.Name).ToList().Contains(memberInfo.Name)).ToList(); 
    PropertyInfo propertyInfo; 
    object value; 
    foreach (var memberInfo in members) 
    { 
     propertyInfo = typeof(T).GetProperty(memberInfo.Name);     
     if (Array.Exists(dx, a => a.Name == propertyInfo.Name)) 
     { 
      value = myobj.GetType().GetProperty(memberInfo.Name).GetValue(myobj, null); 
      propertyInfo.SetValue(x, value, null); 
     } 
    } 
    return (T)x; 
} 
+1

そのアイデアをありがとうが、これは便利な方法ではありません。これは多くのリフレクションコードであり、多くの新しいインスタンスも作成されます。私は実際に私のEFフィルターで実際に持っている列をチェックしたいだけです... – Dosihris

関連する問題