我有一个遗留的HTTP/XML服务,我需要与我的应用程序中的各种功能进行交互.
我必须为服务创建各种请求消息,因此为了避免在代码周围乱七八糟的魔术字符串,我决定创建xml XElement片段来创建一个基本的DSL.
例如.
代替...
new XElement("root",
new XElement("request",
new XElement("messageData", ...)));
Run Code Online (Sandbox Code Playgroud)
我打算用:
Root( Request( MessageData(...) ) );
Run Code Online (Sandbox Code Playgroud)
使用Root,Request和MessageData(当然,这些是出于说明目的)定义为静态方法,它们都执行类似于以下操作:
private static XElement Root(params object[] content)
{
return new XElement("root", content);
}
Run Code Online (Sandbox Code Playgroud)
这给了我一个伪功能组合风格,我喜欢这种任务.
我的终极问题实际上是一个理智/最佳实践,所以它可能过于主观,但我很高兴有机会得到一些反馈.
我打算将这些私有方法移动到公共静态类,以便任何想要为服务撰写消息的类都可以轻松访问它们.
我还打算让服务的不同功能将消息由特定的消息构建类创建,以提高可维护性.
这是实现这个简单的DSL的好方法,还是我错过了一些特殊的酱汁,让我能做得更好?
让我怀疑的是,事实上,只要我将这些方法移动到另一个类,我就会增加这些方法调用的长度(当然,我仍然保留了删除大量魔术字符串的初始目标.)我更关心DSL语言类的大小(loc),而不是语法简洁?
请注意,在这种情况下,远程服务实现不当,并且不符合任何通用消息传递标准,例如WSDL,SOAP,XML/RPC,WCF等.
在这些情况下,创建手工构建的消息显然不是明智之举.
在极少数情况下,您必须处理此类服务,并且无论出于何种原因无法重新设计,下面的答案提供了一些处理情况的可能方法.
我注意到这篇关于使用 C#4.0 构建任意 XML 的文章非常棒。
该库的来源在这里 - https://github.com/mmonteleone/DynamicBuilder/tree/master/src/DynamicBuilder
这时有一个显着的缺陷,没有xml命名空间支持。希望这个问题能够得到解决。
作为一个简单的例子,它是如何完成的。
dynamic x = new Xml();
x.hello("world");
Run Code Online (Sandbox Code Playgroud)
其产量:
<hello>world</hello>
Run Code Online (Sandbox Code Playgroud)
这是从文章中摘取的另一个简单示例。
dynamic x = new Xml();
// passing an anonymous delegate creates a nested context
x.user(Xml.Fragment(u => {
u.firstname("John");
u.lastname("Doe");
u.email("jdoe@example.org");
u.phone(new { type="cell" }, "(985) 555-1234");
}));
Run Code Online (Sandbox Code Playgroud)
其产量:
<user>
<firstname>John</firstname>
<lastname>Doe</lastname>
<email>jdoe@example.org</email>
<phone type="cell">(985) 555-1234</phone>
</user>
Run Code Online (Sandbox Code Playgroud)
使用 Ruby 库后,Builder这种创建任意 XML 的方法同样简洁,甚至近乎“有趣”!
我将其标记为答案,因为尽管它没有直接说明“使用 DSL 创建任意 XML”,但由于语法的极其简洁和动态特性,它往往会消除这种需求。
我个人认为,如果您有 v4.0 编译器并且必须手动启动它,那么这是在 C# 中创建任意 XML 的最佳方法,当然还有更好的方法通过序列化自动生成 XML。将此保留给 XML,它必须采用仅适用于遗留系统的特定形式。