I'm having some trouble with the new MS CRM 2015 as I cannot make my plugins fire.
I even tried to take some of the very simple plugin samples from the 2015 SDK and register them 'non sandbox' but the result is the same.
The only thing I cant actually get to do anything on the triggering event is the plugin profiler, but that doesn't really help me much.
Has anyone else had this problem?
I could really use some advice on what to try/check next because Google doesn't appear to be my friend in this case?
The current deployment is on-premise by the way but there is no Visual Studio available on the server.
Here is the code from sample which I have altered just a tiny bit to only trigger on 1 specific account.
using System;
using System.ServiceModel;
using Microsoft.Xrm.Sdk;
namespace Microsoft.Crm.Sdk.Samples
{
public class FollowupPlugin: IPlugin
{
public void Execute(IServiceProvider serviceProvider)
{
//Extract the tracing service for use in debugging sandboxed plug-ins.
ITracingService tracingService = (ITracingService)serviceProvider.GetService(typeof(ITracingService));
// Obtain the execution context from the service provider.
IPluginExecutionContext context = (IPluginExecutionContext)serviceProvider.GetService(typeof(IPluginExecutionContext));
//</snippetFollowupPlugin1>
//<snippetFollowupPlugin2>
// The InputParameters collection contains all the data passed in the message request.
if (context.InputParameters.Contains("Target") && context.InputParameters["Target"] is Entity)
{
// Obtain the target entity from the input parameters.
Entity entity = (Entity)context.InputParameters["Target"];
//</snippetFollowupPlugin2>
// Verify that the target entity represents an account.
// If not, this plug-in was not registered correctly.
if (entity.LogicalName != "account")
return;
try
{
if (entity.Attributes.ContainsKey("accountid"))
{
if (entity.GetAttributeValue<Guid>("accountid").ToString().ToLower() == "ee03d883-5b18-de11-a0d1-000c2962895d") // specific test account
{
// Create a task activity to follow up with the account customer in 7 days.
Entity followup = new Entity("task");
followup["subject"] = "Send e-mail to the new customer.";
followup["description"] = "Follow up with the customer. Check if there are any new issues that need resolution.";
followup["scheduledstart"] = DateTime.Now.AddDays(7);
followup["scheduledend"] = DateTime.Now.AddDays(7);
followup["category"] = context.PrimaryEntityName;
// Refer to the account in the task activity.
if (context.OutputParameters.Contains("id"))
{
Guid regardingobjectid = new Guid(context.OutputParameters["id"].ToString());
string regardingobjectidType = "account";
followup["regardingobjectid"] = new EntityReference(regardingobjectidType, regardingobjectid);
}
//<snippetFollowupPlugin4>
// Obtain the organization service reference.
IOrganizationServiceFactory serviceFactory = (IOrganizationServiceFactory)serviceProvider.GetService(typeof(IOrganizationServiceFactory));
IOrganizationService service = serviceFactory.CreateOrganizationService(context.UserId);
//</snippetFollowupPlugin4>
// Create the task in Microsoft Dynamics CRM.
tracingService.Trace("FollowupPlugin: Creating the task activity.");
service.Create(followup);
}
}
}
//<snippetFollowupPlugin3>
catch (FaultException<OrganizationServiceFault> ex)
{
throw new InvalidPluginExecutionException("An error occurred in the FollupupPlugin plug-in.", ex);
}
//</snippetFollowupPlugin3>
catch (Exception ex)
{
tracingService.Trace("FollowupPlugin: {0}", ex.ToString());
throw;
}
}
}
}
}
Registration screenshots:
Try to use following code:
using System;
using System.ServiceModel;
using Microsoft.Xrm.Sdk;
namespace Microsoft.Crm.Sdk.Samples
{
public class FollowupPlugin: IPlugin
{
public void Execute(IServiceProvider serviceProvider)
{
//Extract the tracing service for use in debugging sandboxed plug-ins.
ITracingService tracingService = (ITracingService)serviceProvider.GetService(typeof(ITracingService));
// Obtain the execution context from the service provider.
IPluginExecutionContext context = (IPluginExecutionContext)serviceProvider.GetService(typeof(IPluginExecutionContext));
//</snippetFollowupPlugin1>
//<snippetFollowupPlugin2>
// The InputParameters collection contains all the data passed in the message request.
if (context.InputParameters.Contains("Target") && context.InputParameters["Target"] is Entity)
{
// Obtain the target entity from the input parameters.
Entity entity = (Entity)context.InputParameters["Target"];
//</snippetFollowupPlugin2>
// Verify that the target entity represents an account.
// If not, this plug-in was not registered correctly.
if (entity.LogicalName != "account")
return;
try
{
if (entity.Id.Equals(new Guid("ee03d883-5b18-de11-a0d1-000c2962895d")) // specific test account
{
// Create a task activity to follow up with the account customer in 7 days.
Entity followup = new Entity("task");
followup["subject"] = "Send e-mail to the new customer.";
followup["description"] = "Follow up with the customer. Check if there are any new issues that need resolution.";
followup["scheduledstart"] = DateTime.Now.AddDays(7);
followup["scheduledend"] = DateTime.Now.AddDays(7);
followup["category"] = context.PrimaryEntityName;
followup["regardingobjectid"] = entity.ToEntityReference();
//<snippetFollowupPlugin4>
// Obtain the organization service reference.
IOrganizationServiceFactory serviceFactory = (IOrganizationServiceFactory)serviceProvider.GetService(typeof(IOrganizationServiceFactory));
IOrganizationService service = serviceFactory.CreateOrganizationService(context.UserId);
//</snippetFollowupPlugin4>
// Create the task in Microsoft Dynamics CRM.
tracingService.Trace("FollowupPlugin: Creating the task activity.");
service.Create(followup);
}
}
//<snippetFollowupPlugin3>
catch (FaultException<OrganizationServiceFault> ex)
{
throw new InvalidPluginExecutionException("An error occurred in the FollupupPlugin plug-in.", ex);
}
//</snippetFollowupPlugin3>
catch (Exception ex)
{
tracingService.Trace("FollowupPlugin: {0}", ex.ToString());
throw;
}
}
}
}
}
Related
I am working with the Google Provisioning API. I have used Web Application type project from Google developer console. I have used Diamto blog and samples and it works perfectly on my local with all options like FileStore, Custom File Store, Service Account etc but when I uploaded on server user consent screen just doesn't pops up with any options like FileStore, Custom File Store. I spent days to figure out problem and solutions but nothing has worked for me so far.
my configuration
My server configuration is windows server 2008 datacenter r2,.net 4.5,IIS 7.5.
Service account works perfectly but I need to do it by Consent screen so Web Application type of project.
I have used google .net client library with version 1.9.2.27817.
I am just highlighting main code where it gets stuck and rest is same as per Diamto post and github examples.
Let me know if you need more info.
Code
public static DirectoryService AuthenticateOauth(string clientId, string clientSecret, string userName, IDataStore datastore)
{
string[] scopes = new string[] {DirectoryService.Scope.AdminDirectoryUser };
try
{
// here is where we Request the user to give us access, or use the Refresh Token that was previously stored in %AppData%
UserCredential credential = GoogleWebAuthorizationBroker.AuthorizeAsync(new ClientSecrets { ClientId = clientId, ClientSecret = clientSecret }
, scopes
, userName
, CancellationToken.None
, datastore).Result; // at this point it calls getasynch method for custom datasource
DirectoryService service = new DirectoryService(new BaseClientService.Initializer()
{
HttpClientInitializer = credential,
ApplicationName = "GoogleProv",
});
return service;
}
catch (Exception ex)
{
Console.WriteLine(ex.InnerException);
return null;
}
}
{
HttpClientInitializer = credential,
ApplicationName = "GoogleProv",
});
return service;
}
catch (Exception ex)
{
Console.WriteLine(ex.InnerException);
return null;
}
}
///<summary>
// Returns the stored value for the given key or <c>null</c> if the matching file (<see cref="GenerateStoredKey"/>
// in <see cref="FolderPath"/> doesn't exist.
// </summary>
// <typeparam name="T">The type to retrieve</typeparam>
// <param name="key">The key to retrieve from the data store</param>
// <returns>The stored object</returns>
public Task<T> GetAsync<T>(string key)
{
//Key is the user string sent with AuthorizeAsync
if (string.IsNullOrEmpty(key))
{
throw new ArgumentException("Key MUST have a value");
}
TaskCompletionSource<T> tcs = new TaskCompletionSource<T>();
// Note: create a method for opening the connection.
SqlConnection myConnection = new SqlConnection(myconn);
myConnection.Open();
// Try and find the Row in the DB.
using (SqlCommand command = new SqlCommand("select RefreshToken from GoogleUser where UserName = #username;", myConnection))
{
command.Parameters.AddWithValue("#username", key);
string RefreshToken = null;
SqlDataReader myReader = command.ExecuteReader();
while (myReader.Read())
{
RefreshToken = myReader["RefreshToken"].ToString();
}
if (RefreshToken == null )
{
// we don't have a record so we request it of the user.
tcs.SetResult(default(T)); // it comes here
}
else
{
try
{
// we have it we use that.
tcs.SetResult(NewtonsoftJsonSerializer.Instance.Deserialize<T>(RefreshToken));
}
catch (Exception ex)
{
tcs.SetException(ex);
}
}
}
return tcs.Task; // it comes here and than gets hang forever
}
Any of your help is highly appreciated.
I'm a new developper in CRM 2011. I've made that simple code for tests.
It appears that the plugin works fine (checked with the ITracingService) but it seems that the attributes doesn't take the new values.
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Microsoft.Xrm.Sdk.Query;
using Microsoft.Xrm.Sdk;
namespace AgeUpdatePlugin
{
public class AgeUpdatePlugin:IPlugin
{
public void Execute(IServiceProvider serviceProvider)
{
IPluginExecutionContext context = (IPluginExecutionContext)serviceProvider.GetService(typeof(IPluginExecutionContext));
try
{
IOrganizationServiceFactory serviceFactory = (IOrganizationServiceFactory)serviceProvider.GetService(typeof(IOrganizationServiceFactory));
ITracingService tracingService = (ITracingService)serviceProvider.GetService(typeof(ITracingService));
if (tracingService == null)
throw new InvalidPluginExecutionException("Failed to retrieve the tracing service.");
IOrganizationService service = serviceFactory.CreateOrganizationService(context.UserId);
tracingService.Trace("Plugin has started..");
//l'expression de condition
ConditionExpression condition = new ConditionExpression("new_datenaissance", ConditionOperator.NotNull);
FilterExpression filter = new FilterExpression();
filter.Conditions.Add(condition);
ColumnSet cols = new ColumnSet();
//cols.AddColumn("Id");
cols.AddColumn("new_candidatid");
cols.AddColumn("new_age");
cols.AddColumn("new_datenaissance");
QueryExpression query = new QueryExpression();
query.Criteria = filter;
query.EntityName = "new_candidat";
query.ColumnSet = cols;
tracingService.Trace("appel retrieve multiple with date=" + DateTime.Now.ToString() + "/"+DateTime.Now.ToLocalTime().ToString());
var retrieve = service.RetrieveMultiple(query).Entities;
foreach (var c in retrieve)
{
var dt =(DateTime)c.Attributes["new_datenaissance"];
tracingService.Trace(dt.ToString());
if (dt.Month == DateTime.Now.Month && dt.Day == DateTime.Now.Day)
{
tracingService.Trace((DateTime.Now.Year - DateTime.Parse(c["new_datenaissance"].ToString()).Year).ToString());
c.Attributes["new_age"] = DateTime.Now.Year - DateTime.Parse(c["new_datenaissance"].ToString()).Year;
tracingService.Trace(c.Attributes["new_candidatid"].ToString() +" - "+c.Attributes["new_age"].ToString());
service.Update(c);
tracingService.Trace("updated");
}
}
tracingService.Trace("Plugin done working");
throw new InvalidPluginExecutionException("This is from a plugin that Mehdi has created ");
}
catch (Exception exc)
{
throw exc;
}
}
}
}
Whenever you throw an exception within a plugin the transaction is aborted so you won't see your changes applied. Remove the following line:
throw new InvalidPluginExecutionException("This is from a plugin that Mehdi has created ");
I assume you added this to see the trace output which is only available when an exception is thrown. With CRM Online your best bet for tracing is to write to a custom trace entity.
Is it possible to get the identity of a record after the SaveChanges method but prior to the TransactionScope.Complete call using the DbContext? Do we need to cast to ObjectContext to get this functionality?
We need this when we send a message to a transactional queue on a separate server using MSDTC.
Example:
static void Main(string[] args)
{
const string qName = ".\\Private$\\DbContextTransactions";
var db = new BlogDb();
// force db creation & create queue
Console.WriteLine("Count={0}", db.Posts.Count());
if (!MessageQueue.Exists(qName))
MessageQueue.Create(qName, true); // transactional
try
{
using (var t = new TransactionScope())
using (MessageQueue q = new MessageQueue(qName))
{
var post = new Post() { Title = "Test " + DateTime.Now.Ticks.ToString() };
db.Posts.Add(post);
db.SaveChanges();
// TODO: how do we get post.Id (updated identity)?
q.Send(post.Id, MessageQueueTransactionType.Automatic);
t.Complete();
}
}
catch (Exception ex)
{
Console.WriteLine(ex);
}
Console.WriteLine("Count={0}", db.Posts.Count());
Console.ReadLine();
}
The identity value should always be set after you call SaveChanges, even inside a TransactionScope.
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];
I am using http://code.google.com/p/autofac/wiki/MultitenantIntegration version 2.4.4 with Autofac 2.4.4 on an ASP.Net MVC 3.0.
I use the new Asp.Net MVC 3 support (using AutofacDependencyResolver). I encounter a problem that the tenant identification strategy class (implementing ITenantIdentificationStrategy) throws "Request is not available in this context" exception.
I tried using AutofacContrib.Multitenant.Web.RequestParameterTenantIdentificationStrategy class and it also throws the same exception.
My application_start looks as follow
protected void Application_Start()
{
//wire up all the necessary objects used in this web application
IContainer container = BootStrap.RegisterAll().Build();
//multi tenant support
MultitenantContainer multiTenant = new MultiTenancy().Register(container);
DependencyResolver.SetResolver(new AutofacDependencyResolver(multiTenant.BeginLifetimeScope()));
}
Never mind. HttpContext.Current.Request is not available at Application_Start in IIS 7.0. The only solution to this is to capture the HTTPException and set the TenantId to null at Catch and return false.
public bool TryIdentifyTenant(out object tenantId)
{
var current = HttpContext.Current;
try
{
if (current == null)
{
tenantId = null;
return false;
}
var request = current.Request;
}
catch (HttpException)
{
tenantId = null;
return false;
}
//continue with your tenant identification
}