我有标准的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);
//...
}
}
谢谢
答案 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);
}