Tag Archives: CRM Development

Der Überwachungsverlauf zeigt Updates auch auf nicht veränderten Properties

Folgendes Szenario: Ich hole mir einen Account (allen seinen Feldern), verändere dessen Namen und speichere diese Änderung.
Das könnte z.B. mit folgendem Code gemacht werden.

var account = OrganizationService.RetrieveMultiple(
    new QueryExpression("account") { ColumnSet = new ColumnSet(true) })
    .Entities.First();

OrganizationService.Update(account);

Ohne Frage, es funktioniert. Es gibt jedoch ein Problem. Das CRM ist nicht intelligent genug um wirklich nur die veränderten Felder zu verändern. Ein Blick in den Audit Log verrät, dass alle Felder verändert werden.

Update_1

Es gibt drei Gründe weshalb das nicht gewünscht sein könnte.
1. Performance
2. Unübersichtlicher Audit Log
3. Workflow/Plug-Ins welche auf dem Field Change getriggert werden

Was ist die Lösung? Am einfachsten ist es jeweils eine neue Instanz einer “Entity” zu erstellen. Dabei wird die Id des Records gesetzt, welcher updated werden soll. Danach wird nur das Feld gesetzt, dass den geänderten Wert enthält.

var account = OrganizationService.RetrieveMultiple(
    new QueryExpression("account") { ColumnSet = new ColumnSet(true) })
    .Entities.First();

var updateAccount = new Entity("account") { Id = account.Id };
updateAccount["name"] = "Neuer Name2";

OrganizationService.Update(updateAccount);

Ein erneuter Blick in den Audit Log zeigt, dass diese Lösung wie gewünscht funktioniert.

Update_2

Plug-Ins und Instanzvariablen

Ein Plug-In für CRM implementiert das Interface “IPlugin” welches die Methode “public void Execute(IServiceProvider serviceProvider)” vorschreibt.
Diese Methode wird aufgerufen sobald das Plug-In ausgeführt wird und enthält die Logik. Aber wann wird die Plug-In Klasse instantiiert?

Auf MSDN ist im Artikel Write a plugin-in folgender Text, in einem mit “Important” markierten Abschnitt, zu finden:

For improved performance, Microsoft Dynamics CRM caches plug-in instances. The plug-in’s Execute method should be written to be stateless because the constructor is not called for every invocation of the plug-in. Also, multiple system threads could execute the plug-in at the same time. All per invocation state information is stored in the context, so you should not use global variables or attempt to store any data in member variables for use during the next plug-in invocation unless that data was obtained from the configuration parameter provided to the constructor. Changes to a plug-ins registration will cause the plug-in to be re-initialized.

Zusammengefasst heisst dass ein Plug-In wird einmal instantiiert und danach wird nur noch jeweils die Execut Methode ausgeführt.
Aus diesem Grund sollten keine Instanzvariablen verwendet werden.

Folgendes sollte also nicht gemacht werden.
Das Plug-In wird trotzdem funktionieren, es resultiert aber in einem unerwartetem Verhalten.

public class MyPlugin : IPlugin
{
    private ITracingService tracingService;
    private IPluginExecutionContext context;
    private Entity entity;

    public void Execute(IServiceProvider serviceProvider)
    {
        tracingService = (ITracingService)serviceProvider.GetService(typeof(ITracingService));
        context = (IPluginExecutionContext)serviceProvider.GetService(typeof(IPluginExecutionContext));
        entity = (Entity)context.InputParameters["Target"];

        DoThis();
        DoThat();
    }

    private void DoThis()
    {
        // Do something (using tracingService, context and entity)
    }

    private void DoThat()
    {
        // Do something (using tracingService, context and entity)
    }
}

Der Code muss wie folgt geschrieben werden, damit er erwartungsgemäss funktioniert.

public class MyPlugin : IPlugin
{
    public void Execute(IServiceProvider serviceProvider)
    {
        var tracingService = (ITracingService)serviceProvider.GetService(typeof(ITracingService));
        var context = (IPluginExecutionContext)serviceProvider.GetService(typeof(IPluginExecutionContext));
        var entity = (Entity)context.InputParameters["Target"];

        DoThis(tracingService, context, entity);
        DoThat(tracingService, context, entity);
    }

    private void DoThis(ITracingService tracingService, IPluginExecutionContext context, Entity currentEntity)
    {
        // Do something
    }

    private void DoThat(ITracingService tracingService, IPluginExecutionContext context, Entity currentEntity)
    {
        // Do something
    }
}

Diese Lösung ist natürlich suboptimal und führt zu unleserlichem Code.
Eine bessere Option stellt die Verwendung einer privaten Klasse dar um die Logik zu kapseln. Somit können Instanzvariablen verwendet werden. Am besten werden gleich alle Abhängigkeiten zum Plug-In abstrahiert. D.h. anstatt den IPluginExecutionContext gebe ich nur die benötigten Eigenschaften über den Konstruktor von “PluginLogic” weiter. Somit lässt sich der Code auch ausserhalb eines Plug-Ins ausführen und/oder mit Unit Tests testen.

