2013-10-24 26 views
2

好吧,我想我有一些重複的代碼可以使用泛型。如何實現使用Linq和XML的泛型方法

我有兩個不同的Xml文件,我打開,查詢並返回爲綁定到GridViews的集合。這些集合是使用xml中的數據填充的自定義類的列表。每個gridview都有其相應的自定義類。目前我有兩個,並說這些類的名稱是XmlDataSource1XmlDataSource2

下面是當前使用XmlDataSource1作爲示例的工作示例。請注意,XmlDataSource1對象的構造函數從查詢中獲取XElements並填充自身。沒有瘋狂。

GridView gv = new GridView(); 
gv.DataSource = GetXmlDataSource1(pathToXmlFile); 
gv.DataBind(); 

public List<XmlDataSource1> GetXmlDataSource1(string pathToXmlFile) 
{ 
    XDocument xml = XDocument.Load(pathToXmlFile); 
    IEnumerable<XmlDataSource1> query = from s in xml.Descendants("NodeForXml1") 
             select new XmlDataSource1(s); 

    // Where clauses based on user inputs (deferred execution) 
    query = query.Where(x => x.ID = SomeUserInputId); 

    // More of these where clauses if they have inputs for them... 

    // Convert to a List and return 
    return query.ToList(); 
} 

現在,要實現GetXmlDataSource2()方法,它就像98%一樣。當然,主要不同之處在於,linq查詢的select部分創建XmlDataSource2對象的新實例,「NodeForXml2」後代目標節點,以及某些應用/不適用的子句。

如何讓這些GetXmlDataSource#方法具有通用性?理想情況下,我想調用它,如下所述,這是我所嘗試的,但我不能讓linq查詢的選擇部分調用正確的數據對象的構造函數。

GridView gv1 = new GridView(); 
GridView gv2 = new GridView(); 

gv1.DataSource = GetXmlDataSource<XmlDataSource1>(pathToXmlFile); 
gv2.DataSource = GetXmlDataSource<XmlDataSource2>(pathToXmlFile); 

gv1.DataBind(); 
gv2.DataBind(); 

public List<T> GetXmlDataSource<T>(string pathToXmlFile) 
{ 
    // The type of T in case I need it 
    Type typeOfT = typeof(T); 

    XDocument xml = XDocument.Load(pathToXmlFile); 

    // How to make new XmlDataSource1 and 2 objects?? This statement doesn't work. 
    IEnumerable<T> query = from s in xml.Descendants("NodeForXml1") 
          select new T(s); 



    // How to return the IEnumerable query to a List of the T's? 
    return query.ToList(); 
} 

我有多遠?我關門了嗎?

+0

嗯,你是什麼意思「我不能得到選擇部分的linq查詢來調用正確的數據對象的構造函數」 ...?你有錯誤嗎? –

+0

@JeffBridgman是的,我得到了一個異常,因爲它沒有調用相應的構造函數來傳遞T.我從AirL實現了下面提到的方法,並且這些構造函數被激發。不過,我現在正在閱讀有關可能的性能問題,看看這是否是最好的方法。 – bdizzle

回答

2

一種解決方案可能是使用Activator.CreateInstance

IEnumerable<T> query = from s in xml.Descendants("NodeForXml1") 
             select (T)Activator.CreateInstance(typeOfT, s); 

但性能問題提防,here是喬恩斯基特一個偉大的職位和它打交道:

顯然,這不是調用委託慢 - 大概是由於 試圖找到一個帶反射的可訪問構造函數,並調用 它

因此,如果性能是一個需求,最好將代理傳遞給您的GetXmlDataSource1方法,並使用它在您的Linq查詢中創建需要的實例。

關於您需要訪問XmlDataSources公共屬性分爲GetXmlDataSource<T>方法,你至少有2個解決方案:

:創建一個接口包含公共屬性:

public interface IXmlDataSource 
{ 
    string ID { get; set; } 
    string CommonProperty1 { get; set; } 
    string CommonProperty2 { get; set; } 
} 

這一次將實施由你的XmlDataSources。下面是一個典型的實現:

public class XmlDataSource1 : IXmlDataSource 
{ 
    public string ID { get; set; } 
    public string CommonProperty1 { get; set }  
    public string CommonProperty2 { get; set } 

    ... // the rest of your code 
} 

最後,constrainingT型將給予您訪問這些屬性,當你需要他們query = query.Where(x => x.ID = SomeUserInputId);

public List<T> GetXmlDataSource<T>(string pathToXmlFile) where T : IXmlDataSource 

:委託也可以做的伎倆,這裏是典型的呼叫:

GetXmlDataSource<XmlDataSource1>(pathToXmlFile, (query, result) => 
{ 
    return query.Select(e => new XmlDataSource1(e)).Where(x => x.YourProperty == value); 
}); 

With a GetXmlDataSource<T>那會是什麼樣子的方法簽名:

public List<T> GetXmlDataSource<T>(string pathToXmlFile, Func<IEnumerable<XElement>, IEnumerable<T>> transform) 
{ 
    // The type of T in case I need it 
    Type typeOfT = typeof(T); 

    XDocument xml = XDocument.Load(pathToXmlFile); 

    IEnumerable<XElement> query = from s in xml.Descendants("NodeForXml1") 
           select s; 

    // Create and filter XmlDataSource1 instances thanks to the "transform" delegate 
    return transform(query).ToList(); 
} 
+0

是的,那是我編輯它的錯誤,謝謝。我嘗試了Activator.CreateInstance()方法,並且執行了它們的構造函數。打算玩一下,看看where子句是如何表現這種方法的。 – bdizzle

+0

我必須承認@D斯坦利提出了一個非常好的答案。如果性能是一項要求,傳遞委託肯定是實現您所需要的最佳方式。即使有附加參數,語法仍然很輕。 – AirL

+0

任何想法如何在where子句中訪問T的屬性?例如,在OP中:query = query.Where(x => x.ID = SomeUserInputId);我無法再訪問x.ID,因爲它在T中無法訪問? – bdizzle

2

它看起來像你接近 - 一種選擇是有呼叫者傳遞一個函數來創建實例:

public List<T> GetXmlDataSource<T>(string pathToXmlFile, 
            string elementName,  
            Func<XElement,T> factoryMethod) 
{ 
... 
    IEnumerable<T> query = from s in xml.Descendants(elementName) 
             select factoryMethod(s); 
... 
} 

然後調用者會說:

List<XmlDataSource1> list = GetXmlDataSource1(pathToXmlFile, 
               "NodeForXml1", 
               s => new XmlDataSource1(s)) 
+0

嗯,我無法得到這個工作。我在linq查詢中的「select factoryMethod(s)」調用中收到錯誤,指出它有一些無效的參數。 – bdizzle

+0

我的函數定義錯誤 - 請參閱我的編輯。 –