2013-10-24 29 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

我的函数定义错误 - 请参阅我的编辑。 –