Dynamics CRM 4.0 plugin fails when triggered by API - plugins

I have a plugin registered for when an account is created or updated, this is registered for the post stage.
The plugin works fine when a user creates or updates an account through the CRM interface, however when an account is created uging the API the plugin fails with the ever helpful 'server was unable to process the request' message. if an account is updated through the api the plugin also works correctly.
anyone have any ideas why?
UPDATE:
here is the create code
account = new CrmService.account();
account.ownerid = new CrmService.Owner();
account.ownerid.Value = new Guid("37087BC2-F2F0-DC11-A856-001E0B617486");
account.ownerid.type = CrmService.EntityName.systemuser.ToString();
account.name = model.CompanyName;
account.address1_line1 = model.Address1;
account.address1_line2 = model.Address2;
account.address1_stateorprovince = model.County;
account.address1_country = model.Country;
account.address1_city = model.TownCity;
account.address1_postalcode = model.PostCode;
account.new_companytype = new CrmService.Picklist();
switch (model.SmeType)
{
case SmeType.Micro:
account.new_companytype.Value = 1;
break;
case SmeType.Small:
account.new_companytype.Value = 2;
break;
case SmeType.Medium:
account.new_companytype.Value = 3;
break;
default:
break;
}
account.new_balancesheettotal = new CrmService.CrmMoney();
account.new_balancesheettotal.Value = preQualModel.BalanceSheetGBP;
account.revenue = new CrmService.CrmMoney();
account.revenue.Value = preQualModel.SalesTurnoverGBP;
if (model.Website != null)
{
account.websiteurl = model.Website.ToString();
}
account.numberofemployees = new CrmService.CrmNumber();
account.numberofemployees.Value = (int)preQualModel.NumEmployees;
accountGuid = svc.Create(account);
account.accountid = new CrmService.Key();
account.accountid.Value = accountGuid;
Here is the plugin code:
public void Execute(IPluginExecutionContext context)
{
DynamicEntity entity = null;
// Check if the InputParameters property bag contains a target
// of the current operation and that target is of type DynamicEntity.
if (context.InputParameters.Properties.Contains(ParameterName.Target) &&
context.InputParameters.Properties[ParameterName.Target] is DynamicEntity)
{
// Obtain the target business entity from the input parmameters.
entity = (DynamicEntity)context.InputParameters.Properties[ParameterName.Target];
// TODO Test for an entity type and message supported by your plug-in.
if (entity.Name != EntityName.account.ToString()) { return; }
// if (context.MessageName != MessageName.Create.ToString()) { return; }
}
else
{
return;
}
if (entity!=null && !entity.Properties.Contains("address1_postalcode"))
{
return;
}
if (context.Depth > 2)
{
return;
}
try
{
// Create a Microsoft Dynamics CRM Web service proxy.
// TODO Uncomment or comment out the appropriate statement.
// For a plug-in running in the child pipeline, use this statement.
// CrmService crmService = CreateCrmService(context, true);
// For a plug-in running in the parent pipeline, use this statement.
ICrmService crmService = context.CreateCrmService(true);
#region get erdf area from database
string postCode = entity.Properties["address1_postalcode"].ToString();
postCode = postCode.Replace(" ", ""); //remove spaces, db stores pcodes with no spaces, users usually enter them, e.g b4 7xg -> b47xg
string erdfArea = "";
SqlConnection myConnection = new SqlConnection(#"REDACTED");
try
{
myConnection.Open();
}
catch (Exception e)
{
Console.WriteLine(e.ToString());
}
try
{
SqlDataReader myReader = null;
SqlCommand myCommand = new SqlCommand("select ErdfAreaType from dim.Locality WHERE PostCode = '" + postCode+"'",
myConnection);
myReader = myCommand.ExecuteReader();
while (myReader.Read())
{
erdfArea = myReader["ErdfAreaType"].ToString();
}
}
catch (Exception e)
{
Console.WriteLine(e.ToString());
}
try
{
myConnection.Close();
}
catch (Exception e)
{
Console.WriteLine(e.ToString());
}
#endregion
entity.Properties["new_erdfarea"] = erdfArea;
crmService.Update(entity);
}
catch (System.Web.Services.Protocols.SoapException ex)
{
throw new InvalidPluginExecutionException(
String.Format("An error occurred in the {0} plug-in.",
this.GetType().ToString()),
ex);
}
}

Sometimes it can be difficult to see the actual source of an error in plugins. In moments like this tracing is your friend. You can use this tool to enable tracing. When you have the trace files, try to search them for the error you got in your exception. This should tell you more details about what is failing.

Turns out this was due to me expecting data that was not there due to some weird behaviour in CRM.
I was taking the dynamicEntity passed to the plugin like so
entity = (DynamicEntity)context.InputParameters.Properties[ParameterName.Target];
But this was missing critical things like accountid. fixed it by using the PostEntityImage entity instead, which had all of the expected data like so
entity = (DynamicEntity)context.PostEntityImages[ParameterName.Target];

Related

How to get all registered user's from xmpp server(openfire) in android

Hi i need to get all registered user from xmpp server(openfire).
try {
UserSearchManager search = new UserSearchManager(connection);
Form searchForm = search.getSearchForm("search."+connection.getServiceName());
Form answerForm = searchForm.createAnswerForm();
answerForm.setAnswer("Username", true);
answerForm.setAnswer("search", "anbu");
ReportedData data = search.getSearchResults(answerForm, "search." + connection.getServiceName());
if (data.getRows() != null) {
Iterator<ReportedData.Row> it = data.getRows();
while (it.hasNext()) {
ReportedData.Row row = it.next();
Iterator iterator = row.getValues("jid");
if (iterator.hasNext()) {
String value = iterator.next().toString();
Log.i("Iteartor values......", " " + value);
}
}
}
} catch (XMPPException e2) {
e2.printStackTrace();
}
I have installed search.jar in admin panel. still i am getting (remote server not found). But chat is working for me.
And why not using the REST API plugin for Openfire?
https://www.igniterealtime.org/projects/openfire/plugins/restapi/readme.html#retrieve-users

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.

Unstable application uses SqlDependency. Several states and errors

I have a windows application using SqlDependency running at separated thread pool, this application represents a log monitor UI get the latest rows added in a specific table in the database and view it in a DataGridView. You can see the application source code from this LINK, or follow this script.
const string tableName = "OutgoingLog";
const string statusMessage = "{0} changes have occurred.";
int changeCount = 0;
private static DataSet dataToWatch = null;
private static SqlConnection connection = null;
private static SqlCommand command = null;
public frmMain()
{
InitializeComponent();
}
private bool CanRequestNotifications()
{
// In order to use the callback feature of the
// SqlDependency, the application must have
// the SqlClientPermission permission.
try
{
SqlClientPermission perm = new SqlClientPermission(PermissionState.Unrestricted);
perm.Demand();
return true;
}
catch
{
return false;
}
}
private void dependency_OnChange(object sender, SqlNotificationEventArgs e)
{
// This event will occur on a thread pool thread.
// Updating the UI from a worker thread is not permitted.
// The following code checks to see if it is safe to
// update the UI.
ISynchronizeInvoke i = (ISynchronizeInvoke)this;
// If InvokeRequired returns True, the code
// is executing on a worker thread.
if (i.InvokeRequired)
{
// Create a delegate to perform the thread switch.
OnChangeEventHandler tempDelegate = new OnChangeEventHandler(dependency_OnChange);
object[] args = { sender, e };
// Marshal the data from the worker thread
// to the UI thread.
i.BeginInvoke(tempDelegate, args);
return;
}
// Remove the handler, since it is only good
// for a single notification.
SqlDependency dependency = (SqlDependency)sender;
dependency.OnChange -= dependency_OnChange;
// At this point, the code is executing on the
// UI thread, so it is safe to update the UI.
++changeCount;
lblChanges.Text = String.Format(statusMessage, changeCount);
// Reload the dataset that is bound to the grid.
GetData();
}
AutoResetEvent running = new AutoResetEvent(true);
private void GetData()
{
// Start the retrieval of data on another thread to let the UI thread free
ThreadPool.QueueUserWorkItem(o =>
{
running.WaitOne();
// Empty the dataset so that there is only
// one batch of data displayed.
dataToWatch.Clear();
// Make sure the command object does not already have
// a notification object associated with it.
command.Notification = null;
// Create and bind the SqlDependency object
// to the command object.
SqlDependency dependency = new SqlDependency(command);
dependency.OnChange += new OnChangeEventHandler(dependency_OnChange);
using (SqlDataAdapter adapter = new SqlDataAdapter(command))
{
adapter.Fill(dataToWatch, tableName);
try
{
running.Set();
}
finally
{
// Update the UI
dgv.Invoke(new Action(() =>
{
dgv.DataSource = dataToWatch;
dgv.DataMember = tableName;
//dgv.FirstDisplayedScrollingRowIndex = dgv.Rows.Count - 1;
}));
}
}
});
}
private void btnAction_Click(object sender, EventArgs e)
{
changeCount = 0;
lblChanges.Text = String.Format(statusMessage, changeCount);
// Remove any existing dependency connection, then create a new one.
SqlDependency.Stop("Server=.; Database=SMS_Tank_Log;UID=sa;PWD=hana;MultipleActiveResultSets=True");
SqlDependency.Start("Server=.; Database=SMS_Tank_Log;UID=sa;PWD=hana;MultipleActiveResultSets=True");
if (connection == null)
{
connection = new SqlConnection("Server=.; Database=SMS_Tank_Log;UID=sa;PWD=hana;MultipleActiveResultSets=True");
}
if (command == null)
{
command = new SqlCommand("select * from OutgoingLog", connection);
//SqlParameter prm =
// new SqlParameter("#Quantity", SqlDbType.Int);
//prm.Direction = ParameterDirection.Input;
//prm.DbType = DbType.Int32;
//prm.Value = 100;
//command.Parameters.Add(prm);
}
if (dataToWatch == null)
{
dataToWatch = new DataSet();
}
GetData();
}
private void frmMain_Load(object sender, EventArgs e)
{
btnAction.Enabled = CanRequestNotifications();
}
private void frmMain_FormClosing(object sender, FormClosingEventArgs e)
{
SqlDependency.Stop("Server=.; Database=SMS_Tank_Log;UID=sa;PWD=hana;MultipleActiveResultSets=True");
}
The problem:
I have many situations of errors, (images in the first comment)
(No. 1):
I got this error dialog, and I don't know its reason.
(No. 2):
I got nothing in my grid view (No errors, and no data).
(No. 3):
I got only columns names and no rows, although the table has rows.
I need help please.
I may be wrong but a DataSet does not seem to have notification capability so the DataGridView may be surprised if you change it behind its back.
You could try to explicitly show your're changing the data source by first setting it to null:
dgv.DataSource = null;
dgv.DataSource = dataToWatch;
dgv.DataMember = tableName;
It's worth a try...

Prevent sending email and show message via plug-in

I am writing crm2011 plugin in "Email" entity with "Send" Message of Pre_operation. What i want to do is when i click "Send" button in email entity, I do the necessary checking before send. If the checking is not correct, I want to prevent and stop the sending email and show "the alert message" and stop the second plugin(this plugin send email and create the associated entity to convert "Case"). Please give me some suggestion for that plugin?
Should i use pre-Validation stage or Pre_operation state? And how can I return false to stop plugin.
public void Execute(IServiceProvider serviceProvider)
{
try
{
string message = null;
_serviceProvider = serviceProvider;
_context = (IPluginExecutionContext)
serviceProvider.GetService(typeof(IPluginExecutionContext));
_serviceFactory = (IOrganizationServiceFactory)serviceProvider.GetService(typeof(IOrganizationServiceFactory));
_currentUser = _context.UserId;
message = _context.MessageName.ToLower();
if (message == "send")
{
if (_context.InputParameters != null && _context.InputParameters.Contains("EmailId"))
{
object objEmailId = _context.InputParameters["EmailId"];
if (objEmailId != null)
{
_emailId = new Guid(objEmailId.ToString());
FindEmailInfo();
if (_email != null)
{
if (_email.Attributes.Contains("description") && _email.Attributes["description"] != null)//Email descritpion is not null
{
string emaildescription = StripHTML();
//Find KB Article prefix no in system config entity
serviceguideprefix = "ServiceGuidesPrefix";
QueryByAttribute query = new QueryByAttribute("ppp_systemconfig");
query.ColumnSet = new ColumnSet(true);
query.AddAttributeValue(sysconfig_name, serviceguideprefix);
EntityCollection sysconfig = _service.RetrieveMultiple(query);
if (sysconfig.Entities.Count > 0)
{
Entity e = sysconfig.Entities[0];
if (e.Attributes.Contains("ppp_value"))
{
ppp_value = e.Attributes["ppp_value"].ToString();
}
}
if (ppp_value != null && ppp_value != string.Empty)
{
//var matches = Regex.Matches(emaildescription, #"KBA-\d*-\w*").Cast<Match>().ToArray();
var matches = Regex.Matches(emaildescription, ppp_value + #"-\d*-\w*").Cast<Match>().ToArray();
//ReadKBNo(emaildescription);
foreach (Match kbnumber in matches)
{
EntityCollection kbarticlecol = FindKBArticleIds(kbnumber.ToString());
if (kbarticlecol.Entities.Count > 0)
{
Entity kbariticle = kbarticlecol.Entities[0];
if (kbariticle.Attributes.Contains("mom_internalkm"))
{
bool internalserviceguide = (bool)kbariticle.Attributes["mom_internalkm"];
if (internalserviceguide) found = true;
else found = false;
}
else found = false;
}
}
}
if (found)
{
//-----
}
}
}
}
}
}
}
catch (Exception ex)
{
throw new InvalidPluginExecutionException(ex.Message, ex);
}
}
Well stopping the plugin is dead easy you just throw InvalidPluginException, the message you give it will be shown to the user in a alert window. You will have to do this on the pre of the send. In this case I don't think it will matter if its pre-validation or pre-operation.
Edit:
Yes, you should throw an InvalidPluginException even if no exception has happened in code. I accept this isnt what we would normally do, but its the way its meant to work. Msdn has more details: http://msdn.microsoft.com/en-us/library/gg334685.aspx
So for example the code would look like:
public void Execute(IServiceProvider serviceProvider)
{
try
{
//This is where we validate the email send
if(emailIsOkay)
{
//Do something
}
else if(emailIsNotOkay)
{
//Throw and exception that will stop the plugin and the message will be shown to the user (if its synchronous)
throw new InvalidPluginExecutionException("Hello user, your email is not correct!!");
}
}
catch (InvalidPluginExecutionException invalid)
{
//We dont to catch exception for InvalidPluginExecution, so just throw them on
throw;
}
catch (Exception ex)
{
//This exception catches if something goes wrong in the code, or some other process.
throw new InvalidPluginExecutionException(ex.Message, ex);
}
}
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Microsoft.Crm;
using Microsoft.Xrm.Sdk;
using System.ServiceModel;
using Microsoft.Xrm.Sdk.Query;
using Microsoft.Crm.Sdk.Messages;
using System.Text.RegularExpressions;
using System.Xml.Linq;
namespace SendEmail
{
public class Email : IPlugin
{
public void Execute(IServiceProvider serviceprovider)
{
IPluginExecutionContext context = (IPluginExecutionContext)serviceprovider.GetService(typeof(IPluginExecutionContext));
if (!(context.InputParameters.Contains("Target") && context.InputParameters["Target"] is Entity))
return;
//entity
Entity ent = (Entity)context.InputParameters["Target"];
if (ent.LogicalName != "entityName")//EntityName
throw new InvalidPluginExecutionException("Not a Service Request record! ");
//service
IOrganizationServiceFactory serviceFactory = (IOrganizationServiceFactory)serviceprovider.GetService(typeof(IOrganizationServiceFactory));
IOrganizationService _service = serviceFactory.CreateOrganizationService(context.UserId);
string Email="";
if (ent.Contains("emailidfiled"))
Email = (string)ent["emailidfiled"];
#region email template
QueryExpression query = new QueryExpression()
{
EntityName = "template",
Criteria = new FilterExpression(LogicalOperator.And),
ColumnSet = new ColumnSet(true)
};
query.Criteria.AddCondition("title", ConditionOperator.Equal, "templateName");
EntityCollection _coll = _service.RetrieveMultiple(query);
if (_coll.Entities.Count == 0)
throw new InvalidPluginExecutionException("Unable to find the template!");
if (_coll.Entities.Count > 1)
throw new InvalidPluginExecutionException("More than one template found!");
var subjectTemplate = "";
if (_coll[0].Contains("subject"))
{
subjectTemplate = GetDataFromXml(_coll[0]["subject"].ToString(), "match");
}
var bodyTemplate = "";
if (_coll[0].Contains("body"))
{
bodyTemplate = GetDataFromXml(_coll[0]["body"].ToString(), "match");
}
#endregion
#region email prep
Entity email = new Entity("email");
Entity entTo = new Entity("activityparty");
entTo["addressused"] =Email;
Entity entFrom = new Entity("activityparty");
entFrom["partyid"] = "admin#admin.com";
email["to"] = new Entity[] { entTo };
email["from"] = new Entity[] { entFrom };
email["regardingobjectid"] = new EntityReference(ent.LogicalName, ent.Id);
email["subject"] = subjectTemplate;
email["description"] = bodyTemplate;
#endregion
#region email creation & sending
try
{
var emailid = _service.Create(email);
SendEmailRequest req = new SendEmailRequest();
req.EmailId = emailid;
req.IssueSend = true;
GetTrackingTokenEmailRequest wod_GetTrackingTokenEmailRequest = new GetTrackingTokenEmailRequest();
GetTrackingTokenEmailResponse wod_GetTrackingTokenEmailResponse = (GetTrackingTokenEmailResponse)
_service.Execute(wod_GetTrackingTokenEmailRequest);
req.TrackingToken = wod_GetTrackingTokenEmailResponse.TrackingToken;
_service.Execute(req);
}
catch (Exception ex)
{
throw new InvalidPluginExecutionException("Email can't be saved / sent." + Environment.NewLine + "Details: " + ex.Message);
}
#endregion
}
private static string GetDataFromXml(string value, string attributeName)
{
if (string.IsNullOrEmpty(value))
{
return string.Empty;
}
XDocument document = XDocument.Parse(value);
// get the Element with the attribute name specified
XElement element = document.Descendants().Where(ele => ele.Attributes().Any(attr => attr.Name == attributeName)).FirstOrDefault();
return element == null ? string.Empty : element.Value;
}
}
}

Submitting a WebRequest from within a CLR Trigger

I just implemented a prototype solution for updating my caching server in real-time by assigning a CLR Trigger to a table so that whenever a certain column is updated the URL called from the trigger will update the caching server with the correct data.
It's working fine and the code is as follows:
[Microsoft.SqlServer.Server.SqlTrigger(Name = "AdStatusChanged", Target = "Ads", Event = "FOR UPDATE")]
public static void AdStatusChanged()
{
SqlTriggerContext triggContext = SqlContext.TriggerContext;
int adID = 0, adStatusID_Old = 0, adStatusID_New = 0;
if (triggContext.TriggerAction == TriggerAction.Update)
{
using (SqlConnection conn = new SqlConnection("context connection=true"))
{
conn.Open();
SqlCommand sqlComm = new SqlCommand();
SqlPipe sqlP = SqlContext.Pipe;
sqlComm.Connection = conn;
sqlComm.CommandText = "SELECT AdID, AdStatusID from INSERTED";
SqlDataReader reader = sqlComm.ExecuteReader();
if (reader.Read())
{
adID = reader.GetInt32(0);
adStatusID_New = reader.GetInt32(1);
}
reader.Close();
sqlComm.CommandText = "SELECT AdID, AdStatusID from DELETED WHERE AdID = " + adID;
reader = sqlComm.ExecuteReader();
if (reader.Read())
{
adID = reader.GetInt32(0);
adStatusID_Old = reader.GetInt32(1);
}
}
if (adID == 0 || adStatusID_New == adStatusID_Old)
{
// Check could be more thorough !
return;
}
WebResponse httpResponse = null;
try
{
string apiURL = string.Format("{0}/{1}", "http://localhost:14003/Home", "UpdateAdStatus?adID=" + adID + "&adStatusID=" + adStatusID_New);
var httpWebRequest = (HttpWebRequest)WebRequest.Create(apiURL);
httpWebRequest.Method = "GET";
httpResponse = (HttpWebResponse)httpWebRequest.GetResponse();
// check for successful response
}
catch (Exception ex)
{
Log("WebRequest from within SQL Server failed ! " + ex.Message);
}
finally
{
if (httpResponse != null)
{
httpResponse.Close();
}
}
}
}
I would like to have some expert/experienced views on the "CONS" of this approach regarding performance, deadlocks, sql crashing, or other areas that could be of potential concern.
Has anyone tried this (I'm sure many must have) and what was the result ? a successful implementation or did you revert to some other method or updating the cache real-time?