我想构建类似管道的东西,我可以将命令(转换器,读取器,编写器......)放在一起,就像线性工作流程一样 并通过命令的给定参数连接它们。一切都应该是可序列化的,并且必须使用干净的xml, 因为第一步没有计划图形编辑器,所以编辑了xml(在VS中使用生成的模式和智能感知支持这并不痛苦。)
示例:
<DataCommand name="AboFileReader">
<connection>
<parameter name="filename" direction="in" type="string">
<parameter name="tabledata" direction="out" type="DataTable">
</connection>
</DataCommand>
<DataCommand name="TrimConverter">
<connection>
<param name="tabledata" direction="in" type="DataTable" >
<param name="tabledata" direction="out" type="DataTable" >
</connection>
</DataCommand>
<DataCommand name="AboDataConverter">
<connection>
<param name="tabledata" direction="in" type="DataTable" >
<param name="tabledata" direction="out" type="DataTable" >
</connection>
</DataCommand>
<DataCommand name="AboSqlWriter">
<connection>
<param name="tabledata" direction="in" type="DataTable" >
</connection>
</DataCommand>
我可以通过三种不同的方式来实现它,所有这些方式都有利有弊,我不知道选择哪种方式。
首先我认为1.是要走的路,但后来我提出了解决方案3.因为它提供了一个干净的xml并且是本机可序列化的。 对于下一步(编辑器),绑定到GUI应该很容易,而做反射的属性网格不是我的第一选择(反射总是很难看),有一组参数可以很容易地绑定到GUI。
也许组合1和3是要在启动时收集集合中的所有参数?
1。数据注释
[Parameter(Direction = "In", Description="...")]
public string FileName {get; set;}
[Parameter(Direction = "Out", Description="...")]
public DataTable Table {get; set;}
优点: +本机参数被暴露 +编码时的intellisense
缺点: - 原生序列化? - 丑陋的xml?
2。 Windows Workflow Foundation样式
public InParameter<string> FileName {get; set;}
public OutParameter<DataTable> Table {get; set;}
优点: 编码时+ intellisense +参数被暴露 +序列化工作
缺点: - 丑陋的xml?
第3。集合填写了构造函数
ObservableCollection<Parameter> Parameters {get; set;}
public Init()
{
Parameters.Add( new Parameter() { Name="FileName", Type = typeOf(string), Direction="In", Description="..." } );
Parameters.Add( new Parameter() { Name="Table", Type = typeOf(DataTable), Direction="Out", Description="..." } );
}
优点: +序列化工作 + clean xml
缺点: - 参数未在源代码中公开 - 编码时无智能感知
您的经历是什么,前进的方向是什么?
答案 0 :(得分:0)
就个人而言,我会选择一个不那么详细的XML表示,例如:
<ReadFile filename="..." />
<Trim />
<Convert />
<WriteSQL />
我假设大多数操作都有特定的输入和输出类型,所以除非这是可配置的,否则不需要拼写出来。这消除了一堆属性,但不是全部,所以你的问题仍然存在。理想情况下,您需要的是:
由于数字1,您将要使用属性。由于数字2,您需要自动确定需要序列化哪些属性 - 反射所在的位置。由于在数字3中,您只想序列化那些可配置的字段 - 因此您需要一些东西来区分这些属性。
使用属性来指示要公开的属性是一个好主意,因为它清楚地传达了他们的意图。它还允许您忽略其他属性,这样可以保持序列化格式的清晰。您也可以选择仅序列化公共属性,但属性可以提供更大的灵活性。最糟糕的是,它们会使代码更加混乱。
以下是我如何设置基础:
public interface IOperation
{
Type InputType { get; }
Type OutputType { get; }
object GetOutput(object input);
}
每个操作都提供自己的IOperation
接口实现:
public class ReadFileOperation : IOperation
{
[Parameter]
public string Filename { get; set; }
public Type InputType { get { return null; } } // No input
public Type OutputType { get { return typeof(DataTable); } }
public object GetOutput(object input)
{
// TODO: Read file into DataTable here!
}
}
如果您的操作可以配置为使用不同的输入或输出类型,那么提供InputType
或OutputType
setter和[Parameter]
属性应该可以正常工作。< / p>
您可以编写单个函数来查找实现IOperation的所有类型,并存储每种类型的属性列表。然后,在解析或写入时,您将查找当前类型并使用其属性列表。这个函数只需要调用一次,如果性能确实是一个问题,你可以使用它的输出在你的应用程序启动时生成解析/编写代码,但除非我真的不得不这样做,否则我不会这样做
public Dictionary<Type, List<PropertyInfo>> DetectParameterProperties()
{
Dictionary<Type, List<PropertyInfo>> lookup = new Dictionary<Type, List<PropertyInfo>>();
IEnumerable<Type> operations = Assembly
.GetAssembly(typeof(IOperation))
.GetTypes()
.Where(t => t.IsClass && !t.IsAbstract && typeof(IOperation).IsAssignableFrom(t));
foreach (Type operation in operations)
{
lookup[operation] = new List<PropertyInfo>();
IEnumerable<PropertyInfo> parameters = operation.GetProperties().Where(p => p.CustomAttributes.Any(a => a.AttributeType == typeof(ParameterAttribute)));
foreach (PropertyInfo parameter in parameters)
lookup[operation].Add(parameter);
}
return lookup;
}
请注意,随着您提供更多配置选项,错误处理变得更加重要。一个简单的类型检查,以确保所有输入和输出类型匹配是好的,但有些操作可能需要自己检查,以确保它们不会以其他方式配置错误。