2017-08-05 10 views
0

私は非常に単純なフレームワークで、CRUD機能を自動化したいと思っています。この質問の目的のために、以下のコードを作成しました。問題を説明するために単純化しました。抽象クラスで定義されたプロパティ値を取得

すべての項目は、(CRUD関数を含む)基本クラス「DbItem」から派生します。これにより、子クラスは追加機能を提供し、DbItemが格納されるテーブル名も定義します。たとえば、 "Equipment"と "Entity"は両方ともDbItemから派生し、テーブル名(それぞれ "equipment"と "entities")を定義します。しかし、 "Entity"クラスは抽象クラスであり、 "Human"クラスと "Animal"クラスによってさらに導出されます。 (すべての人間と動物は共有された "エンティティ"テーブルに保存されますが、機器は別のテーブルに保存されます)。

この部分は動作します。 DbItemで定義されたSave()メソッドは、DbTableプロパティを適切に解決します。

しかし、DbCollectionクラスもあり、これは汎用C#コレクションクラスを拡張しています。私は、DbCollectionが反映された正しいデータベーステーブル名を自動的に決定できることを望みます。

したがって、すべての機器のリストを作成したい場合は、新しいDbCollectionを作成し、コードが適切なSELECT文(SELECT ... FROM equipment ...)を作成します。これは機能します。

しかし、すべてのエンティティ(動物や人間)のリストが必要な場合は、問題があります。 「エンティティ」は抽象としてマークされており、インスタンス化することはできません。しかし、Entity.DbTableプロパティの値を取得する方法もわかりません。

簡単な「修正」は、Entityクラス定義から修飾された「抽象」を削除することです。しかし、これは私にとって正しいことではありません。

Entity.DbTableプロパティの値を取得する方法を教えてください。

class Program 
{ 
    abstract class DbItem 
    { 
     public int Id { get; set; } 
     public abstract string DbTable { get; } 

     public void Save() 
     { 
      Console.WriteLine($"INSERT INTO {DbTable} (Id) VALUES ({this.Id})..."); 
     } 
    } 

    abstract class Entity : DbItem 
    { 
     public sealed override string DbTable { get => "entities"; } 
    } 

    class Human : Entity { } 

    class Equipment : DbItem 
    { 
     public override string DbTable => "equipment"; 
    } 

    class DbCollection<T> : System.Collections.ObjectModel.Collection<T> 
    { 
     public virtual string DbTable { get 
      { 
       Type t = typeof(T); 
       //System.Reflection.PropertyInfo p = t.GetProperty("DbName"); 

       if(t.IsAbstract) 
       { 
        // What do we do here to get the value of Entity.DbTable? 
        var prop = t.GetProperty("DbTable"); 
        // Herein lies the problem: One cannot instantiate an abstract class to provide to the GetValue() method 
        return prop.GetValue(null).ToString(); // System.Reflection.TargetException: 'Non-static method requires a target.' 

       } 
       else 
       { 
        var obj = Activator.CreateInstance(t); 
        //return (obj as DbItem).DbTable; // this also works 
        var prop = t.GetProperty("DbTable"); 
        return prop.GetValue(obj).ToString(); 
       } 
      } 
     } 

     public DbCollection() 
     { 
      Console.WriteLine($"SELECT Id FROM {DbTable} WHERE ..."); 
     } 

    } 

    static void Main(string[] args) 
    { 
     var h = new Human(); 
     h.Save(); // 1. Correctly outputs "entities"; 

     var e = new Equipment(); 
     e.Save(); // 2. Correctly outputs "equipment"; 

     var ec = new DbCollection<Equipment>(); // 3. Correctly outputs "equipment" 


     var hc = new DbCollection<Human>(); // 4. Correctly outputs "entities" 

     var entityCollection = new DbCollection<Entity>(); // 5. Error. 

     Console.ReadLine(); 

    } 
} 
+0

あなたは何を意味しますか:アブストラクトを削除すると、あなたの右には聞こえません。なぜそれは抽象クラスでなければならないのですか?あなたは同じものの必要性を説明してもらえますか? – bhuvin

+0

人間でも動物でもないエンティティは決して存在しないので、「エンティティ」は抽象的でなければならないと私は思うだろう。 私は、人間や動物に固有のもの(および対応するクラスでのみ定義されているもの)に加えて、人間と動物が共有するいくつかのプロパティ/メソッド(例:名前、住所...)を含むために "エンティティ"を使用します。 私はエンティティを抽象としてマークすることは、概念的に正しいと理解しました。 これが当てはまらない場合は、「抽象的な」を削除することをお勧めします:)。 – Ziggga

+0

あなたは静的メンバーを探しています。 '[Attribute]'も妥当です。 –

答えて

1

属性を使用しないでください。属性を使用してください。それはあなたが望むものです、そうですか?コンパイル時に固定されたテーブル名にクラスを関連付けるには?

[DbTable("entities")] 
abstract class Entity : DbItem 
{ 
    //Don't need this any more 
    //public sealed override string DbTable { get => "entities"; } 
} 

をそして、このようにそれを取得する:あなたの人間/動物/エンティティ/ DbItemクラスに追加します

[AttributeUsage(AttributeTargets.Class | Inherited = true)] 
public class DbTableAttribute: System.Attribute 
{ 
    private readonly string _name; 

    public string Name { get { return _name; } } 

    public DbTableAttribute(string name) 
    { 
     _name = name; 
    } 
} 

まず、カスタムを作成するには、テーブル名を保存するクラスの属性:

public string GetDbTable<T>() where T : DbItem 
{ 
    var attr = typeof(T).GetCustomAttributes(
     typeof(DbTableAttribute), true 
    ).FirstOrDefault() as DbTableAttribute; 
    return attr?.Name; 
} 
+0

ジョンありがとう - このアプローチは私のためにうまくいくようだ!私は元の投稿に修正されたコードを補足します。 ベスト、Žiga – Ziggga

0

5番目のケースでは、どのようなことが起こりますか?

abstractクラスのインスタンスを作成する方法は絶対にありません。エンティティクラスからキーワードabstractを削除します。 Entityクラスの外部作成を防止する場合は、internalコンストラクタを使用できます。

コレクションの最初のエントリを使用してDbTableの結果を得ることもできます.2番目のアイテムが別のタイプである可能性があるため危険です。

+0

ありがとう、マイケル - 5番目のケースでは、4番目のケースと同じ結果を得たいです - "...エンティティ" ... DBTableを取得するために最初のエントリを使用できません - コレクションにはこの情報が必要ですすでに最初のエントリを取得する – Ziggga

関連する問題