我是微软Dynamics 365 & Power Platform方面的工程师罗勇,也是2015年7月到2018年6月连续三年Dynamics CRM/Business Solutions方面的微软最有价值专家(Microsoft MVP),欢迎关注我的微信公众号 MSFTDynamics365erLuoYong ,回复421或者20200726可方便获取本文,同时可以在第一间得到我发布的最新博文信息,follow me!
关于这个消息的官方文档请参考 DeliverIncomingEmail Action 。
为了方便测试,我建立一个public queue,如下:
为Queue的mailbox执行Approve Email 和 Test & Enable Mailbox 后确保其Incoming Email Status 和 Outgoing Email Status 都是Success 。
然后我撰写了一个插件,代码如下,
using Microsoft.Xrm.Sdk; using Microsoft.Xrm.Sdk.Query; using System; namespace DemoPlugins { public class EmailDeliverIncomingPostPlugin : IPlugin { public void Execute(IServiceProvider serviceProvider) { ITracingService tracingService = (ITracingService)serviceProvider.GetService(typeof(ITracingService)); tracingService.Trace($"Enter {this.GetType()} on {DateTime.UtcNow.ToString("yyyy-MM-dd HH:mm:ss:ms")}"); IPluginExecutionContext context = (IPluginExecutionContext)serviceProvider.GetService(typeof(IPluginExecutionContext)); tracingService.Trace($"context.UserId = {context.UserId}"); tracingService.Trace($"context.InitiatingUserId = {context.InitiatingUserId}"); if (context.InputParameters.Contains("ExtraProperties") && context.InputParameters["ExtraProperties"] is Entity) { tracingService.Trace("InputParameters contains ExtraProperties and ExtraProperties is Entity."); var currentEntity = (Entity)context.InputParameters["ExtraProperties"]; tracingService.Trace($"ExtraProperties Entity Logical Name = { currentEntity.LogicalName},Entity Id = { currentEntity.Id }"); foreach (var item in currentEntity.Attributes) { if (item.Value != null) { tracingService.Trace($"ExtraProperties attribute key = {item.Key} value = {item.Value.ToString()} type = { item.Value.GetType()}"); } else { tracingService.Trace($"ExtraProperties attribute key = {item.Key} value = null"); } } } else { tracingService.Trace("InputParameters does not contain ExtraProperties or ExtraProperties are not Entity."); } int i = 1; if (context.InputParameters.Contains("Attachments") && context.InputParameters["Attachments"] is EntityCollection) { tracingService.Trace("InputParameters contains Attachments and Attachments is EntityCollection."); var ec = (EntityCollection)context.InputParameters["Attachments"]; foreach (var ecEntity in ec.Entities) { tracingService.Trace($"Attachment - {i},Entity Logical Name = {ecEntity.LogicalName},Entity Id ={ecEntity.Id}"); foreach (var item in ecEntity.Attributes) { if (item.Value != null) { if (item.Value.ToString().Length >= 210) { tracingService.Trace($"Attachments attribute key = {item.Key} value = {item.Value.ToString().Substring(0, 200)} type = { item.Value.GetType()}"); } else { tracingService.Trace($"Attachments attribute key = {item.Key} value = {item.Value.ToString()} type = { item.Value.GetType()}"); } } else { tracingService.Trace($"Attachments attribute key = {item.Key} value = null type = { item.Value.GetType()}"); } } i++; } } else { tracingService.Trace("InputParameters does not contain Attachments or Attachments are not EntityCollection."); } } } }
我将其注册到email实体的DeliverIncoming 消息的PostOperation阶段,为了减少影响,我将Execution Mode设置为Asynchronous .
然后我往往这个queue发一封没有附件的邮件,打开Plug-in Trace Log产生的日志如下,这个User是 SYSTEM 账号。
Enter DemoPlugins.EmailDeliverIncomingPostPlugin on 2020-07-26 09:46:44:4644 context.UserId = 4274a1cf-588b-41b4-8901-1c08551bc142 context.InitiatingUserId = 4274a1cf-588b-41b4-8901-1c08551bc142 InputParameters contains ExtraProperties and ExtraProperties is Entity. ExtraProperties Entity Logical Name = email,Entity Id = 00000000-0000-0000-0000-000000000000 ExtraProperties attribute key = conversationindex value = 0101D6633143B8D6902F5314F04EA808A611CD7546B2 type = System.String ExtraProperties attribute key = followemailuserpreference value = False type = System.Boolean ExtraProperties attribute key = correlationmethod value = 0 type = System.Int32 InputParameters contains Attachments and Attachments is EntityCollection.
发一份有附件的邮件,日志如下:
Enter DemoPlugins.EmailDeliverIncomingPostPlugin on 2020-07-26 09:56:59:5659 context.UserId = 4274a1cf-588b-41b4-8901-1c08551bc142 context.InitiatingUserId = 4274a1cf-588b-41b4-8901-1c08551bc142 InputParameters contains ExtraProperties and ExtraProperties is Entity. ExtraProperties Entity Logical Name = email,Entity Id = 00000000-0000-0000-0000-000000000000 ExtraProperties attribute key = conversationindex value = 0101D663326AC370C067DF46244E8F41F010B7177835 type = System.String ExtraProperties attribute key = followemailuserpreference value = False type = System.Boolean ExtraProperties attribute key = correlationmethod value = 0 type = System.Int32 InputParameters contains Attachments and Attachments is EntityCollection. Attachment - 1,Entity Logical Name = activitymimeattachment,Entity Id =54ea4ca9-5633-40d0-9419-dbcfa1c18642 Attachments attribute key = mimetype value = image/jpeg type = System.String Attachments attribute key = attachmentcontentid value = [email protected] type = System.String Attachments attribute key = body value = /9j/4AAQSkZJRgABAQAAAQABAAD/2wBDAAoHBwgHBgoICAgLCgoLDhgQDg0NDh0VFhEYIx8lJCIfIiEmKzcvJik0KSEiMEExNDk7Pj4+JS5ESUM8SDc9Pjv/2wBDAQoLCw4NDhwQEBw7KCIoOzs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7 type = System.String Attachments attribute key = filename value = 罗勇照片_全身.jpg type = System.String Attachments attribute key = activitymimeattachmentid value = 54ea4ca9-5633-40d0-9419-dbcfa1c18642 type = System.Guid
如果是答复邮件被抓取有啥不一样,我先通过LuoYong Test Queue往我的邮箱发一封邮件,这个邮件与一个订单做了关联,然后我回复邮件,我们通过日志来看:
1. ExtraProperties参数增加了属性 parentactivityid ,类型为Guid,这个值是答复邮件的Guid,有了这个就可以知道邮件是关于那个实体记录,获取关联的实体记录的详细信息。
2. ExtraProperties参数增加了属性 inreplyto ,类型为string,值类似这种 0C27DF54E5964AD88C8EB1A4B0F0576E1D66334DB9AC@LUOYONGMAILFORQUEUE.CRM435740.ONMICROSOFT.COM ,我也不知道用处。
Enter DemoPlugins.EmailDeliverIncomingPostPlugin on 2020-07-26 10:18:31:1831 context.UserId = 4274a1cf-588b-41b4-8901-1c08551bc142 context.InitiatingUserId = 4274a1cf-588b-41b4-8901-1c08551bc142 InputParameters contains ExtraProperties and ExtraProperties is Entity. ExtraProperties Entity Logical Name = email,Entity Id = 00000000-0000-0000-0000-000000000000 ExtraProperties attribute key = conversationindex value = 015D51E512321D4FBA83806548FAB761C9D66334DB9AF222A0EC8E type = System.String ExtraProperties attribute key = inreplyto value = <0C27DF54E5964AD88C8EB1A4B0F0576E1D66334DB9AC@LUOYONGMAILFORQUEUE.CRM435740.ONMICROSOFT.COM> type = System.String ExtraProperties attribute key = followemailuserpreference value = False type = System.Boolean ExtraProperties attribute key = correlationmethod value = 3 type = System.Int32 ExtraProperties attribute key = parentactivityid value = 6d018be8-27cf-ea11-a813-000d3aa088a2 type = System.Guid InputParameters contains Attachments and Attachments is EntityCollection. Attachment - 1,Entity Logical Name = activitymimeattachment,Entity Id =e7d28ef7-abdd-4e2d-bd11-a9ab46d40785 Attachments attribute key = mimetype value = text/plain type = System.String Attachments attribute key = attachmentcontentid value = [email protected] type = System.String Attachments attribute key = body value = bHZvX29yZGVyZGVza3JlcXVlc3RzKCkNCi9xdWV1ZXMoOTI5NWZhOWMtNDkxZC1lYTExLWE4MTEtMDAwZDNhMzc4ZjQ3KQ0KL3N5c3RlbXVzZXJzKEB7Ym9keSgnR2V0X09yZGVyX0Rlc2tfUmVjb3JkX0VNRUEnKT9bJ19vd25lcmlkX3ZhbHVlJ119KQ0KDQpjb25j type = System.String Attachments attribute key = filename value = temp.txt type = System.String Attachments attribute key = activitymimeattachmentid value = e7d28ef7-abdd-4e2d-bd11-a9ab46d40785 type = System.Guid Attachment - 2,Entity Logical Name = activitymimeattachment,Entity Id =360999cd-9f0d-46cd-822f-22071c834013 Attachments attribute key = mimetype value = image/png type = System.String Attachments attribute key = attachmentcontentid value = [email protected] type = System.String Attachments attribute key = body value = iVBORw0KGgoAAAANSUhEUgAAC7gAAAfQCAIAAADdcIVtAAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8YQUAAAAJcEhZcwAAHYcAAB2HAY/l8WUAAP+lSURBVHhe7N0NYBTFwf9x2tr6WsFXEBG07VPt86Lt37ZBBfGlFS2iUEVqUaAqgghGFEFAEEENghghQhB5CQqE type = System.String Attachments attribute key = filename value = 1.png type = System.String Attachments attribute key = activitymimeattachmentid value = 360999cd-9f0d-46cd-822f-22071c834013 type = System.Guid
那下面我们做点增强,如果答复的邮件是关于 ly_order 实体记录的话(能知道关联的是哪条记录当然你就可以做得更多),我就创建一个新邮件,将收到的邮件的附件发送给我的邮箱 [email protected] 。
请注意,我这里利用了仅限内部使用的参数 ExtraProperties ,参数中的内容有可能变化而不通知,各位谨慎使用。
using Microsoft.Crm.Sdk.Messages; using Microsoft.Xrm.Sdk; using Microsoft.Xrm.Sdk.Query; using System; namespace DemoPlugins { public class EmailDeliverIncomingPostPlugin : IPlugin { public void Execute(IServiceProvider serviceProvider) { ITracingService tracingService = (ITracingService)serviceProvider.GetService(typeof(ITracingService)); tracingService.Trace($"Enter {this.GetType()} on {DateTime.UtcNow.ToString("yyyy-MM-dd HH:mm:ss:ms")}"); IPluginExecutionContext context = (IPluginExecutionContext)serviceProvider.GetService(typeof(IPluginExecutionContext)); tracingService.Trace($"context.UserId = {context.UserId}"); tracingService.Trace($"context.InitiatingUserId = {context.InitiatingUserId}"); IOrganizationServiceFactory serviceFactory = (IOrganizationServiceFactory)serviceProvider.GetService(typeof(IOrganizationServiceFactory)); IOrganizationService orgSvc = serviceFactory.CreateOrganizationService(null); if (context.InputParameters.Contains("ExtraProperties") && context.InputParameters["ExtraProperties"] is Entity) { tracingService.Trace("InputParameters contains ExtraProperties and ExtraProperties is Entity."); var currentEntity = (Entity)context.InputParameters["ExtraProperties"]; tracingService.Trace($"ExtraProperties Entity Logical Name = { currentEntity.LogicalName},Entity Id = { currentEntity.Id }"); if (currentEntity.Contains("parentactivityid")) { tracingService.Trace($"ExtraProperties parentactivityid = {currentEntity.GetAttributeValue<Guid>("parentactivityid")}"); var replytoEmail = orgSvc.Retrieve("email", currentEntity.GetAttributeValue<Guid>("parentactivityid"), new ColumnSet("regardingobjectid")); if (replytoEmail.Contains("regardingobjectid") && replytoEmail.GetAttributeValue<EntityReference>("regardingobjectid").LogicalName == "ly_order") { tracingService.Trace($"Reply to email is regarding {replytoEmail.GetAttributeValue<EntityReference>("regardingobjectid").LogicalName} with id = {replytoEmail.GetAttributeValue<EntityReference>("regardingobjectid").Id}"); Entity fromEntity = new Entity("activityparty"); fromEntity["partyid"] = new EntityReference("queue", Guid.Parse("86476289-1fcf-ea11-a813-000d3aa088a2")); Entity toEntity = new Entity("activityparty"); toEntity["addressused"] = "[email protected]"; Entity emailEntity = new Entity("email"); emailEntity["from"] = new Entity[] { fromEntity }; emailEntity["to"] = new Entity[] { toEntity }; emailEntity["regardingobjectid"] = replytoEmail.GetAttributeValue<EntityReference>("regardingobjectid"); emailEntity["subject"] = "罗勇撰写的通过Dynamics 365自动抓取邮件附件发送新邮件"; emailEntity["description"] = "<h1>罗勇通过程序创建的邮件</h1>"; var emailId = orgSvc.Create(emailEntity); tracingService.Trace($"Created email with id ={emailId}"); //如果包括附件,为刚才的邮件添加附件 if (context.InputParameters.Contains("Attachments") && context.InputParameters["Attachments"] is EntityCollection) { var attachmentEC = context.InputParameters["Attachments"] as EntityCollection; if (attachmentEC.Entities.Count >= 1) { tracingService.Trace($"The reply email has {attachmentEC.Entities.Count} attachments."); foreach (var attachEntity in attachmentEC.Entities) { var activityMimeAttachment = new Entity("activitymimeattachment"); activityMimeAttachment["objectid"] = new EntityReference("email", emailId); activityMimeAttachment["objecttypecode"] = "email"; activityMimeAttachment["filename"] = attachEntity.GetAttributeValue<string>("filename"); activityMimeAttachment["body"] = attachEntity.GetAttributeValue<string>("body"); activityMimeAttachment["mimetype"] = attachEntity.GetAttributeValue<string>("mimetype"); orgSvc.Create(activityMimeAttachment); tracingService.Trace($"Created an activitymimeattachment for email with id = {emailId}"); } } } //别忘了发送邮件 SendEmailRequest sendEmailRequest = new SendEmailRequest { EmailId = emailId, IssueSend = true }; orgSvc.Execute(sendEmailRequest); } } } else { tracingService.Trace("InputParameters does not contain ExtraProperties or ExtraProperties are not Entity."); } } } }
看我收到了自动转发过来的邮件:
你可能会问,还能获取其他信息,当然还包括其他信息,比如收件人,邮件标题,邮件内容,发件人,收到邮件的时间等,可以通过官方文档看到包括那些,代码可以通过这样来看下:
foreach(var item in context.InputParameters) { if (item.Value != null) { tracingService.Trace($"InputParameters key = {item.Key} value = {item.Value.ToString()} type = { item.Value.GetType()}"); } else { tracingService.Trace($"InputParameters key = {item.Key} value = null"); } }
我这里提供一个参数值示例,可以看到From的值不只是邮箱,具体情况具体查看。
InputParameters key = MessageId value = <FR1P152MB07091928254D8482F3F57DEE88750@FR1P152MB0709.LAMP152.PROD.OUTLOOK.COM> type = System.String InputParameters key = From value = "罗 勇" [email protected] type = System.String InputParameters key = To value = [email protected] type = System.String InputParameters key = Cc value = type = System.String InputParameters key = Bcc value = type = System.String InputParameters key = ReceivedOn value = 7/26/2020 11:16:14 AM type = System.DateTime InputParameters key = SubmittedBy value = "罗 勇" [email protected] type = System.String InputParameters key = Importance value = 1 type = System.String InputParameters key = Body value = body type = System.String InputParameters key = Attachments value = Microsoft.Xrm.Sdk.EntityCollection type = Microsoft.Xrm.Sdk.EntityCollection InputParameters key = Subject value = 回复: LuoYong Test Queue给你发一封邮件 CRM:0001001 type = System.String InputParameters key = ValidateBeforeCreate value = True type = System.Boolean InputParameters key = ExtraProperties value = Microsoft.Xrm.Sdk.Entity type = Microsoft.Xrm.Sdk.Entity