How to perform the EXEC task in a “loop” with MSBuild ItemGroups

msbuild

How do I perform the EXEC task in a "loop" with MSBuild ItemGroups?

Instead of repeating this command over and over, like so:

    <Exec ContinueOnError="false" Command="sqlcmd -S $(ServerName) $(SqlServerUser) -d $(DbName) -v TableName=%22account%22 -i data\add_sql_cache.sql -b" />
    <Exec ContinueOnError="false" Command="sqlcmd -S $(ServerName) $(SqlServerUser) -d $(DbName) -v TableName=%22services%22 -i data\add_sql_cache.sql -b" />
    <Exec ContinueOnError="false" Command="sqlcmd -S $(ServerName) $(SqlServerUser) -d $(DbName) -v TableName=%22servicesGroup%22 -i data\add_sql_cache.sql -b" />
    <Exec ContinueOnError="false" Command="sqlcmd -S $(ServerName) $(SqlServerUser) -d $(DbName) -v TableName=%22servicesCategory%22 -i data\add_sql_cache.sql -b" />

I'd rather define an ItemGroup and just execute a "loop". I've got the ItemGroup down:

<ItemGroup>
    <CachedTables Include="account" />
    <CachedTables Include="services" />
    <CachedTables Include="servicesGroup" />
    <CachedTables Include="servicesCategory" />

But due to MSBuild's amazingly unintuitive syntax, I have no idea how to perform the Exec task in a loop with the ItemGroup above as an input.

Best Answer

There are two ways to do this, both are forms of "batching"

You can batch a target and perform the Exec and other operations,

<Target Name="ExecMany"
  Outputs="%(CachedTables.Identity)">
  <Exec
    Command="sqlcmd -S ... TableName=%22%(CachedTables.Identity)%22 -i ..."
    />
  <SomeOtherTask ThatUses="%(CachedTables.Identity)" />
</Target>

The other is to use task batching, just on the Exec task. It is similar,

<Target Name="ExecMany">
  <Exec
    Command="sqlcmd -S ... TableName=%22%(CachedTables.Identity)%22 -i ..."
    />
  <SomeOtherTask ThatUses="%(CachedTables.Identity)" />
</Target>

The difference is how these will operate. With the first, since the batching is for the whole target (achieved with the Outputs attribute), the Exec task, then the SomeOtherTask will execute for each item in the group. In other words,

Exec with "account"
SomeOtherTask with "account"
Exec with "services"
SomeOtherTask with "services"
...

The second options, batching each task separately, would produce the following sequence,

Exec with "account"
Exec with "services"
...
SomeOtherTask with "account"
SomeOtherTask with "services"
...