two similar plugins in Dynamics CRM 2016 ONPREMISE to merge into one - plugins

I have two similar plugins in Dynamics CRM 2016 ONPREMISE to merge into one.
They're
registered to the same entity,
both triggered by update message,
one plugin check value of 3 fields, the other check value of 4 fields. If all equal to a specified value, then go on. If not, return.
4.set, map or calculate the value from old record to new record. two plugins handle two sets of fields.
create a new record.
What I can think is "if else-if " structure. But it looks so naive. Anybody have any advice?
The other plugin checks 3 other fields and execute similar action creating new record with some other fields set or mapped.
Thanks,
protected void ExecuteApplication(LocalPluginContext localContext)
{
IPluginExecutionContext context = null;
IOrganizationService service = null;
ITracingService tracer = null;
context = localContext.PluginExecutionContext;
service = localContext.OrganizationService;
tracer = localContext.TracingService;
try
{
// ensure we have an application and update message
Entity application = new Entity(applicationEntityName);
if (context.InputParameters.Contains("Target") && context.InputParameters["Target"] is Entity)
{
application = (Entity)context.InputParameters["Target"];
if (!application.LogicalName.ToLower().Equals(this.applicationEntityName))
{
return;
}
}
else
{
return;
}
if (context.MessageName.ToLower() != "update")
{
return;
}
// Fetch data from PreImage
Entity postImageApplication = context.PostEntityImages["PostImage"];
//check three fields are not null
if (application.GetAttributeValue<OptionSetValue>("statuscode") == null ||
postImageApplication.GetAttributeValue<EntityReference>("new_service").Name == null ||
postImageApplication.GetAttributeValue<EntityReference>("new_source").Name == null)
{
return;
}
if (
application.GetAttributeValue<OptionSetValue>("statuscode").Value == 881780003 &&
postImageApplication.GetAttributeValue<EntityReference>("new_service").Name == "CIC"
)
// process if the update meets the criteria
{
Entity newApplication = new Entity("new_application");
// set
newApplication.Attributes.Add("new_effectiveapplication", true);
newApplication.Attributes.Add("new_confirmdate", DateTime.Now);
newApplication.Attributes.Add("new_signdate", DateTime.Now);
//mapped
if (postImageApplication.Attributes.Contains("new_client"))
{
newApplication.Attributes.Add("new_client", postImageApplication["new_client"]);
}
if (postImageApplication.Attributes.Contains("new_servicecentre"))
{
newApplication.Attributes.Add("new_servicecentre", postImageApplication["new_servicecentre"]);
}
service.Create(newApplication);
}
else
{
return;
}

I like to abstract unwieldy predicates into their own method.
How about something like this:
private bool allFieldsHaveValues()
{
return application.GetAttributeValue<OptionSetValue>("statuscode") != null
&& postImageApplication.GetAttributeValue<EntityReference>("new_service").Name != null
&& postImageApplication.GetAttributeValue<EntityReference>("new_source").Name != null;
}
private bool valuesAreValid()
{
return application.GetAttributeValue<OptionSetValue>("statuscode").Value == 881780003
&& postImageApplication.GetAttributeValue<EntityReference>("new_service").Name == "CIC";
}
if (allFieldsHaveValues() && valuesAreValid())
{
Entity newApplication = new Entity("new_application");

Related

Working on pre-operation plug-in to update "Modified By" field in MSCRM -- Need help fixing code

I am trying to update the "Modified By" field based on a text field called "Prepared By", which contains the name of a user. I've created a pre-operation plug-in to do this and believe I am close to done. However, the "Modified By" field is still not successfully getting updated. I am relatively new to coding and CRM, and could use some help modifying the code and figuring out how I can get this to work.
using Microsoft.Xrm.Sdk;
using Microsoft.Xrm.Sdk.Query;
using System;
using System.Linq;
namespace TimClassLibrary1.Plugins
{
public class CreateUpdateContact : IPlugin
{
public void Execute(IServiceProvider serviceProvider)
{
var tracingService = (ITracingService)serviceProvider.GetService(typeof(ITracingService));
var context = (IPluginExecutionContext)serviceProvider.GetService(typeof(IPluginExecutionContext));
var factory = (IOrganizationServiceFactory)serviceProvider.GetService(typeof(IOrganizationServiceFactory));
var service = factory.CreateOrganizationService(context.UserId);
tracingService.Trace("Start plugin");
tracingService.Trace("Validate Target");
if (!context.InputParameters.Contains("Target") || !(context.InputParameters["Target"] is Entity))
return;
tracingService.Trace("Retrieve Target");
var target = (Entity)context.InputParameters["Target"];
String message = context.MessageName.ToLower();
SetCreatedByAndModifiedBy(tracingService, service, target, message);
}
private void SetCreatedByAndModifiedBy(ITracingService tracingService, IOrganizationService service, Entity target, string message)
{
tracingService.Trace("Start SetPriceList");
tracingService.Trace("Validate Message is Create or Update");
if (!message.Equals("create", StringComparison.OrdinalIgnoreCase) && !message.Equals("update", StringComparison.OrdinalIgnoreCase))
return;
tracingService.Trace("Retrieve Attributes");
var createdByReference = target.GetAttributeValue<EntityReference>("new_createdby");
var modifiedByReference = target.GetAttributeValue<EntityReference>("new_modifiedby");
tracingService.Trace("Retrieve And Set User for Created By");
RetrieveAndSetUser(tracingService, service, target, createdByReference, "createdby");
tracingService.Trace("Retrieve And Set User for Modified By");
RetrieveAndSetUser(tracingService, service, target, modifiedByReference, "modifiedby");
}
private void RetrieveAndSetUser(ITracingService tracingService, IOrganizationService service, Entity target, EntityReference reference, string targetAttribute)
{
tracingService.Trace("Validating Reference");
if (reference == null)
return;
tracingService.Trace("Retrieving and Validating User");
var user = RetrieveUserByName(service, reference.Name, new ColumnSet(false));
if (user == null)
return;
tracingService.Trace("Setting Target Attribute");
target[targetAttribute] = user.ToEntityReference();
}
private Entity RetrieveUserByName(IOrganizationService service, string name, ColumnSet columns)
{
var query = new QueryExpression
{
EntityName = "systemuser",
ColumnSet = columns,
Criteria = new FilterExpression
{
FilterOperator = LogicalOperator.And,
Conditions =
{
new ConditionExpression
{
AttributeName = "fullname",
Operator = ConditionOperator.Equal,
Values = { name }
}
}
}
};
var retrieveResponse = service.RetrieveMultiple(query);
if (retrieveResponse.Entities.Count == 1)
{
return retrieveResponse.Entities.FirstOrDefault();
}
else
{
return null;
}
}
}
}
If you do get use from method Retreiveusernyname then you have to use below code
target[“modifiedby”] = new EntityRefrence(user.logicalname,user.id);
I don't see anything obviously wrong with your update, however you are taking a complicated and unnecessary step with your RetrieveUserByName() method. You already have EntityReference objects from your new_createdby and new_modifiedby fields, you can simply assign those to the target:
if (message.Equals("create", StringComparison.OrdinalIgnoreCase))
{
target["createdby"] = target["new_createdby];
}
else if (message.Equals("update", StringComparison.OrdinalIgnoreCase))
{
target["modifiedby"] = target["new_modifiedby];
}
If new_createdby and new_modifiedby are not entity references, then that would explain why your existing code does not work, if they are, then use my approach.

Crm plugin update fails

I have created two new fields named "Price" for quote and quote product and I want to update the second every time I update the first.
Here is my code:
protected void ExecutePostAccountUpdateContacts(LocalPluginContext localContext)
{
if (localContext == null)
{
throw new ArgumentNullException("localContext");
}
string oldPrice = "";
string newPrice = "";
IPluginExecutionContext context = localContext.PluginExecutionContext;
IOrganizationService service = localContext.OrganizationService;
var ServiceContext = new OrganizationServiceContext(service);
ITracingService tracingService = localContext.TracingService;
if (context.InputParameters.Contains("Target") &&
context.InputParameters["Target"] is Entity)
{
Entity entity = (Entity)context.InputParameters["Target"];
Entity preImageEntity = (context.PreEntityImages != null && context.PreEntityImages.Contains(this.preImageAlias)) ? context.PreEntityImages[this.preImageAlias] : null;
// get the post entity image
Entity postImageEntity = (context.PostEntityImages != null && context.PostEntityImages.Contains(this.postImageAlias)) ? context.PostEntityImages[this.postImageAlias] : null;
if (preImageEntity.Attributes.Contains("Price"))
{
oldPrice = (string)preImageEntity.Attributes["Price"];
}
if (postImageEntity.Attributes.Contains("Price"))
{
newPrice = (string)postImageEntity.Attributes["Price"];
}
if (newPrice != oldPrice)
{
try
{
//Create query to get the related contacts
var res = from c in ServiceContext.CreateQuery("Products")
where c["parentQuoteid"].Equals(entity.Id)
select c;
foreach (var c in res)
{
Entity e = (Entity)c;
e["Price"] = newPrice;
ServiceContext.UpdateObject(e);
}
ServiceContext.SaveChanges();
}
catch (FaultException ex)
{
throw new InvalidPluginExecutionException("An error occurred in the plug-in.", ex);
}
}
}
}
Although you haven't asked a question, your query isn't quite right. So I am assuming your plugin fails when querying for product with a parentquoteid.
Not all linq operators are implemented, also , pass the entity logical name to the create query as a parameter, so instead of Products, just product. There is no out of the box field called parentquoteid, are you missing your custom attribute prefix?
var res = from c in ServiceContext.CreateQuery("product")
where c.GetAttributeValue<Guid>("new_parentquoteid") == entity.Id
select c;

Entity framework tracking wrong model object after helper method

Below is some basic code that's a part of our Create controller method. If a certain checkbox is selected, we want to create a copy of the agenttransmission object we are currently creating, with a couple fields altered.
When the program goes into the helper method, it creates the sub record without incident, however for some reason, when the program finishes and comes back to the Create method, the model object agenttransmission becomes the sub model object. All value, including the PK are suddenly pressent in the agenttransmission object.
Not sure how this is happening since there is a string return value on the helper method and no fields are touched on the agenttransmission record.
Create method
//Create substat if requested
if (agenttransmission.OverrideId)
{
status += ". " + CreateSubStat(agenttransmission);
}
Helper method
public string CreateSubStat(AgentTransmission master)
{
string msg = string.Empty;
if (ModelState.IsValid)
{
using (AgentResourcesEntities tempDb = new AgentResourcesEntities())
{
try
{
//Check to see if substat already exists
var check = (from a in db.AgentTransmission
where a.ParentId == master.ID && a.RecordStatus.Equals("P")
select a).ToList();
if (check.Count > 0) return string.Empty;
//If not add dependent record
AgentTransmission sub = master;
sub.OverrideId = false;
sub.DRMCompanyId = string.Empty;
sub.CONCode = "00";
sub.RecordStatus = "P";
sub.ParentId = master.ID;
sub.ID = 0;
sub.IsSubstat = true;
sub.SendToDynamicsOptions = "N";
sub.SendToNMF = false;
//Remove blanks from ClearinghousePartners list
sub.ClearinghousePartners.RemoveAll(
x =>
string.IsNullOrWhiteSpace(x.ClearingHouseName) &&
string.IsNullOrWhiteSpace(x.TradingPartnerName) && x.StartDate == null);
sub.AgentRelationshipCodes.RemoveAll(
x =>
string.IsNullOrWhiteSpace(x.RelationshipId) &&
!x.EffectiveDate.HasValue && x.Id == 0);
foreach (var item in sub.AgentRelationshipCodes)
{
item.LastChangeDate = DateTime.Now;
item.LastChangeId = SecurityRules.GetUserName(User);
item.AgentTransmission = sub;
item.AgtTableId = sub.ID;
}
foreach (var item in sub.ClearinghousePartners)
{
item.AgentTransmission = sub;
item.AgtTransId = sub.ID;
}
db.AgentTransmission.Add(sub);
db.SaveChanges();
msg = "Substat saved with status of 'Dependent'.";
}
catch (DbEntityValidationException dbEx)
{
msg = "Error creating substat. IT has been informed and will respond shortly.";
SendEmail.ErrorMail(dbEx.Message, SecurityRules.GetUserName(User));
}
catch (Exception ex)
{
msg = "Error creating substat. IT has been informed and will respond shortly.";
SendEmail.ErrorMail(ex, SecurityRules.GetUserName(User));
}
}
}
else
{
//Invalid ModelState error handling
string messages = string.Join("; ", ModelState.Values
.SelectMany(x => x.Errors)
.Select(x => x.ErrorMessage));
SendEmail.ErrorMail("Error Creating Substat: " + messages, SecurityRules.GetUserName(User));
msg = "Error creating substat. IT has been informed and will respond shortly.";
}
return msg;
}
The line...
AgentTransmission sub = master;
...doesn't copy master to sub, but assigns it to sub, because AgentTransmission is a reference type. So everything you do to sub you do to master.
You either must clone master to sub (create a new object and copy its properties), or re-fetch the master object from the database after the CreateSubStat call.

create and update plugin in crm 2013 c#

I need to write plugin in CRM 2013 that do two thing:
If statecode = 3 and the field el_meeting_in_outlook_id is empty I
need to create a new appoitment.
If statecode = 3 and the field el_meeting_in_outlook_id is not empty
I need to update an existing appoitment.
This is what I wrote:
using Microsoft.Xrm.Sdk;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using tmura_Entity_Plugins;
namespace tmura_Entity__Plugins
{
public class postCreateUpdateServiceAppointment : Plugin
{
public postCreateUpdateServiceAppointment()
: base(typeof(postCreateUpdateServiceAppointment))
{
base.RegisteredEvents.Add(new Tuple<int, string, string, Action<LocalPluginContext>>(40, "Create", null, new Action<LocalPluginContext>(ExecutePostCreate)));
base.RegisteredEvents.Add(new Tuple<int, string, string, Action<LocalPluginContext>>(40, "Update", null, new Action<LocalPluginContext>(ExecutePostUpdate)));
}
private void ExecutePostCreate(LocalPluginContext obj)
{
Logger.WriteMessage("Enter ExecutePostCreate", CrmLogService.MessageLevel.Info, "");
if ((obj.PluginExecutionContext.InputParameters.Contains("Target")) && (obj.PluginExecutionContext.InputParameters["Target"] is Entity))
{
Entity serviceAppontment = (Entity)obj.PluginExecutionContext.InputParameters["Target"];
if (serviceAppontment.LogicalName != "serviceappointment")
return;
Logger.WriteMessage("", CrmLogService.MessageLevel.Info, "");
if ((serviceAppontment.Attributes.Contains("statecode")) || ((int)(serviceAppontment.Attributes["statecode"]) == 3) && (serviceAppontment.Attributes["el_meeting_in_outlook_id"] == null))
{
try
{
Entity appointment = new Entity("appointment");
appointment.Attributes.Add("subject", "Opened automatically");
appointment.Attributes.Add("description", "Just Checking");
appointment.Attributes.Add("el_serviceappointment_id", new EntityReference("serviceappointment", serviceAppontment.Id));
appointment.Attributes.Add("scheduledstart", DateTime.Now.AddDays(7));
appointment.Attributes.Add("scheduledend", DateTime.Now.AddDays(7));
obj.OrganizationService.Create(appointment);
}
catch (Exception ex)
{
Logger.WriteException(ex);
}
}
else if (((int)(serviceAppontment.Attributes["statecode"]) == 3) && (serviceAppontment.Attributes["el_meeting_in_outlook_id"] != null))
{
//TODO
}
}
}
}
}
I don't know what to write in the second section that is supposed to update the appointment. I tried to search the web but with no success.
Can you please help?
Where you have defined the registered events, you have to put "serviceappointment", that is the entity logical name, instead of null.
Then replace: (serviceAppontment.Attributes.Contains("statecode")) || ((int)(serviceAppontment.Attributes["statecode"]) == 3) by (serviceAppontment.Attributes.Contains("statecode")) && ((OptionSetValue)(serviceAppontment.Attributes["statecode"]).Value == 3), because the field "statecode" is an OptionSet. Also replace the || by &&, because when serviceAppontment.Attributes.Contains("statecode")) is false, ((OptionSetValue)(serviceAppontment.Attributes["statecode"]).Value will throw an NullReferenceException.
To update an existing appointment, looks like there is an lookup in appointment entity to serviceappointment entity. So, you have to retrieve all the appointments related with the serviceappointment. You can use this query:
QueryExpression queryExp = new QueryExpression
{
EntityName = "appointment",
ColumnSet = new ColumnSet("subject", "description", "scheduledstart", "scheduledend"),
Criteria = new FilterExpression
{
Conditions = { new ConditionExpression("el_serviceappointment_id", ConditionOperator.Equal, serviceAppontment.Id) }
}
};
EntityCollection collection = obj.OrganizationService.RetrieveMultiple(queryExp);
if (collection.Entities.Count > 0)
{
// now that you have all the appointments related with the serviceappoitement
// you can update de any appointment you want
obj.OrganizationService.Update(appointment);
}
To update your record with a value in a lookup field in serviceappoitnment to may retrieve the serviceappoitment record Entity svcApp = obj.OrganizationService.Retrieve(serviceAppontment.LogicalName, serviceAppontment.Id, new ColumnSet("<lookup_field_in_serviceappoitnment>");
You need to add to the collumnset in the query expression the attribute requiredattendees, and then update the appoitment
if(appointment.Attributes.Contains("requiredattendees") == true)
{
appointment.Attributes["requiredattendees"] = svcApp.Attributes["<lookup_field_in_serviceappoitnment>"];
}
else
{
appointment.Attributes.Add("requiredattendees", svcApp.Attributes["<lookup_field_in_serviceappoitnment>");
}

EF : related entities are set as INSERT instead of being only referenced to the added entity

I tried to find an answer through the related questions I got but I didn't see the same situation I have now. I a beginner with this framework.
The thing is that in the DB the TopicFeedbackType is always referred but TopicNavigatedUrl and Product are always inserted event if I attach the entity. What am I doing wrong? I attach the found entity to the correct entity set name. The entity gets attached. I noticed that before the p_TopicQuickFb have one of its property updated by an attached entity, its EntityKey is null, but when the currentNavUrl(for example) is set, the EntityKey of p_TopicQuickFb is not null anymore. Its value is "EntitySet=TopicQuickFeedbacks" but there is no id.
I am really lost in there.
public void AddTopicQuickFeedback(TopicQuickFeedback p_TopicQuickFb, string p_SessionID)
{
TopicFeedbackType currentType = this.GetTopicFeedbackType(p_TopicQuickFb.TopicFeedbackType.FeedbackType);
bool currentTypeAttached = false;
TopicNavigatedUrl currentNavUrl = this.GetTopicNavigatedUrl(p_TopicQuickFb.TopicNavigatedUrl.Url);
bool currentNavUrlAttached = false;
Product currentProduct = this.GetProduct(p_TopicQuickFb.Product.Name, p_TopicQuickFb.Product.MajorVersion, p_TopicQuickFb.Product.MinorVersion);
bool currentProductAttached = false;
using (COHFeedbackEntities context = GetObjectContext())
{
TopicFeedback tf = GetTopicFeedback(p_SessionID, context);
if (tf != null)
{
if (currentType != null)
{
p_TopicQuickFb.TopicFeedbackType = null;
context.AttachToOrGet<TopicFeedbackType>("TopicFeedbackTypes", ref currentType);
currentTypeAttached = true;
p_TopicQuickFb.TopicFeedbackType = currentType;
}
if (currentNavUrl != null)
{
p_TopicQuickFb.TopicNavigatedUrl = null;
context.AttachToOrGet<TopicNavigatedUrl>("TopicNavigatedUrls", ref currentNavUrl);
currentNavUrlAttached = true;
p_TopicQuickFb.TopicNavigatedUrl = currentNavUrl;
}
if (currentProduct != null)
{
p_TopicQuickFb.Product = null;
context.AttachToOrGet<Product>("Products", ref currentProduct);
currentProductAttached = true;
p_TopicQuickFb.Product = currentProduct;
}
tf.TopicQuickFeedbacks.Add(p_TopicQuickFb);
context.SaveChanges();
context.Detach(tf);
if (currentNavUrlAttached)
{
context.TopicNavigatedUrls.Detach(currentNavUrl);
}
if (currentProductAttached)
{
context.Products.Detach(currentProduct);
}
if (currentTypeAttached)
{
context.TopicFeedbackTypes.Detach(currentType);
}
}
}
}
I found the method in this post : Is is possible to check if an object is already attached to a data context in Entity Framework?
public static void AttachToOrGet<T>(this System.Data.Objects.ObjectContext context, string entitySetName, ref T entity)
where T : IEntityWithKey
{
System.Data.Objects.ObjectStateEntry entry;
// Track whether we need to perform an attach
bool attach = false;
if (
context.ObjectStateManager.TryGetObjectStateEntry
(
context.CreateEntityKey(entitySetName, entity),
out entry
)
)
{
// Re-attach if necessary
attach = entry.State == EntityState.Detached;
// Get the discovered entity to the ref
entity = (T)entry.Entity;
}
else
{
// Attach for the first time
attach = true;
}
if (attach)
{
context.AttachTo(entitySetName, entity);
}
}
Test method:
User user = new User(true, false, false);
string commentStr = "This is my comment";
Product product = new Product("ProductName", 7, 0);
TopicFeedbackComment commFeedback = new TopicFeedbackComment(commentStr, new TopicNavigatedUrl("http://testurl.com/test0"), product);
TopicFeedback feedback = new TopicFeedback(sessionID, user, FeedbackState.New);
provider.AddTopicFeedback(feedback);
TopicFeedback addedFeedback = provider.RetrieveTopicFeedback(sessionID);
provider.AddTopicFeedbackComment(commFeedback, sessionID);
Running this again and again do just INSERT to the
Can't post images so I can provide schema it if necessary.
My answer is in my last comment. I found it by myself.
If someone would like to comment why it's working this way it would be nice! :)