2017-02-19 70 views
0

我的數據訪問層我想使用DTO並確保所有網關(表數據網關)都將實現返回給定網關特定DTO列表的方法。 要做到這一點,我創建接口這樣確保所有類將共享相同的方法和字段

public interface IDataTransferObject{} 
所有的DTO像我的所有網關此

public class UserAccountTypeDTO : IDataTransferObject 
{ 
    public int Id { get; set; } 
    public int AccountTitle { get; set; } 
    public int CreditTypeId { get; set; } 
    public bool Active { get; set; } 
} 

然後我做了通用接口實現這個接口

public interface IDefaultGateway<D> where D:IDataTransferObject 
{ 
    List<D> Read(SqlDataReader sqlReader); 
} 

並最終通過我的網關實現了IDefaultGateway

class UserAccountTypeGW : IDefaultGateway<UserAccountTypeDTO> 
{ 
    private const string Table = " UserAccountType "; 

    private string SelectAll = "select * from" + Table + ""; 
    private string SelectById = ...; 


    public List<UserAccountTypeDTO> Read(SqlDataReader sqlReader) 
    { 
     throw new NotImplementedException(); 
    } 
} 

可以讓空接口只是以某種方式將類組合在一起即使它們不共享任何行爲?

網關中的字段是相同的,只有它的值在變化。我做了複製和粘貼到每個網關的字段,我想知道是否有更快/更懶的方式如何做到這一點。

  • 接口和字段不是朋友,因此它可能已被定義爲屬性。由於需要在每個DTO中創建構造函數來初始化屬性,並且設置爲private,所以它比我所做的要糟糕。
  • 抽象類幾乎與界面相同
  • 創建基類,定義字段和方法以及在網關構造函數中初始化(或覆蓋)它們在這種情況下更好還是與我所做的幾乎相同?
+0

爲什麼所有的類都需要共享相同的方法和字段?什麼代碼會使用'IDataTransferObject'或'IDefaultGateway '?看起來他們只是提醒程序員在代碼中遵循特定的模式,但是沒有人會真正通過你描述的接口或基類來使用它們。 –

+0

是的,他們在那裏提醒我不要忘記那種方法。 – Bendom

回答

3

您需要搜索的單詞是Marker Interfaces。這是MSDN的指導原則:

避免使用標記接口(沒有成員的接口)。 如果您需要將類標記爲具有特定特徵(標記),則通常使用自定義屬性而不是接口。

話雖如此,檢查一個類是否實現接口比檢查它是否具有屬性更容易。

而作爲@Groo在這個答案的註釋部分,這是一個非常好的點提到:

標記接口也至少給了一些編譯時檢查,相較於屬性

+2

與屬性相比,標記接口還至少可以提供一些編譯時檢查。 – Groo

+0

@Groo這是一個很好的觀點。謝謝。 – CodingYoshi

+0

所以它是正確和方便之間的權衡。非常感謝你。我發現關於抽象類的兩個評論都很有用,但我將其標記爲正確的答案。 – Bendom

1

關於問題的第二部分(如何避免代碼重複),這是抽象類的用途。

您可以提供,如果需要其派生類中的某些默認值可以覆蓋:

abstract class BaseGateway<T> : IDefaultGateway<T> where T : IDataTransferObject 
{ 
    readonly string _tableName; 
    readonly string _selectAll; 

    public BaseGateway() 
    { 
     // default table name 
     _tableName = this.GetType().Name.Replace("DTO", ""); 
     _selectAll = $"select * from {_tableName}"; 
    } 

    // these members are virtual, so that they can be overriden 
    protected virtual string TableName => _tableName; 
    protected virtual string SelectAll => _selectAll; 

    // derived classes should implement their own 'Read' method 
    public abstract List<T> Read(IDataReader sqlReader); 
} 

但請注意,在做這樣的東西,使你的代碼容易受到SQL注入攻擊。使用ORM或至少一個像「Dapper」這樣的「微」ORM將是一個更好的主意。

小巧玲瓏給你這種簡單的幾個平原IDbConnection擴展方法:使用這些標記接口

public class Dog 
{ 
    public int Age { get; set; } 
    public string Name { get; set; } 
}    

using (IDbConnection conn = OpenConnection()) 
{ 
    var dog = conn 
     .Query<Dog>("Select * from Dog where Age = @Age", new { Age = 10 }) 
     .FirstOrDefault(); 
} 
+0

謝謝,這看起來不錯。我可以問你什麼時候/如何使用SQL注入?我不認爲這是不可能的,但我不熟悉這個問題,我不明白怎麼可能捕獲我發送到數據庫的sql命令並進行更改。 我使用SqlCommand及其屬性Parameters來設置where子句params。我聽說它應該照顧危險的投入。 – Bendom

+0

@Bendom:哦,沒問題,我只是想確保你和其他讀者都知道它,因爲你似乎會自己構建大量的sql字符串。參數化查詢阻止注入,而Dapper也自動使用返回的數據填充類,並且[這樣做非常快](https://www.exceptionnotfound.net/dapper-vs-entity-framework-vs-ado-net-performance -benchmarking /)。 – Groo

1

避免。現在使用標記接口的唯一「好」理由是如果你打算使用反射來操縱某些類型的對象(而這次不是這種情況)。

在這種情況下 - 抽象類似乎是最好的選擇。在抽象類和屬性中定義Read()方法及其默認實現。根據需要用virtual方法覆蓋所有後代。這樣,您甚至不需要包裝類來讀取每個特定類型的對象,因爲您可以直接引用該對象並直接調用Read()

相關問題