如何使用Linq和XML实现通用方法方法

时间:2013-10-24 19:32:26

标签: c# .net xml linq

好吧,我想我有一些重复的代码,可以使用泛型。

我有两个不同的Xml文件,我打开,查询和返回绑定到GridViews的集合。这些集合是使用xml中的数据填充的自定义类的列表。每个gridview都有相应的自定义类。目前我有两个,并说这些类的名称是 XmlDataSource1 XmlDataSource2

以下是使用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%一样。当然,主要区别在于创建XmlDataSource2对象的新实例的linq查询的选择部分,“NodeForXml2”后代目标节点,以及一些适用/不适用的where子句。

如何使这些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();
}

我有多远?我接近了吗?

2 个答案:

答案 0 :(得分:2)

一种解决方案可能是使用Activator.CreateInstance

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

但要注意性能问题,here是Jon Skeet处理它的一篇好文章:

  

显然,这比召集代表要慢 - 大概是因为   试图找到一个带有反射和调用的可访问构造函数   它

因此,如果需要性能,最好将委托传递给GetXmlDataSource1方法,并使用它在Linq查询中创建所需的实例。

关于您需要访问XmlDataSources公共属性到GetXmlDataSource<T>方法,您至少有两个解决方案:

1 :创建包含公共属性的界面:

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
}

最后,constraining T类型会授予您在query = query.Where(x => x.ID = SomeUserInputId);

中需要时访问这些属性的权限
public List<T> GetXmlDataSource<T>(string pathToXmlFile) where T : IXmlDataSource

2 :代理人也可以做到这一点,在这种情况下这是一个典型的电话:

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

使用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();
}

答案 1 :(得分: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))