c#按名称实例化映射类

时间:2013-12-03 21:03:28

标签: c# class map factory

我有标准的XML数据,代表客户的采购订单。每个客户都会以不同方式填充XML数据,因此我需要一个单独的方法来根据其规范处理订单。我的目标是使这个可扩展,所以我使用了一个接口,因为我希望能够在添加新客户时创建其他类。

如何根据客户选择不同的Map类?

public class XmlPurchaseOrder
{
    public DateTime Created { get; set; }
    public string CustomerId { get; set; }
    public string PurchaseOrderId { get; set; }
    public string MapName { get; set; }
    //...
}

public interface IXmlMapper
{
     CustomerOrder MapToCustomerOrder(XmlPurchaseOrder po);
}

public class  CustomerOrder
{
    public int Id { get; set; }
    public string CustomerId { get; set; }
    public string CustomerPoId { get; set; }
    public DateTime OrderDate { get; set; }
}

//Maps by customer

public class McClownMap : IXmlMapper
{
    public CustomerOrder MapToCustomerOrder(XmlPurchaseOrder po)
    {
        return  new CustomerOrder()
            {
                CustomerId = "McD123",
                CustomerPoId = po.PurchaseOrderId,
                OrderDate = DateTime.Today
            };
    }
}

public class BkMap : IXmlMapper
{
    public CustomerOrder MapToCustomerOrder(XmlPurchaseOrder po)
    {
        return new CustomerOrder()
        {
            CustomerId = "BxK331",
            CustomerPoId = string.Format("BxK{0}", po.PurchaseOrderId),
            OrderDate = DateTime.Today.AddDays(-1)
        };
    }
}

public class TacoWorldMap : IXmlMapper
{
    public CustomerOrder MapToCustomerOrder(XmlPurchaseOrder po)
    {
        return new CustomerOrder()
        {
            CustomerId = "TW-33",
            CustomerPoId = string.Format("{0}-{1}",po.PurchaseOrderId, DateTime.Now.Ticks), 
            OrderDate = po.Created
        };
    }
}

class Program
{
    private static void Main(string[] args)
    {
        const string xmlFile = "CustomerPo.xml";
        var objStreamReader = new StreamReader(xmlFile);
        var xmlData = new XmlSerializer(new XmlPurchaseOrder().GetType());
        var po = (XmlPurchaseOrder)xmlData.Deserialize(objStreamReader);
        objStreamReader.Close();


        //How do I create the associated class by the MapName specified.

        IXmlMapper t = Activator.CreateInstance(Type.GetType(po.MapName));

        var customerOrder = t.MapToCustomerOrder(po);
        //...
    }
}

谢谢

4 个答案:

答案 0 :(得分:0)

也许您可以拆分工作负载,以便您的反序列化程序根据确定采购订单类型的特征使用XmlPurchaseOrder(枚举)装饰PurchaseOrderType。如果这是由XML结构本身确定的,比如通过标签或属性,这是一个简单的任务 - 否则将XmlPurchaseOrder子类化,并引入一个“计算”类型的虚拟方法。

这项工作的另一部分是实例化具体PurchaseOrder - 这可以使用Factory为每种采购订单使用一种Create方法进行简化,或者使用{{1}上的大开关进行简化。 1}}枚举。

答案 1 :(得分:0)

这让我想到了通过.Net提供的提供者模型。我目前正在使用它根据其提供者类型实例化不同的API提供程序。

您可以设置从ProviderBase继承的几乎无限数量的不同类,并添加此类所需的任何方法。然后,您创建每个.dll以执行您需要的任何功能,并且因为它们都从一些类似的基类继承而来,您可以将主方法开始处理其中的功能。

基类:

namespace ProviderManager
{ 
  abstract public class SendProviderBase : ProviderBase
  {
    abstract public void Process(whatever args you need);
  }
}

用于实例化不同提供者的助手类

namespace ProviderManger
{
public class ProviderManger
{
  private ConfigHandler sendConfig;

  public ProviderManger()
  {
    sendConfig = ConfigurationManger.GetSection("sendProvider") as ConfigHandler;
  }

