2017-08-05 240 views
0

我正在研究一個非常簡單的框架,我想在其中自動執行CRUD功能。爲了這個問題的目的,我創建了下面的代碼,簡單來說明我的問題。獲取抽象類中定義的屬性值

所有項目都從(抽象)基類「DbItem」(包含CRUD函數)派生而來,其中子類提供了附加功能,並且還定義了存儲DbItems的表名。例如,「設備」和「實體」都源自DbItem,並定義表名(分別爲「設備」和「實體」)。然而,「實體」類是抽象的,並且進一步由「人類」和「動物」類派生。 (所有的人類和動物都存儲在共享的「實體」表中,但設備存儲在單獨的表中)。在DbItem中定義的Save()方法正確地解析了DbTable屬性。

然而,我也有一個DbCollection類,它擴展了泛型C#集合類。我希望DbCollection能夠通過反射自動確定正確的數據庫表名。因此,如果我想創建一個所有設備的列表,我創建一個新的DbCollection,並且該代碼組成相應的SELECT語句(SELECT ... FROM設備...)。這工作。

但是,如果我想要一個所有實體(動物和人類)的列表,我有一個問題。 「實體」被標記爲抽象的,我不應該能夠實例化它。但是,我也不知道如何獲取Entity.DbTable屬性的值。

一個簡單的「修復」是從Entity類定義中刪除限定的「abstract」。然而,這聽起來並不正確。

你能告訴我如何獲得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

您正在尋找'static'成員。一個'[Attribute]'也是合理的。 –

回答

1

請勿使用屬性,請使用屬性。這就是你想要的,對吧?將類與在編譯時修復的表名關聯起來?

首先,創建一個自定義屬性存儲表名類:

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

將它添加到您的人類/動物/實體/ DbItem類:

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

並檢索它像這樣:

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

謝謝約翰 - 這種方法似乎對我很好!我將補充我原來的帖子和修改後的代碼。 Best,Žiga – Ziggga

0

你期望在你的第五個案例中發生什麼?

絕對沒有辦法創建一個abstract類的實例。從您的實體類中刪除abstract關鍵字。如果你想阻止Entity類的外部創建,你可以使用一個internal構造函數。

由於這是一個集合,您可能還會使用該集合的第一個條目來獲得DbTable結果 - 可能是危險的,因爲第二個條目可能是另一個類型。

+0

謝謝邁克爾 - 在第五種情況下,我希望得到與第四種情況相同的結果 - 「...實體」... 我無法使用第一個條目獲取DBTable - 集合需要此信息已經得到第一個條目 – Ziggga