在MSBuild任务中的Hashtable/Dictionary参数

3

我想在 MSBuild 任务间发送/共享 DictionaryHashtable

我有以下两个自定义任务:Get 生成 Hashtable,Set 应该使用它。

Get.cs

public class Get : Task
{
    [Output]
    public Hashtable Output { get; set; }

    public override bool Execute()
    {
        Output = new Hashtable();
        return true;
    }
}

Set.cs

public class Set : Task
{
    [Required]
    public Hashtable Output { get; set; }

    public override bool Execute()
    {
        var items = Output.Cast<DictionaryEntry>().ToDictionary(d => d.Key.ToString(), d => d.Value.ToString());

        foreach(var item in items)
        {
            //Do Something
        }

        return true;
    }
}

以下类可以很好地构建成Assembly.dll
然后,我使用上述的Assembly.dll在以下构建目标脚本中调用Get和Set自定义任务: MyTarget.targets
<?xml version="1.0" encoding="utf-8"?>
<Project xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
  <UsingTask TaskName="Get" AssemblyFile=".\Assembly.dll"/>
  <UsingTask TaskName="Set" AssemblyFile=".\Assembly.dll"/>

  <Target Name="Get">

    <Get>
      <Output TaskParameter="Output" ItemName="Output" />
    </Get>
    <Set Output=@(Output) />

  </Target>

</Project>

当我使用上述目标构建项目时,MSBuild显示以下错误:

"Get"任务的“Output”参数的类型“System.Collections.Hashtable”不受MSBuild支持。

我怎样才能在自定义MSBuild任务的属性中使用Hashtable或Dictionary?

2个回答

6
任务中可以放置或输出的参数仅限于 ITaskItem 或者 ITaskItem 数组。因此,您的属性应该从以下形式更改:
public Hashtable Output { get; set; }

to

public ITaskItem[] Output { get; set; }

为了满足这个要求,接下来你需要一个实现ITaskItem的实现类。这将允许你处理你的哈希集或字典。我留给你自己添加,但最简单的KeyValue类可能是这样的:

to match that requirement.

public class KeyValue: ITaskItem
{
    string _spec = String.Empty;

    public KeyValue(string key, string value)
    {
        _spec = key;
        metadata.Add("value", value);
    }

    Dictionary<string,string> metadata = new Dictionary<string,string>();

    public string ItemSpec
    {
        get {return _spec;}
        set {}
    }
    public ICollection MetadataNames
    {
        get {return metadata.Keys;}
    }
    public int MetadataCount
    {
        get {return metadata.Keys.Count;}
    }
    public string GetMetadata(string metadataName)
    {
        return metadata[metadataName];
    }
    public void SetMetadata(string metadataName, string metadataValue) 
    {
        metadata[metadataName] = metadataValue;
    }
    public void RemoveMetadata(string metadataName)
    {
    }
    public void CopyMetadataTo(ITaskItem destinationItem)
    {
    }
    public IDictionary CloneCustomMetadata()
    {
          return metadata;
    }
}

这个类将生成一个Item,如果它在普通的MSBuild脚本中完成,它将看起来像这样:

 <Item Include="key">
    <value>some value</value>
 </Item>

接下来,您可以调整“设置任务”和“获取任务”,以使用这个新类“KeyValue”。
public class Set : Task
{

    TaskLoggingHelper log;

    public Set() {
        log = new TaskLoggingHelper(this);
    }

    [Required]
    public ITaskItem[] Output { get; set; }

    public override bool Execute()
    {
        log.LogMessage("start set");
        foreach(var item in Output)
        {
            log.LogMessage(String.Format("Set sees key {0} with value {1}.",item.ItemSpec, item.GetMetadata("value")));
        }
        log.LogMessage("end set");
        return true;
    }
}

public class Get : Task
{

    // notice this property no longer is called Output
    // as that gave me errors as the property is reserved       
    [Output]
    public ITaskItem[] Result { get; set; }

    public override bool Execute()
    {
        // convert a Dictionary or Hashset to an array of ITaskItems
        // by creating instances of the class KeyValue.
        // I use a simple list here, I leave it as an exercise to do the other colletions
        Result = new List<ITaskItem> { new KeyValue("bar", "bar-val"), new KeyValue("foo","foo val") }.ToArray();
        return true;
    }
}

我用来测试上述代码的构建文件:

<Project xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
  <UsingTask TaskName="Get" AssemblyFile=".\cb.dll"/>
  <UsingTask TaskName="Set" AssemblyFile=".\cb.dll"/>

  <Target Name="Get">

    <Get>
      <Output  TaskParameter="Result"  ItemName="GetResult" />
    </Get>

    <!-- lets see what we've got -->
    <Message Importance="high" Text="key: @(GetResult) :: value: %(value)" />

    <Set Output="@(GetResult)">

    </Set>

  </Target>

</Project>

运行后的结果如下:
Build started 24-12-2017 21:26:17.
Project "C:\Prj\bld\test.build" on node 1 (default targets).
Get:
  key: bar :: value: bar-val
  key: foo :: value: foo val
  start set
  Set sees key bar with value bar-val.
  Set sees key foo with value foo val.
  end set
Done Building Project "C:\Prj\bld\test.build" (default targets).


Build succeeded.
    0 Warning(s)
    0 Error(s)

0
你可以使用TaskItem(String, IDictionary)构造函数,它看起来像这样。
public class Get : Task
{
    [Output]
    public ITaskItem Result { get; set; }

    public override bool Execute()
    {
        var values = new Dictionary<string, string>();
        values["Foo"] = "Bar";
        Result = new TaskItem("Result", values);
        return true;
    }
}

<UsingTask TaskName="Get" AssemblyFile=".\cb.dll"/>

<Target Name="Get">
  <Get>
    <Output TaskParameter="Result" ItemName="ResultItem" />
  </Get>
  <Message Text="%(ResultItem.Foo)" />
</Target>

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接