  public SendProviderBase GetSendProviderBase(string MapName)
  {
    try 
    {
      ProviderSettings settings = sendConfig.Providers[MapName];
      return (SendProviderBase)ProvidersHelper.InstantiateProvider(settings, typeof(SendProviderBase));
    }
    //appropriate catch block and whatever else
}}

ConfigHandler代码

namespace ProviderManger
{
  class ConfigHandler : ConfigurationSection
  {
    [ConfigurationProperty("providers")}
    public ProviderSettingsCollection Providers
    {
      get
      { return base["providers"] as ProviderSettingsCollection; }
}}}

在Main中使用

providerManager = new ProviderManager();
SendProviderManger provider = providerManager.GetSendProviderBase(MapName);
provider.Process(whatever args...);

显然你可以将SendProviderBase重命名为与你正在做的事情更相关的东西,但我保留了这个名字,因为它在我的代码中是一致的。您需要的另一件事是声明.config部分用于存储映射到与之相关的.dll的MapNames。由于我的应用程序是一个Web服务,因此我们有一个web.config,其中包含以下部分: 自定义章节声明:

<configSections>
  <section name="sendProvider" type="KC.ProviderManager.ConfigHandler, ProviderManager"/>
</configSections>

发送提供商部分:

<sendProviders>
  <providers>
    <add name="MapNameX" type="namespace.classname, assemblyname">

所以基本上它的作用是你在web.config中提供providerManger.GetSendProviderBase(MapNameX)名称,它会返回给你(假设其他一切都是正确构建的)在该程序集中找到的类。然后,您可以调用基类上找到的方法来开始处理(provider.Process())。

其他必要的参考文献如下

System.Reflection;
System.Configuration;
System.Configuration.Provider;
System.Web.Configuration;

这是高度可扩展的,因为您可以根据需要添加任意数量的提供程序,只要它们正确继承

或者,对于更简化但仍然相当可扩展的解决方案,请查看此link

答案 2 :(得分:0)

一种非常简单的方法是为每个客户添加一个配置设置,映射到用于处理订单的类型。

<appSettings>
<add key="Customer1" value="MyApp.Logic.Customer1Processor" />
<add key="Customer2" value="MyApp.Logic.Customer2Processor" />
//etc...
</appSettings>

然后像你当前一样使用Activator.CreateInstance。

答案 3 :(得分:0)

我做了一些进一步的研究,我需要的是工厂。这是我对Design Patterns Library

提出的名为David Starr的Pluralsight.com视频中的演示的解释
public class CustomerMapFactory
    {
            private Type[] _mapTypes;

            public CustomerMapFactory()
            {
                LoadAvailableMaps();
            }
            //Return a newly created Type
            public IXmlMapper CreateInstance(string customerId)
            {
                var t = GetTypeToCreate(customerId);
                if (t == null) throw new Exception("Customer map not found");
                return Activator.CreateInstance(t) as IXmlMapper;
            }

            //Find the map to instantiate
            Type GetTypeToCreate(string customerId)
            {
                return _mapTypes.FirstOrDefault(tpMap => tpMap.Name.Contains(customerId));
            }

            //Identify all Types that use the IXmlMapper
            private void LoadAvailableMaps()
            {
                _mapTypes = Assembly.GetExecutingAssembly()
                                    .GetTypes()
                                    .Where(t => t.GetInterface(typeof(IXmlMapper).ToString()) != null)
                                    .ToArray();
            }
        }
    }

这是利用工厂的程序

    class Program
    {
        private static void Main(string[] args)
        {
            //Same as above
            const string xmlFile = "CustomerPo.xml";
            var objStreamReader = new StreamReader(xmlFile);
            var xmlData = new XmlSerializer(new XmlPurchaseOrder().GetType());
            var po = (XmlPurchaseOrder)xmlData.Deserialize(objStreamReader);
            objStreamReader.Close();

        //Now utilizing the factory.
        var mf = new CustomerMapFactory();
        var poMap = mf.CreateInstance("BkMap");
        var customerOrder = poMap.MapToCustomerOrder(po);

    }