Tag Archives: CRM

Dynamics CRM API: E-Mail von Template erstellen

Das CRM bietet die Möglichkeit, E-Mail Templates zu erstellen. Basierend auf diesen Templates können dann neue E-Mails erstellt werden. Das Funktioniert natürlich auch über die API.

Als erstes der Code um eine E-Mail Template bei gegebenem Namen zu holen. Nebst dem Namen wird auch noch der TypeCode berücksichtigt. Der TypeCode ist nichts anderes als der Schemaname der Entität, für die das Template erstellt wurde.

private Guid GetTemplateIdByName(string templateName, string templateTypeCode)
{
    var query = new QueryByAttribute("template");
    query.AddAttributeValue("title", templateName);
    query.AddAttributeValue("templatetypecode", templateTypeCode);
    return organizationService.RetrieveMultiple(query).Entities.Single().Id;
}

In einem zweiten Schritt wird vom Template, mit Hilfe des “InstantiateTemplateRequest”, eine neue E-Mail erstellt. Das heisst, es wird ein neuer Record vom der Entity “E-Mail” im Response zurückgegeben. Dieser Record wurde jedoch im CRM erstellt. Der InstantiateTemplateRequest erwartet die folgenden Properties:

  • ObjectI: Die Id des Records für den die E-Mail erstellt werden soll
  • ObjectType: Der Name des Records für den die E-Mail erstellt werden soll
  • TemplateId: Die Id des Templates welches verwendet werden soll
private Email CreateEmailFromTemplate(Entity regardingEntity, string templateName)
{
    var templateId = GetTemplateIdByName(templateName, "contact");

    var request = new InstantiateTemplateRequest
    {
        ObjectId = regardingEntity.Id,
        ObjectType = regardingEntity.LogicalName,
        TemplateId = templateId
    };

    var repsonse = (InstantiateTemplateResponse)organizationService.Execute(request);
    return repsonse.EntityCollection.Entities.Single().ToEntity<Email>();
}

Schlussendlich kann die, vom Template generierte, E-Mail bearbeitet (z.B. Hinzufügen Empfänger) und mit einem “CreateRequest” erstellt werden. Wie ich dieser E-Mail dann ein Attachment hinzufügen und sie schlussendlich versenden kann, zeige ich in meinem Post von nächster Woche.

var email = CreateEmailFromTemplate(myCase, "myTemplate")
email.Id = organizationService.Create(email);

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

Outlook & C# Serie – Introduction

In meinem letzten privaten Projekt habe ich als PoC einen Prototypen gebaut welcher den Windows Live (oder Outlook.com) Kalender mit Outlook 2013 synchronisiert. Dabei bin ich auf der Outlook 2013 Seite auf einige “Hindernisse” gestossen. Diese Hindernisse und natürlich wie sie zu überwinden sind, stelle ich in der “Outlook & C# Serie” vor.

Mit diesem Post wird erst einmal der Grundstein gelegt. Jeder Anfang ist schwer, deshalb ganz allgemein: Wie hole ich Daten aus meinem Outlook?

Es ist egal ob ein Add-in geplant ist oder ob von einer externen Applikation bsp. WPF auf Outlook Elemente zugegriffen werden soll, der Code ist derselbe. Dafür wird folgendes benötigt.

  1. VSTO (Visual Studio Tools for Office) Installieren:
    http://www.microsoft.com/en-us/download/details.aspx?id=23656

  2. Referenz hinzufügen
    Falls nun ein Add-in geschrieben werden soll, einfach ein Projekt des entsprechenden Typs erstellen. In diesem Fall soll jedoch eine externe Applikation entwickelt werden. Dafür wird ein neues Projekt erstellt und die folgende DLL referenziert:
    C:Program Files (x86)Microsoft Visual Studio 12.0Visual Studio Tools for OfficePIAOffice15Microsoft.Office.Interop.Outlook.dll

  3. Zugriff auf Outlook
    Um Zugriff auf das Outlook Objekt modell zu erhalten wird eine neue Instanz der folgenden Klasse erstellt “Microsoft.Office.Interop.Outlook.Application”.

Outlook ist in Folder organisiert. Hier ein Beispiel wie der Kalender Folder geholt werden kann:

var appointmentsFolder = (Folder)outlook.Session.GetDefaultFolder(OlDefaultFolders.olFolderCalendar);

Eine Übersicht über das Objektmodell gibt es hier: http://msdn.microsoft.com/en-us/library/ms268893.aspx

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.");
}

Dynamics CRM API: E-Mail mit attachment versenden

Eine E-Mail mit Attachment versenden. Wie geht das?
Eigentlich ist es ganz einfach. Vorausgesetzt man weiss über das Folgende Bescheid:

  • Attachments sind eine eigene Entität Namens “ActivityMimeAttachment”
  • Attachments werden den E-Mails über eine 1:n Beziehung angehängt
  • Als erstes muss das E-Mail im CRM erstellt werden
  • E-Mail erstellen != E-Mail senden

