C# TPL数据流块从单个输入产生多个输出
我已经开始研究TPL数据流作为处理工作流的解决方案 处理工作流的要点是从多个表中读入输入消息,并从中创建四个反射对象,然后将它们持久化到其他四个表中,因此每个输入消息都应生成四条新消息 我无法确定可以帮助创建四个对象的预定义块中的一个,起初TransformManyBlock似乎是我要寻找的,但它返回相同类型的多个对象,其中我将有四种类型 问题示例 我们有两个表,其中包含来自两个遗留系统的员工详细信息,它们的实体如下所示C# TPL数据流块从单个输入产生多个输出,c#,task-parallel-library,tpl-dataflow,C#,Task Parallel Library,Tpl Dataflow,我已经开始研究TPL数据流作为处理工作流的解决方案 处理工作流的要点是从多个表中读入输入消息,并从中创建四个反射对象,然后将它们持久化到其他四个表中,因此每个输入消息都应生成四条新消息 我无法确定可以帮助创建四个对象的预定义块中的一个,起初TransformManyBlock似乎是我要寻找的,但它返回相同类型的多个对象,其中我将有四种类型 问题示例 我们有两个表,其中包含来自两个遗留系统的员工详细信息,它们的实体如下所示 public partial class EmployeeTblA {
public partial class EmployeeTblA
{
public int Id { get; set; }
public int System { get; set; }
public string Forename { get; set; }
public string Surname { get; set; }
public int Age { get; set; }
public int Number { get; set; }
public string Street { get; set; }
public string PostCode { get; set; }
public virtual EmployeeSystem SystemNavigation { get; set; }
}
public partial class EmployeeTblB
{
public int Id { get; set; }
public int System { get; set; }
public string Name { get; set; }
public int Age { get; set; }
public string Address { get; set; }
public string Postcode { get; set; }
public virtual EmployeeSystem SystemNavigation { get; set; }
}
public class BaseEmployee
{
public int Id { get; set; }
public int System { get; set; }
public string Name { get; set; }
public int Age { get; set; }
public string Address { get; set; }
public string Postcode { get; set; }
}
public partial class EmployeeName
{
public int Id { get; set; }
public int System { get; set; }
public int LegacyId { get; set; }
public string Name { get; set; }
public virtual EmployeeSystem SystemNavigation { get; set; }
}
public partial class EmployeeAge
{
public int Id { get; set; }
public int System { get; set; }
public int LegacyId { get; set; }
public int Age { get; set; }
public virtual EmployeeSystem SystemNavigation { get; set; }
}
public partial class EmployeeAddress
{
public int Id { get; set; }
public int System { get; set; }
public int LegacyId { get; set; }
public string Address { get; set; }
public string Postcode { get; set; }
public virtual EmployeeSystem SystemNavigation { get; set; }
}
我们希望从两个系统获取数据,并将数据放入我们闪亮的新系统中,为此,我们需要将旧系统中的实体转换为新系统中使用的实体。首先,我们将旧系统中的实体转换为如下所示的基类
public partial class EmployeeTblA
{
public int Id { get; set; }
public int System { get; set; }
public string Forename { get; set; }
public string Surname { get; set; }
public int Age { get; set; }
public int Number { get; set; }
public string Street { get; set; }
public string PostCode { get; set; }
public virtual EmployeeSystem SystemNavigation { get; set; }
}
public partial class EmployeeTblB
{
public int Id { get; set; }
public int System { get; set; }
public string Name { get; set; }
public int Age { get; set; }
public string Address { get; set; }
public string Postcode { get; set; }
public virtual EmployeeSystem SystemNavigation { get; set; }
}
public class BaseEmployee
{
public int Id { get; set; }
public int System { get; set; }
public string Name { get; set; }
public int Age { get; set; }
public string Address { get; set; }
public string Postcode { get; set; }
}
public partial class EmployeeName
{
public int Id { get; set; }
public int System { get; set; }
public int LegacyId { get; set; }
public string Name { get; set; }
public virtual EmployeeSystem SystemNavigation { get; set; }
}
public partial class EmployeeAge
{
public int Id { get; set; }
public int System { get; set; }
public int LegacyId { get; set; }
public int Age { get; set; }
public virtual EmployeeSystem SystemNavigation { get; set; }
}
public partial class EmployeeAddress
{
public int Id { get; set; }
public int System { get; set; }
public int LegacyId { get; set; }
public string Address { get; set; }
public string Postcode { get; set; }
public virtual EmployeeSystem SystemNavigation { get; set; }
}
然后我们想从基类中创建三个新对象,它们表示新系统的实体,如下所示
public partial class EmployeeTblA
{
public int Id { get; set; }
public int System { get; set; }
public string Forename { get; set; }
public string Surname { get; set; }
public int Age { get; set; }
public int Number { get; set; }
public string Street { get; set; }
public string PostCode { get; set; }
public virtual EmployeeSystem SystemNavigation { get; set; }
}
public partial class EmployeeTblB
{
public int Id { get; set; }
public int System { get; set; }
public string Name { get; set; }
public int Age { get; set; }
public string Address { get; set; }
public string Postcode { get; set; }
public virtual EmployeeSystem SystemNavigation { get; set; }
}
public class BaseEmployee
{
public int Id { get; set; }
public int System { get; set; }
public string Name { get; set; }
public int Age { get; set; }
public string Address { get; set; }
public string Postcode { get; set; }
}
public partial class EmployeeName
{
public int Id { get; set; }
public int System { get; set; }
public int LegacyId { get; set; }
public string Name { get; set; }
public virtual EmployeeSystem SystemNavigation { get; set; }
}
public partial class EmployeeAge
{
public int Id { get; set; }
public int System { get; set; }
public int LegacyId { get; set; }
public int Age { get; set; }
public virtual EmployeeSystem SystemNavigation { get; set; }
}
public partial class EmployeeAddress
{
public int Id { get; set; }
public int System { get; set; }
public int LegacyId { get; set; }
public string Address { get; set; }
public string Postcode { get; set; }
public virtual EmployeeSystem SystemNavigation { get; set; }
}
上述示例中我的第三方物流的大致流程
我知道我可以创建一个自定义块,但我不知道如何使用它来使用数据流向四个单独的链接ActionBlock提供输出,有人能给我指出正确的方向吗?广播块是我最终使用的组件,我使用它将BaseEmployee对象广播到其他输出流,分离出我需要创建的反射对象 完整管道如下所示
_transEmployeeA = new TransformBlock<EmployeeTblA, BaseMsg>((input) =>
{
return new BaseMsg()
{
Id = input.Id,
System = input.System,
Name = string.Concat(input.Forename, " ", input.Surname),
Age = input.Age,
Address = string.Concat(input.Number, " ", input.Street),
Postcode = input.PostCode
};
});
_transEmployeeB = new TransformBlock<EmployeeTblB, BaseMsg>((input) =>
{
return new BaseMsg()
{
Id = input.Id,
System = input.System,
Name = input.Name,
Age = input.Age,
Address = input.Address,
Postcode = input.Postcode
};
});
_broadcastBaseMsg = new BroadcastBlock<BaseMsg>(null);
_transEmployeeName = new TransformBlock<BaseMsg, EmployeeName>((baseMsg) =>
{
return new EmployeeName()
{
System = baseMsg.System,
LegacyId = baseMsg.Id,
Name = baseMsg.Name
};
});
_transEmployeeAge = new TransformBlock<BaseMsg, EmployeeAge>((baseMsg) =>
{
return new EmployeeAge()
{
System = baseMsg.System,
LegacyId = baseMsg.Id,
Age = baseMsg.Age
};
});
_transEmployeeAddress = new TransformBlock<BaseMsg, EmployeeAddress>((baseMsg) =>
{
return new EmployeeAddress()
{
System = baseMsg.System,
LegacyId = baseMsg.Id,
Address = baseMsg.Address,
Postcode = baseMsg.Postcode
};
});
_bufferEmployeeName = new BufferBlock<EmployeeName>();
_bufferEmployeeAge = new BufferBlock<EmployeeAge>();
_bufferEmployeeAddress = new BufferBlock<EmployeeAddress>();
_actionEmployeeName = new ActionBlock<EmployeeName>((output) =>
{
using (var cxt = new SandboxContext())
{
cxt.EmployeeNames.Add(output);
cxt.SaveChanges();
}
});
_actionEmployeeAge = new ActionBlock<EmployeeAge>((output) =>
{
using (var cxt = new SandboxContext())
{
cxt.EmployeeAges.Add(output);
cxt.SaveChanges();
}
});
_actionEmployeeAddress = new ActionBlock<EmployeeAddress>((output) =>
{
using (var cxt = new SandboxContext())
{
cxt.EmployeeAddresses.Add(output);
cxt.SaveChanges();
}
});
var linkOpts = new DataflowLinkOptions()
{
PropagateCompletion = true
};
// Transform Employees and pass to Batch
_transEmployeeA.LinkTo(_broadcastBaseMsg, linkOpts);
_transEmployeeB.LinkTo(_broadcastBaseMsg, linkOpts);
// Transform Broadcast to respective outputs
_broadcastBaseMsg.LinkTo(_transEmployeeName, linkOpts);
_broadcastBaseMsg.LinkTo(_transEmployeeAge, linkOpts);
_broadcastBaseMsg.LinkTo(_transEmployeeAddress, linkOpts);
// Add outputs to Buffer
_transEmployeeName.LinkTo(_bufferEmployeeName, linkOpts);
_transEmployeeAge.LinkTo(_bufferEmployeeAge, linkOpts);
_transEmployeeAddress.LinkTo(_bufferEmployeeAddress, linkOpts);
// Persist outputs to DB
_bufferEmployeeName.LinkTo(_actionEmployeeName, linkOpts);
_bufferEmployeeAge.LinkTo(_actionEmployeeAge, linkOpts);
_bufferEmployeeAddress.LinkTo(_actionEmployeeAddress, linkOpts);
\u transEmployeeA=新的TransformBlock((输入)=>
{
返回新的BaseMsg()
{
Id=input.Id,
系统=输入。系统,
Name=string.Concat(input.Forename,“,input.lasname),
年龄=输入。年龄,
地址=string.Concat(input.Number,“,input.Street),
邮政编码=输入。邮政编码
};
});
_transEmployeeB=新TransformBlock((输入)=>
{
返回新的BaseMsg()
{
Id=input.Id,
系统=输入。系统,
Name=input.Name,
年龄=输入。年龄,
地址=输入。地址,
邮政编码=输入。邮政编码
};
});
_broadcastBaseMsg=新的广播块(空);
_transEmployeeName=新TransformBlock((baseMsg)=>
{
返回新的EmployeeName()
{
System=baseMsg.System,
LegacyId=baseMsg.Id,
Name=baseMsg.Name
};
});
_transEmployeeAge=新TransformBlock((baseMsg)=>
{
返回新员工年龄()
{
System=baseMsg.System,
LegacyId=baseMsg.Id,
Age=baseMsg.Age
};
});
_transEmployeeAddress=新TransformBlock((baseMsg)=>
{
返回新员工地址()
{
System=baseMsg.System,
LegacyId=baseMsg.Id,
地址=baseMsg.Address,
Postcode=baseMsg.Postcode
};
});
_bufferEmployeeName=新的BufferBlock();
_bufferEmployeeAge=新的BufferBlock();
_bufferEmployeeAddress=新的BufferBlock();
_actionEmployeeName=新操作块((输出)=>
{
使用(var cxt=new SandboxContext())
{
cxt.EmployeeNames.Add(输出);
cxt.SaveChanges();
}
});
_actionEmployeeAge=新操作块((输出)=>
{
使用(var cxt=new SandboxContext())
{
cxt.EmployeeAges.Add(输出);
cxt.SaveChanges();
}
});
_actionEmployeeAddress=新操作块((输出)=>
{
使用(var cxt=new SandboxContext())
{
cxt.EmployeeAddresses.Add(输出);
cxt.SaveChanges();
}
});
var linkOpts=新数据流链接选项()
{
完成=真
};
//对员工进行改造并传递给批处理
_transEmployeeA.LinkTo(_broadcastBaseMsg,linkOpts);
_transEmployeeB.LinkTo(_broadcastBaseMsg,linkOpts);
//将广播转换为各自的输出
_broadcastbasemg.LinkTo(_transEmployeeName,linkOpts);
_broadcastbasemg.LinkTo(_transEmployeeAge,linkOpts);
_broadcastbasemg.LinkTo(_transEmployeeAddress,linkOpts);
//将输出添加到缓冲区
_transEmployeeName.LinkTo(_bufferEmployeeName,linkOpts);
_transEmployeeAge.LinkTo(_bufferEmployeeAge,linkOpts);
_transEmployeeAddress.LinkTo(_bufferEmployeeAddress,linkOpts);
//将输出持久化到数据库
_bufferEmployeeName.LinkTo(_actionEmployeeName,linkOpts);
_bufferEmployeeAge.LinkTo(_actionEmployeeAge,linkOpts);
_bufferEmployeeAddress.LinkTo(_actionEmployeeAddress,linkOpts);
另外@TheodorZoulias的评论有助于简化我对TPL的使用