public class MyPlugin : IPlugin
{
    public void Execute(IServiceProvider serviceProvider)
    {
        var tracingService = (ITracingService)serviceProvider.GetService(typeof(ITracingService));
        var context = (IPluginExecutionContext)serviceProvider.GetService(typeof(IPluginExecutionContext));
        var entity = (Entity)context.InputParameters["Target"];

        var pluginLogic = new PluginLogic(tracingService, context, entity);
        pluginLogic.Run();
    }

    private class PluginLogic
    {
        private readonly ITracingService tracingService;
        private readonly IPluginExecutionContext context;
        private readonly Entity currentEntity;

        public PluginLogic(ITracingService tracingService, IPluginExecutionContext context, Entity currentEntity)
        {
            this.tracingService = tracingService;
            this.context = context;
            this.currentEntity = currentEntity;
        }

        public void Run()
        {
            DoThis();
            DoThat();
        }

        private void DoThis()
        {
            // Do something
        }

        private void DoThat()
        {
            // Do something
        }
    }
}

Impersonating/ActOnBehalf Benutzer NACH dem erstellen des IOrganizationServices

Die Klasse CrmConnection bietet eine sehr komfortable Möglichkeit um sich zu einem CRM Webservice zu verbinden. Sie verfügt über ein Property “CallerId”, mit dem es möglich ist ein Benutzer zu impersonaten.
Wird nun von der “CrmConnection” aus ein “IOrganizationService” erstellt (Instanz von “OrganizationService”) und die “CallerId” der CrmConnection nachträglich geändert, interessiert das den “OrganizationService” herzlich wenig.

Ein “OrganizationServiceProxy” verfügt ebenfalls über das Property “CallerId”.
Der Konstruktur besitzt jedoch keine Überladung die eine Instanz der “CrmConnection” Klasse erwartet.

Der folgende Code zeigt wie eine nachträgliche Impersonation trotzdem möglich ist.

var typedOrganizationService = organizationService as OrganizationService;
if (typedOrganizationService != null)
{
    var organizationServiceProxy = typedOrganizationService.InnerService as OrganizationServiceProxy;
    if (organizationServiceProxy != null)
    {
        organizationServiceProxy.CallerId = userId;
    }
    else
    {
        logger.Error("Impersonation failed! The OrganizationService.InnerService was not an implemenation of OrganizationServiceProxy.");
    }
}
else
{
    logger.Error("Impersonation failed! The IOrganizationService was not an implemenation of OrganizationService.");
}

Plug-In auf Close Message eines Incidents – wo ist die Incident Id?

Wird ein Plug-In auf die “Close” Message eines Incidents registriert, wird das Plug-In gefeuert, sobald der Incident geschlossen wird. Die “PrimaryEntityId” des “IPluginExecutionContext” ist jedoch leer (Guid.Empty). Wie erhalte ich die Id des Incidents? Die Id des Incidents, welches die “Close” Message ausgelöst hat erhält man wie folgt:

var context = (IPluginExecutionContext)serviceProvider.GetService(typeof(IPluginExecutionContext));
var incidentResolutionEntity = (Entity)context.InputParameters["IncidentResolution"];
Guid incidentId = ((EntityReference)incidentResolutionEntity.Attributes["incidentid"]).Id;

System Administrator Role identifizieren

Basierend auf der gewählten Base Language der CRM Organization haben die Roles (Sicherheitsrollen) unterschiedliche Namen. So heisst der “System Administrator” auf Deutsch “Systemadministrator”. Es stellt sich nun die Frage, wie man eine Role organisationsübergreifend und unabhängig von der Base Language identifizieren kann.
Continue reading

Query einer Ansicht im Code ausführen

Es kommt häufig vor das eine bestimmte Aktion auf einer Menge Records einer Entität ausgeführt werden soll. Natürlich soll konfiguriert werden können welche Records das sind. Was liegt da näher als die bereits vorhandene “Advance Find” zu verwenden um diesen Filter zu definieren? Continue reading

Referenzen auf Records in Workflows/Workflow Activities

Wird in einem Workflow bzw. in einer Workflow Activity auf einen Record einer Entität zugegriffen, muss diese Referenz beim Deployment auf ein anderes System jeweils neu gesetzt werden. Der Grund ist einfach: Die Id’s der Records stimmen nicht überein. Es gibt verschiedene Möglichkeiten wie das gehandelt werden kann. Eine davon zeige ich in diesem Post.
Continue reading

CRM Customizations VS/TFS Integration

Microsoft Dynamics CRM bietet die optimale Grundlage um schnell Projekte abwickeln zu können. Viele Anpassungen an der Applikation erfordern keinen Code. Die Anpassungen werden als „Solutions“, in Form einer ZIP-Datei, exportiert. In Enterprise Szenarien soll jedoch auch für diese Anpassungen die volle ALM Funktionalität von Visual Studio und TFS genutzt werden können.

Continue reading