Im Post von letzter Woche habe ich gezeigt wie sich ein E-Mail von einem Template erstellen lässt. Dieser Post hier baut darauf auf und geht davon aus, dass bereits eine E-Mail im CRM existiert (E-Mail Activity wurde erstellt aber noch nicht gesendet). Nun soll dieser E-Mail Attachment angehängt werden. Schlussendlich soll die E-Mail versendet werden.

  1. Hinzufügen eines Attachments
    Der Code ist simpel und spricht für sich alleine.
private void AddAttachment(Email email, string fileName, string mimeType, byte[] content)
{
    var activityMimeAttachment = new ActivityMimeAttachment
    {
        FileName = fileName,
        Body = Convert.ToBase64String(content),
        MimeType = mimeType,
        ObjectTypeCode = email.EntityLogicalName,
        ObjectId = email.ToEntityReference()
    };

    organizationService.Create(activityMimeAttachment);
}
  1. Senden der E-Mail
private void SendEmail(Guid emailId)
{
    var reqSendEmail = new SendEmailRequest
    {
        EmailId = emailId,
        TrackingToken = string.Empty,
        IssueSend = true
    };

    organizationService.Execute(reqSendEmail);
}

SSRS Report über Webservice als PDF Rendern

Sql Server Reporting Services sind ein mächtiges Werkzeug. Eine häufige Anforderung im Entwickleralltag ist das generieren von PDF Dokumenten. Bekannter massen lässt sich ein SSRS Report auch als PDF exportieren. Ist nun schon ein SSRS Server im Einsatz kann mittels der SSRS API ein Report als PDF gerendert werden.

Die SOAP API der SQL Server Reporting Serivces besteht aus 5 Endpoints, wobei nur zwei von Belangen und einer bei der Report Generierung von Nutzen ist.

  1. Der Management Endpoint
    http:///ReportServer/ReportService2010.asmx?wsdl
    Zum verwalten (z.B. Report hochladen oder löschen) von SSRS.
  2. Der Execution Endpoint
    http:///ReportServer/ReportExecution2005.asmx?wsdl
    Zum ausführen bzw. zum Render von Reports.

Report Server Web Service Endpoints

Für unsere Anforderung, benötigen wir den Execution Endpoint. Zuerst werden, wie bei jedem anderen Web Service, aus dem WSDL die Proxy Klassen generiert. Danach kann mit der Klasse “ReportExecutionServiceSoapClient” ein Report gerendert werden. Die folgende Klasse rendert einen Report der keine Parameter entgegennimmt. Dabei kommt Windows Authentication zum Einsatz.

public class SsrsService
{
    private readonly ReportExecutionServiceSoapClient reportExecutionService;

    public SsrsService(string endpointUrl, NetworkCredential credentials)
    {
        var binding = new BasicHttpBinding
        {
            Security =
            {
                Mode = BasicHttpSecurityMode.TransportCredentialOnly,
                Transport = { ClientCredentialType = HttpClientCredentialType.Ntlm }
            }
        };

        var endpointAddress = new EndpointAddress(endpointUrl);

        reportExecutionService = new ReportExecutionServiceSoapClient(binding, endpointAddress);
        reportExecutionService.ClientCredentials.Windows.ClientCredential = credentials;
        reportExecutionService.ClientCredentials.Windows.AllowedImpersonationLevel = TokenImpersonationLevel.Impersonation;
    }

    public byte[] RenderReport(string format, string path)
    {
        var trustedUserHeader = new TrustedUserHeader();

        ExecutionInfo executionInfo;
        ServerInfoHeader serviceInfoHeader;
        reportExecutionService.LoadReport(trustedUserHeader, path, null, out serviceInfoHeader, out executionInfo);

        const string deviceInfo = @"False";
        string extension;
        string mimeType;
        byte[] result;
        string[] streamIds;
        Warning[] warnings;
        string encoding;
        var executionHeader = new ExecutionHeader { ExecutionID = executionInfo.ExecutionID };

        reportExecutionService.Render(
          executionHeader, trustedUserHeader, format, deviceInfo,
            out result, out extension, out mimeType, out encoding, out warnings, out streamIds);

        return result;
    }
}

Der Aufruf zum generieren eines Reports als PDF zeigt folgendes Snippet.

const string ReportPath = @"/FolderXY/Untitled";
var ssrsService = new SsrsService(url, CredentialCache.DefaultNetworkCredentials);
var bytes = ssrsService.RenderReport("PDF", ReportPath);

Wichtig dabei:
Der ReportPath muss mit “/” beginnen und der Report wird ohne Filename Extension angegeben.

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;