c# solution to a list of system commands
I currently have a c# utility that runs a very long list of steps, for instance:
- copy a file from here to there i.e.: foreach(file in list) File.Copy ()
- create a bunch of directories foreach (dir in list) Directory.Create
- Loop into a list of directories and create a file. foreach (dir in list) File.Create()
- Take a know list of files and replace some text inside foreach (file in list) Execute ("replace.exe", text1, text2)
etc.
What I did was to take all these steps and create a bunch of methods in c# that do those steps. The advantage of that is, I get to check for each command I run, log any problems and s开发者_JS百科o on. The downside of that is, it is hard coded.
I would like to (and I need to) move those steps out of the code, like in a configuration file or xml file and then read the commands one at a time and run them inside c#. In this way I still have the advantage of checking for return errors, creating logs, etc. but I don't have the actual list and commands hard coded.
I don't want to use a script (like Perl or batch) because I want to be able to monitor some of the steps I do. In other words, I really want to keep the actual execution inside my c# program but have the program being guided by the configuration file that I feed to it.
xml is quite flexible but I am not sure I can do all that with it. For instance how do I turn into xml something like:
list1 = {file1, file2};
list2 = {dir1,dir2,dir3};
foreach (file in list1)
File.Copy(file,dir2\\file);
File.Copy(file,dir3\\file);
Any ideas on what I could/should use to accomplish that?
Thanks Tony
Here is my loop task
[XmlAttribute]
public string DataTableName { get; set; }
public TaskListClass TaskList { get; set; }
public override void Execute(WorkContainer container)
{
int iteration = 0;
string originalTaskName=String.Empty;
// Log Start
base.Execute(container);
// Get the Data Table Based upon the ContainerKey
DataTable loopTable = container.GetKeyValue<DataTable>(DataTableName);
// If not found throw exception and exit
if (loopTable == null)
{
throw new ArgumentException(
"DataTable Not Found or Initialized For Loop Variable",
DataTableName);
}
// For each row returned run the task in order as a mini-process
foreach (DataRow row in loopTable.Rows)
{
iteration++;
foreach (TaskBase task in TaskList.Tasks)
{
// If it is the first iteration then create a new string with the iteration number
// else just replace the last iteration number with the current iteration number
if (iteration == 1)
{
task.Name = String.Format("Iteration {0} - {1}", iteration, task.Name);
}
else
{
task.Name = task.Name.Replace(String.Format("Iteration {0}",iteration-1),String.Format("Iteration {0}",iteration));
}
// Call the task Constructor to Initialize the object
task.GetType().GetConstructor(new Type[0]).Invoke(null);
foreach (DataColumn dc in loopTable.Columns)
{
// Store in Stack Variable
string propertyName = dc.ColumnName;
// If a field in the table matches a
// property Name set that property
PropertyInfo propInfo = task.GetType().GetProperty(propertyName);
if (propInfo != null)
{
propInfo.SetValue(task, row[dc], null);
}
else
{
// Else if the task has a Parameters collection add the
// name of the column and value to the Parameter Collection
propInfo = task.GetType().GetProperty("Parameters");
if (propInfo != null)
{
List<Parameter> parameters = propInfo.GetValue(task, null) as List<Parameter>;
// If the parameter Name already Exist then Override it
// This means that the values in the DataTable override the values in the XML
if (parameters.Contains(new Parameter { Name = propertyName }))
{
parameters.Remove(new Parameter { Name = propertyName });
}
parameters.Add(new Parameter { Name = propertyName, Value = row[dc].ToString(), Type="String" });
}
else
{
ParameterNotFoundException pExp = new ParameterNotFoundException(propertyName, task.GetType().ToString());
throw new TaskException("Unable to assign a Parameter", pExp.Message, pExp);
}
}
}
task.Execute(container);
}
}
base.Finish(container);
}
The XML looks something like this
<LoopTask Name="Loop Report Creation" DataTableName="[? DistributionList ?]">
<TaskList>
<ReportBatchTask Name="Report In Loop">
<ConnectionName>xxx</ConnectionName>
<Parameters />
</ReportBatchTask>
</TaskList>
</LoopTask>
To do what you're asking, you would have to have each possible action defined in code, so if you create a new action, that would require editing your program. There shouldn't be any problem configuring existing actions any way you want, though.
<TaskList>
<MyTask>
<Action type="CreateDirectory">
<target>dir1</target>
<target>dir2</target>
</Action>
</MyTask>
<MyTask>
<SourceFiles>
<file>mypath\file1.txt</file>
<file>mypath\file2.txt</file>
</SourceFile>
<Action type="Replace">
<originalText>Text to replace</originalText>
<replacementText>Some new text</replacementText>
</Action>
<Action type="Copy">
<target>dir1</target>
<target>dir2</target>
</Action>
</MyTask>
</TaskList>
If you had a separate class for each possible Action
your would then create and execute an appropriate Action
using what you read in from the XML.
Yes I have done a similar project: Here are some idea's without having to type the whole thing.
I created Tasks and every Task inherieted from Task class for basic logging and
public virtual void Execute(WorkContainer container)
But every class did something so the Copy Class would have a source, destination and overwrite properties.
Then I would create a Task List which was a List of Task that could then be serialized into xml
would look something like
<TaskList>
<TransactionSqlTask Name="Get Interest Register Data">
<Parameters>
<Parameter>
<Type>String</Type>
<Value>M</Value>
<Name>PaymentInd</Name>
</Parameter>
</Parameters>
<DataTableName>
<string>InterestRegisterData</string>
</DataTableName>
<IgnoreOutputDataTable>false</IgnoreOutputDataTable>
<StoredProcName>proc_Report_NotesInterestRegister</StoredProcName>
<ConnectionName>xxx</ConnectionName>
</TransactionSqlTask>
...
That Xml would be a command line argument which would be read in, deserialized and turn back into a List of Task
Then I would just loop through the List and Call .Execute
Now there was also a Work Container to pass values from one task to another. See this post
Using Generics to return a literal string or from Dictionary<string, object>
I can provide more detail later, right now I do not have the project infront of me but hopefully this gave you some ideas.
Have you considered rewriting your functions as MSBuild tasks?
That way you can leverage the pre-existing msbuild tasks, and write your script as an msbuild file (which fits nicely in any build process). You can even debug MSBuild tasks
精彩评论