Dynamics CRM C#插件:联系人增改时给SystemCustomizer角色用户批量发邮件
Dynamics CRM C#插件:向SystemCustomizer角色用户发送通知邮件的实现方案
先理清核心思路
我之前做过类似的需求,核心其实就是两步:精准获取SystemCustomizer角色下的有效用户,然后选择合适的方式给这些用户发送通知邮件。至于用循环逐个发还是批量请求,各有优劣,我给你拆解清楚:
两种发送方式的对比
- 循环逐个发送:代码逻辑简单直观,上手快,适合用户数量少的场景(比如10个以内),但用户多的话会产生多次API调用,性能稍差,而且单个发送失败可能需要单独处理。
- 批量请求发送:用
ExecuteMultipleRequest打包所有邮件请求,一次性执行,API调用次数大幅减少,效率更高,是官方推荐的批量操作方式,适合用户数量较多的场景,还能设置失败不中断其他请求。
具体实现步骤与代码示例
第一步:获取SystemCustomizer角色下的有效用户
首先要查询Dynamics CRM中的角色和关联用户,这里用QueryExpression构建查询,还会自动过滤掉禁用、没有邮箱的用户:
private List<Entity> GetSystemCustomizerUsers(IOrganizationService service) { // 先找到System Customizer角色 var roleQuery = new QueryExpression("role") { ColumnSet = new ColumnSet("roleid"), Criteria = new FilterExpression { Conditions = { new ConditionExpression("name", ConditionOperator.Equal, "System Customizer") } } }; var roleResult = service.RetrieveMultiple(roleQuery); if (roleResult.Entities.Count == 0) return new List<Entity>(); var roleId = roleResult.Entities[0].Id; // 查询该角色下的有效用户(排除禁用+无邮箱的) var userQuery = new QueryExpression("systemuser") { ColumnSet = new ColumnSet("fullname", "internalemailaddress"), Criteria = new FilterExpression { Conditions = { new ConditionExpression("isdisabled", ConditionOperator.Equal, false), new ConditionExpression("internalemailaddress", ConditionOperator.NotNull) } }, LinkEntities = { new LinkEntity { LinkFromEntityName = "systemuser", LinkFromAttributeName = "systemuserid", LinkToEntityName = "systemuserroles", LinkToAttributeName = "systemuserid", LinkCriteria = new FilterExpression { Conditions = { new ConditionExpression("roleid", ConditionOperator.Equal, roleId) } } } } }; var userResult = service.RetrieveMultiple(userQuery); return userResult.Entities.ToList(); }
第二步:准备邮件内容与基础信息
从插件上下文获取当前创建/更新的联系人信息,构造邮件的主题和正文:
// 从插件上下文的PostImage获取最新联系人信息(注册插件时要配置PostImage) var contact = context.PostEntityImages["ContactPostImage"] as Entity; var contactName = contact.GetAttributeValue<string>("fullname"); var contactEmail = contact.GetAttributeValue<string>("emailaddress1"); var contactId = contact.Id.ToString(); // 邮件主题和正文(可以根据需求调整格式) var subject = $"联系人更新通知:{contactName}已创建/修改"; var body = $@"<p>您好,</p> <p>联系人 <strong>{contactName}</strong> 已完成创建或更新操作,相关信息如下:</p> <ul> <li>联系人姓名:{contactName}</li> <li>联系人邮箱:{contactEmail}</li> <li>联系人ID:{contactId}</li> </ul> <p>请留意相关业务跟进。</p>";
第三步:实现邮件发送逻辑
方式一:循环逐个发送邮件(适合小批量用户)
这种方式代码简单,容易调试,单个用户发送失败不会影响其他用户:
var targetUsers = GetSystemCustomizerUsers(service); foreach (var user in targetUsers) { try { // 构造邮件实体 var email = new Entity("email") { ["subject"] = subject, ["description"] = body, // 发件人设为当前插件运行用户 ["from"] = new EntityCollection(new List<Entity> { new Entity("activityparty") { ["partyid"] = new EntityReference("systemuser", service.GetCurrentUserId()) } }), // 收件人设为当前循环的用户 ["to"] = new EntityCollection(new List<Entity> { new Entity("activityparty") { ["partyid"] = new EntityReference("systemuser", user.Id) } }) }; // 先保存邮件到系统 var emailId = service.Create(email); // 执行发送请求 var sendRequest = new SendEmailRequest { EmailId = emailId, TrackingToken = string.Empty, IssueSend = true }; service.Execute(sendRequest); } catch (Exception ex) { // 记录单个用户发送失败的日志,不中断循环 Trace.TraceError($"给用户【{user.GetAttributeValue<string>("fullname")}】发送邮件失败:{ex.Message}"); } }
方式二:批量发送邮件(推荐,适合大批量用户)
用ExecuteMultipleRequest打包所有请求,减少API调用次数,提升效率:
var targetUsers = GetSystemCustomizerUsers(service); if (!targetUsers.Any()) return; // 第一步:批量创建邮件 var createEmailRequests = new ExecuteMultipleRequest { Settings = new ExecuteMultipleSettings { ContinueOnError = true, // 单个创建失败不影响其他 ReturnResponses = true // 需要返回结果获取邮件ID }, Requests = new OrganizationRequestCollection() }; foreach (var user in targetUsers) { var email = new Entity("email") { ["subject"] = subject, ["description"] = body, ["from"] = new EntityCollection(new List<Entity> { new Entity("activityparty") { ["partyid"] = new EntityReference("systemuser", service.GetCurrentUserId()) } }), ["to"] = new EntityCollection(new List<Entity> { new Entity("activityparty") { ["partyid"] = new EntityReference("systemuser", user.Id) } }) }; createEmailRequests.Requests.Add(new CreateRequest { Target = email }); } // 执行批量创建 var createResponses = (ExecuteMultipleResponse)service.Execute(createEmailRequests); // 第二步:批量发送成功创建的邮件 var sendEmailRequests = new ExecuteMultipleRequest { Settings = new ExecuteMultipleSettings { ContinueOnError = true, ReturnResponses = false }, Requests = new OrganizationRequestCollection() }; for (int i = 0; i < createResponses.Responses.Count; i++) { if (createResponses.Responses[i].Fault == null) { var emailId = ((CreateResponse)createResponses.Responses[i].Response).id; sendEmailRequests.Requests.Add(new SendEmailRequest { EmailId = emailId, IssueSend = true }); } else { var userName = targetUsers[i].GetAttributeValue<string>("fullname"); Trace.TraceError($"给用户【{userName}】创建邮件失败:{createResponses.Responses[i].Fault.Message}"); } } // 执行批量发送 if (sendEmailRequests.Requests.Count > 0) { service.Execute(sendEmailRequests); }
插件注册关键注意事项
- 事件注册:选择
Contact实体的Create和Update消息,建议设置为异步执行,避免阻塞联系人的创建/更新操作,提升用户体验。 - PostImage配置:必须配置PostImage,包含
fullname、emailaddress1等你需要用到的联系人字段,这样插件才能获取到最新的联系人数据。 - 权限配置:插件运行的服务账号需要拥有
读取角色/用户、创建邮件、发送邮件的权限,建议用专门的系统服务账号来注册插件,避免权限问题。
内容的提问来源于stack exchange,提问作者NaveenGNK
相关产品推荐
相关产品推荐

