Category Archives: Crm 2011

All about Microsoft Dynamics CRM 2011

Handle birthdays in Microsoft CRM 2011/2013

The birthday field in Microsoft Dynamics CRM in the contact entity is nice, but if you want to see the birthdays today or even want to have the birthdays in your calendar you have to spend some effort. Some solutions split the birthday-date in several fields for processing => day, month. Or they use a workflow to set the next birthday.

Here I want to describe a new approach. Why don’t we use the recurring appointment for displaying the birthdates. This is a standard entity and synchronized with outlook.

Basically it is a plugin. Additionally you add a lookupfield to the contact entity to have a reference to the master recurring appointment entity.

Birthday PluginSample

 

In your code replace this name with your custom attributename:

private string appointment_birthday_attribute_name = "soho_birthday";

 

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Microsoft.Xrm.Sdk;
using Microsoft.Xrm.Sdk.Client;
using Microsoft.Xrm.Sdk.Linq;
using Microsoft.Xrm.Sdk.Query;

namespace CRM_2011_Plugins
{
    public class soho_birthday : IPlugin
    {
        public enum RecurrenceRulePatternEndType
        {
            NoEndDate = 1,
            Occurrences = 2,
            PatternEndDate = 3
        };

        public enum RecurrencePatternTypes
        {
            Daily = 0,
            Weekly = 1,
            Monthly = 2,
            Yearly = 3
        };

        public enum DayOfWeek
        {
            Sunday = 0x01,
            Monday = 0x02,
            Tuesday = 0x04,
            Wednesday = 0x08,
            Thursday = 0x10,
            Friday = 0x20,
            Saturday = 0x40
        };

        private string appointment_birthday_attribute_name = "soho_birthday";

        public void Execute(IServiceProvider serviceProvider)
        {
            IPluginExecutionContext context = (IPluginExecutionContext)serviceProvider.GetService(typeof(IPluginExecutionContext));
            IOrganizationServiceFactory serviceFactory = (IOrganizationServiceFactory)serviceProvider.GetService(typeof(IOrganizationServiceFactory));
            IOrganizationService service = serviceFactory.CreateOrganizationService(context.UserId);

            ITracingService tracingService = (ITracingService)serviceProvider.GetService(typeof(ITracingService));

            if (context.InputParameters.Contains("Target") && context.InputParameters["Target"] is Entity)
            {
                Entity entity = (Entity)context.InputParameters["Target"];
                if (entity.LogicalName == "contact" && entity.Attributes.Contains("birthdate"))
                {
                    //  if the entity is created => create the recurring appointment and update your lookupfield with the created master
                    if (context.MessageName == "Create")
                    {
                        Entity newRecurringAppointment = this.createRecurringAppointment(entity, context.UserId);
                        Guid recurringappointmentmasterID = service.Create(newRecurringAppointment);
                        Entity cUpdate = new Entity("contact");
                        cUpdate.Id = entity.Id;
                        cUpdate.Attributes.Add(appointment_birthday_attribute_name, new EntityReference("recurringappointmentmaster", recurringappointmentmasterID));
                        service.Update(cUpdate);
                    }
                    // If the entity is updated get the "old" contact
                    if (context.MessageName == "Update")
                    {
                        if (entity.LogicalName == "contact" && entity.Attributes.Contains("birthdate"))
                        {
                            Entity oldContact = service.Retrieve("contact", entity.Id, new ColumnSet(new string[] { "fullname", "ownerid", appointment_birthday_attribute_name }));
                            // if a birthday is set to null it means the birthday filed was deleted. 
                            //In this case we delete the recurring appointment
                            if (entity.Attributes["birthdate"] != null)
                            {
                                // we need the contacts fullname for the subject. so if we do not update the field we get it from the "old" contact
                                if (!entity.Attributes.Contains("fullname"))
                                    entity.Attributes.Add("fullname", oldContact["fullname"]);

                                Guid? OwnerID = null;
                                if (entity.Attributes.Contains("ownerid"))
                                {
                                    OwnerID = ((EntityReference)entity["ownerid"]).Id;
                                }
                                else
                                {
                                    OwnerID = ((EntityReference)oldContact["ownerid"]).Id;
                                }

                                // Create a new recurringappointment
                                Entity newRecurringAppointment = this.createRecurringAppointment(entity, OwnerID);

                                // If there is a existing recurringappointment pointing to birthday update this entity, else create a new one
                                if (oldContact.Attributes.Contains(appointment_birthday_attribute_name))
                                {
                                    newRecurringAppointment.Id = ((EntityReference)oldContact[appointment_birthday_attribute_name]).Id;
                                    service.Update(newRecurringAppointment);
                                    entity[appointment_birthday_attribute_name] = new EntityReference("recurringappointmentmaster", newRecurringAppointment.Id);
                                }
                                else
                                {
                                    Guid recurringappointmentmasterID = service.Create(newRecurringAppointment);
                                    entity.Attributes.Add(appointment_birthday_attribute_name, new EntityReference("recurringappointmentmaster", recurringappointmentmasterID));
                                }

                            }
                            else
                            {
                                if (oldContact.Attributes.Contains(appointment_birthday_attribute_name))
                                {
                                    service.Delete("recurringappointmentmaster", ((EntityReference)oldContact[appointment_birthday_attribute_name]).Id);
                                    entity[appointment_birthday_attribute_name] = null;
                                }
                            }
                        }
                    }
                }
            }
        }

        // Creates an recurring appointment
        public Entity createRecurringAppointment(Entity entity, Guid? OwnerID)
        {
            // if the contact has an owner use his id to create a required attendee
            Guid? ownerid = null;
            if (entity.Attributes.Contains("ownerid"))
            {
                ownerid = ((EntityReference)entity["ownerid"]).Id;
            }
            else
            {
                if (OwnerID != null)
                {
                    ownerid = OwnerID.Value; ;
                }
            }

            // Create a recurring appointment
            Entity newRecurringAppointment = new Entity("recurringappointmentmaster");
            DateTime birthday = (DateTime)entity["birthdate"];
            // its a allday event
            newRecurringAppointment.Attributes.Add("isalldayevent", true);
            if (entity.Attributes.Contains("fullname"))
            {
                newRecurringAppointment.Attributes.Add("subject", "Geburtstag von " + entity["fullname"].ToString());
            }
            // set the reagrding filed to the contact
            newRecurringAppointment.Attributes.Add("regardingobjectid", new EntityReference(entity.LogicalName, entity.Id));
            // Birthday is recurring once a year
            newRecurringAppointment.Attributes.Add("recurrencepatterntype", new OptionSetValue((int)RecurrencePatternTypes.Yearly));
            // start the pattern today
            newRecurringAppointment.Attributes.Add("patternstartdate", DateTime.Today);
            // Dont stop the recurring appointment at a certain date
            newRecurringAppointment.Attributes.Add("patternendtype", new OptionSetValue((int)RecurrenceRulePatternEndType.NoEndDate));
            // every year => interval = 1
            newRecurringAppointment.Attributes.Add("interval", 1);
            // Set the day (zero-based!)
            newRecurringAppointment.Attributes.Add("dayofmonth", birthday.Day + 1);
            // set the month
            newRecurringAppointment.Attributes.Add("monthofyear", new OptionSetValue(birthday.Month));
            // pretty useless because its a all day event, but if this is not set, the start and end-date were set incorrectly
            newRecurringAppointment.Attributes.Add("starttime", DateTime.Now);
            newRecurringAppointment.Attributes.Add("endtime", DateTime.Now.AddHours(1));

            // set the required attendee
            if (ownerid != null)
            {
                Entity activityparty = new Entity("activityparty");
                activityparty.Attributes.Add("partyid", new EntityReference("systemuser", ownerid.Value));
                newRecurringAppointment.Attributes.Add("requiredattendees", new Entity[] { activityparty });
            }
            return newRecurringAppointment;
        }
    }
}

Thats it. Compile, Register, Enjoy…

 

PS: WHat I forgot to mention.

Register the Create / Contact Step als Postcreate and the Update / Contact Step as PreUpdate :-)

Leave a Reply

Your email address will not be published. Required fields are marked *


*

You may use these HTML tags and attributes: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>

Microsoft Dynamics CRM 2013 – Plugin Messages – RetrieveMultiple– deep dive

Message Name Ownership Type Message Availability Entity Supported Deployment
RetrieveMultiple User-owned and organization-owned entities Both Server

The RetrieveMultiple Message is called when you open a view, and query entities with fetchxml or QueryExpression.

What happens when you retrieve a list of entities (in this case it is a list of open opportunities)  …

12.10.2013 19:53:42.004 :: INFO :: Entity: opportunity => Message: RetrieveMultiple => Stage: 10
12.10.2013 19:53:42.009 :: INFO :: Entity: opportunity => Message: RetrieveMultiple => Stage: 20
12.10.2013 19:53:42.014 :: INFO :: Entity: opportunity => Message: RetrieveMultiple => Stage: 40

The message has an input Parameters called “Query”. This is a fetchXml expression. In the example below I pressed on the button reload in the view “open opportunities”. As you an see the PrimaryEntityName is set to opportunity. On Stage 10 and 20 you still have the chance to change the fetchxml, but on stage 40 (post stage) you will find an outputparameter of tye BusinessEntityCollection which is a list of the returned entities (in our case the opportunities).

If you take a closer look at the inputParameter “Query” you will perhaps recognize that the fetchxml represents the FetchXml of the SavedQueryBase table in the sql database. There are only a few differences.

These three attributes are added for the view. The first parameter (page)  is the paging cookie, the count parameter is the number of records the view wants to retrieve and the returntotalrecordcount tells the query to return the total number of  records. If you go to the next page in the view the page variable is increased and so on …

page=”1″
count=”250″
returntotalrecordcount=”true”

Here is the corresponding context-log.

12.10.2013 19:53:41.931 :: INFO :: Start XrmLoggerPlugin ======================================================================== >>>
12.10.2013 19:53:41.931 :: INFO :: MessageName: RetrieveMultiple
12.10.2013 19:53:41.931 :: INFO :: PrimaryEntityName: opportunity
12.10.2013 19:53:41.931 :: INFO :: PrimaryEntityId: 00000000-0000-0000-0000-000000000000
12.10.2013 19:53:41.931 :: INFO :: Stage: 10
12.10.2013 19:53:41.931 :: INFO :: BusinessUnitId: 7647d68b-471d-e311-b013-000c2926dde4
12.10.2013 19:53:41.931 :: INFO :: CorrelationId: 55308b0f-0896-45a8-b653-605bb499f6eb
12.10.2013 19:53:41.931 :: INFO :: Depth: 1
12.10.2013 19:53:41.931 :: INFO :: InitiatingUserId: 0cd683a0-541d-e311-b013-000c2926dde4
12.10.2013 19:53:41.931 :: INFO :: InputParameters:
12.10.2013 19:53:42.004 :: INFO ::  >  Key: Query / Value: Microsoft.Xrm.Sdk.Query.QueryExpression  >> Queryexpression:
<fetch distinct="false" no-lock="true" mapping="logical" page="1" count="250" returntotalrecordcount="true"><entity name="opportunity"><attribute name="name" /><attribute name="estimatedvalue" /><attribute name="estimatedclosedate" /><attribute name="customerid" /><attribute name="opportunityratingcode" /><attribute name="closeprobability" /><attribute name="opportunityid" /><attribute name="name" /><attribute name="customerid" /><attribute name="estimatedclosedate" /><attribute name="estimatedvalue" /><attribute name="closeprobability" /><attribute name="opportunityratingcode" /><filter type="and"><condition attribute="statecode" operator="eq" value="0" /></filter><order attribute="estimatedclosedate" descending="false" /><link-entity name="contact" to="customerid" from="contactid" link-type="outer" alias="opportunitycustomeridcontactcontactid"><attribute name="emailaddress1" /></link-entity></entity></fetch>
12.10.2013 19:53:42.004 :: INFO :: IsExecutingOffline: False
12.10.2013 19:53:42.004 :: INFO :: IsInTransaction: False
12.10.2013 19:53:42.004 :: INFO :: IsOfflinePlayback: False
12.10.2013 19:53:42.004 :: INFO :: IsolationMode: 1
12.10.2013 19:53:42.004 :: INFO :: Mode: 0
12.10.2013 19:53:42.004 :: INFO :: OperationCreatedOn: 10/12/2013 5:53:41 PM
12.10.2013 19:53:42.004 :: INFO :: OperationId: 00000000-0000-0000-0000-000000000000
12.10.2013 19:53:42.004 :: INFO :: OrganizationId: eba3ca00-3338-49dd-b206-8ed1aacb1c3e
12.10.2013 19:53:42.004 :: INFO :: OrganizationName: crmdemo
12.10.2013 19:53:42.004 :: INFO :: OutputParameters:
12.10.2013 19:53:42.004 :: INFO :: 
12.10.2013 19:53:42.004 :: INFO :: OwningExtension: Microsoft.Xrm.Sdk.EntityReference
12.10.2013 19:53:42.004 :: INFO :: ParentContext: 
12.10.2013 19:53:42.004 :: INFO :: PostEntityImages: Microsoft.Xrm.Sdk.EntityImageCollection
12.10.2013 19:53:42.004 :: INFO :: PreEntityImages: Microsoft.Xrm.Sdk.EntityImageCollection
12.10.2013 19:53:42.004 :: INFO :: RequestId: 
12.10.2013 19:53:42.004 :: INFO :: SecondaryEntityName: none
12.10.2013 19:53:42.004 :: INFO :: SharedVariables: Microsoft.Xrm.Sdk.ParameterCollection
12.10.2013 19:53:42.004 :: INFO :: UserId: 0cd683a0-541d-e311-b013-000c2926dde4
12.10.2013 19:53:42.004 :: INFO :: Ende XrmLoggerPlugin ======================================================================== >>>

12.10.2013 19:53:42.006 :: INFO :: Start XrmLoggerPlugin ======================================================================== >>>
12.10.2013 19:53:42.006 :: INFO :: MessageName: RetrieveMultiple
12.10.2013 19:53:42.006 :: INFO :: PrimaryEntityName: opportunity
12.10.2013 19:53:42.006 :: INFO :: PrimaryEntityId: 00000000-0000-0000-0000-000000000000
12.10.2013 19:53:42.006 :: INFO :: Stage: 20
12.10.2013 19:53:42.006 :: INFO :: BusinessUnitId: 7647d68b-471d-e311-b013-000c2926dde4
12.10.2013 19:53:42.006 :: INFO :: CorrelationId: 55308b0f-0896-45a8-b653-605bb499f6eb
12.10.2013 19:53:42.006 :: INFO :: Depth: 1
12.10.2013 19:53:42.006 :: INFO :: InitiatingUserId: 0cd683a0-541d-e311-b013-000c2926dde4
12.10.2013 19:53:42.006 :: INFO :: InputParameters:
12.10.2013 19:53:42.006 :: INFO ::  >  Key: Query / Value: Microsoft.Xrm.Sdk.Query.QueryExpression  >> Queryexpression:
<fetch distinct="false" no-lock="true" mapping="logical" page="1" count="250" returntotalrecordcount="true"><entity name="opportunity"><attribute name="name" /><attribute name="estimatedvalue" /><attribute name="estimatedclosedate" /><attribute name="customerid" /><attribute name="opportunityratingcode" /><attribute name="closeprobability" /><attribute name="opportunityid" /><attribute name="name" /><attribute name="customerid" /><attribute name="estimatedclosedate" /><attribute name="estimatedvalue" /><attribute name="closeprobability" /><attribute name="opportunityratingcode" /><filter type="and"><condition attribute="statecode" operator="eq" value="0" /></filter><order attribute="estimatedclosedate" descending="false" /><link-entity name="contact" to="customerid" from="contactid" link-type="outer" alias="opportunitycustomeridcontactcontactid"><attribute name="emailaddress1" /></link-entity></entity></fetch>
12.10.2013 19:53:42.006 :: INFO :: IsExecutingOffline: False
12.10.2013 19:53:42.006 :: INFO :: IsInTransaction: False
12.10.2013 19:53:42.006 :: INFO :: IsOfflinePlayback: False
12.10.2013 19:53:42.006 :: INFO :: IsolationMode: 1
12.10.2013 19:53:42.006 :: INFO :: Mode: 0
12.10.2013 19:53:42.006 :: INFO :: OperationCreatedOn: 10/12/2013 5:53:42 PM
12.10.2013 19:53:42.006 :: INFO :: OperationId: 00000000-0000-0000-0000-000000000000
12.10.2013 19:53:42.006 :: INFO :: OrganizationId: eba3ca00-3338-49dd-b206-8ed1aacb1c3e
12.10.2013 19:53:42.006 :: INFO :: OrganizationName: crmdemo
12.10.2013 19:53:42.006 :: INFO :: OutputParameters:
12.10.2013 19:53:42.006 :: INFO :: 
12.10.2013 19:53:42.006 :: INFO :: OwningExtension: Microsoft.Xrm.Sdk.EntityReference
12.10.2013 19:53:42.006 :: INFO :: ParentContext: 
12.10.2013 19:53:42.006 :: INFO :: PostEntityImages: Microsoft.Xrm.Sdk.EntityImageCollection
12.10.2013 19:53:42.006 :: INFO :: PreEntityImages: Microsoft.Xrm.Sdk.EntityImageCollection
12.10.2013 19:53:42.006 :: INFO :: RequestId: 
12.10.2013 19:53:42.006 :: INFO :: SecondaryEntityName: none
12.10.2013 19:53:42.006 :: INFO :: SharedVariables: Microsoft.Xrm.Sdk.ParameterCollection
12.10.2013 19:53:42.006 :: INFO :: UserId: 0cd683a0-541d-e311-b013-000c2926dde4
12.10.2013 19:53:42.006 :: INFO :: Ende XrmLoggerPlugin ======================================================================== >>>

12.10.2013 19:53:42.011 :: INFO :: Start XrmLoggerPlugin ======================================================================== >>>
12.10.2013 19:53:42.011 :: INFO :: MessageName: RetrieveMultiple
12.10.2013 19:53:42.011 :: INFO :: PrimaryEntityName: opportunity
12.10.2013 19:53:42.011 :: INFO :: PrimaryEntityId: 00000000-0000-0000-0000-000000000000
12.10.2013 19:53:42.011 :: INFO :: Stage: 40
12.10.2013 19:53:42.011 :: INFO :: BusinessUnitId: 7647d68b-471d-e311-b013-000c2926dde4
12.10.2013 19:53:42.011 :: INFO :: CorrelationId: 55308b0f-0896-45a8-b653-605bb499f6eb
12.10.2013 19:53:42.011 :: INFO :: Depth: 1
12.10.2013 19:53:42.011 :: INFO :: InitiatingUserId: 0cd683a0-541d-e311-b013-000c2926dde4
12.10.2013 19:53:42.011 :: INFO :: InputParameters:
12.10.2013 19:53:42.014 :: INFO ::  >  Key: Query / Value: Microsoft.Xrm.Sdk.Query.QueryExpression  >> Queryexpression:
<fetch distinct="false" no-lock="true" mapping="logical" page="1" count="250" returntotalrecordcount="true"><entity name="opportunity"><attribute name="name" /><attribute name="estimatedvalue" /><attribute name="estimatedclosedate" /><attribute name="customerid" /><attribute name="opportunityratingcode" /><attribute name="closeprobability" /><attribute name="opportunityid" /><attribute name="name" /><attribute name="customerid" /><attribute name="estimatedclosedate" /><attribute name="estimatedvalue" /><attribute name="closeprobability" /><attribute name="opportunityratingcode" /><filter type="and"><condition attribute="statecode" operator="eq" value="0" /></filter><order attribute="estimatedclosedate" descending="false" /><link-entity name="contact" to="customerid" from="contactid" link-type="outer" alias="opportunitycustomeridcontactcontactid"><attribute name="emailaddress1" /></link-entity></entity></fetch>
12.10.2013 19:53:42.014 :: INFO :: IsExecutingOffline: False
12.10.2013 19:53:42.014 :: INFO :: IsInTransaction: False
12.10.2013 19:53:42.014 :: INFO :: IsOfflinePlayback: False
12.10.2013 19:53:42.014 :: INFO :: IsolationMode: 1
12.10.2013 19:53:42.014 :: INFO :: Mode: 0
12.10.2013 19:53:42.014 :: INFO :: OperationCreatedOn: 10/12/2013 5:53:42 PM
12.10.2013 19:53:42.014 :: INFO :: OperationId: 00000000-0000-0000-0000-000000000000
12.10.2013 19:53:42.014 :: INFO :: OrganizationId: eba3ca00-3338-49dd-b206-8ed1aacb1c3e
12.10.2013 19:53:42.014 :: INFO :: OrganizationName: crmdemo
12.10.2013 19:53:42.014 :: INFO :: OutputParameters:
12.10.2013 19:53:42.014 :: INFO ::  >  Key: BusinessEntityCollection / Value: Microsoft.Xrm.Sdk.EntityCollection  >> EntityCollection:
opportunity/ (8/8)
12.10.2013 19:53:42.014 :: INFO :: OwningExtension: Microsoft.Xrm.Sdk.EntityReference
12.10.2013 19:53:42.014 :: INFO :: ParentContext: 
12.10.2013 19:53:42.014 :: INFO :: PostEntityImages: Microsoft.Xrm.Sdk.EntityImageCollection
12.10.2013 19:53:42.014 :: INFO :: PreEntityImages: Microsoft.Xrm.Sdk.EntityImageCollection
12.10.2013 19:53:42.014 :: INFO :: RequestId: 
12.10.2013 19:53:42.014 :: INFO :: SecondaryEntityName: none
12.10.2013 19:53:42.014 :: INFO :: SharedVariables: Microsoft.Xrm.Sdk.ParameterCollection
12.10.2013 19:53:42.014 :: INFO :: UserId: 0cd683a0-541d-e311-b013-000c2926dde4
12.10.2013 19:53:42.014 :: INFO :: Ende XrmLoggerPlugin ======================================================================== >>>

 

 

 

 

 

Leave a Reply

Your email address will not be published. Required fields are marked *


*

You may use these HTML tags and attributes: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>

Microsoft Dynamics CRM: Simple File Logging for Plugins (and Workflows)

Well, there are many logging tools out there, but most of them depend on external assemblies. It is (not impossible but) more or less difficult to implement them in plugins.

So I decided to write a very simple file logger for myself. And this is also the tool I use to demonstrate what happens within the execution of plugins in my “Plugin messages – deep dive”. I tested it with Dynamics CRM 2013, but it should work the same way with every other version!

The Logger

The XrmLogger class is very easy to understand. There is a XrmLoggerConfig class where you specify where to write the files and the name you like your logs to have. The filename is a combination of a string and the date. In our Example the name would be “2013-10-09-XrmLoggerDemo.log”. Every day a new file is created. But you can change this very easily.

XrmLoggerConfig config = new XrmLoggerConfig()
            {
                LogDirectoryName = @"C:\LOG",
                LogFileGroup = "XrmLoggerDemo",
                logLevel = LogLevel.VERBOSE
            };

Next step is to get the execution context and do whatever your plugin is supposed to do. In my example I write the entity name, the message and the stage the plugin is running at.

xLog.ialert(String.Format("Entity: {0} => Message: {1} => Stage: {2}", context.PrimaryEntityName, context.MessageName, context.Stage));

So thats it more or less. Compile the code and register the assembly. Next choose the entity and the messages you want to listen at. And here is the result:

09.10.2013 14:27:28.400 :: INFO :: Entity: account => Message: RetrieveMultiple => Stage: 10
09.10.2013 14:27:28.405 :: INFO :: Entity: account => Message: RetrieveMultiple => Stage: 20
09.10.2013 14:27:28.458 :: INFO :: Entity: account => Message: RetrieveMultiple => Stage: 40

Below you find a listing of the complete code and the files do download.

xrlmLogger.cs

using System;
using System.Text;
using System.IO;

namespace xrmLog
{
    public enum LogLevel
    {
        VERBOSE = 0,
        INFO = 10,
        WARNING = 20,
        ERROR = 30,
    }

    public class XrmLoggerConfig
    {
        private string _LogDirectoryName = @"C:\LOG\";
        private string _LogFileName = "{0}-{1}.log";
        private string _LogFileGroup = "XrmAppLog";
        private LogLevel _logLevel = LogLevel.VERBOSE;

        public string LogDirectoryName
        {
            set
            {
                this._LogDirectoryName = value;
            }
            get
            {
                return this._LogDirectoryName;
            }
        }

        public string LogFileName
        {
            set
            {
                this._LogFileName = value;
            }
            get
            {
                return this._LogFileName;
            }
        }

        public string LogFileGroup
        {
            set
            {
                this._LogFileGroup = value;
            }
            get
            {
                return this._LogFileGroup;
            }
        }

        public LogLevel logLevel
        {
            set
            {
                this._logLevel = value;
            }
            get
            {
                return this._logLevel;
            }
        }

    }

    public class XrmLogger
    {

        private Boolean _dologging = true;
        private XrmLoggerConfig _config = new XrmLoggerConfig();

        private FileStream _fileStream;

        public XrmLogger()
        {
            this.init(_config);
        }

        public XrmLogger(string logFileName)
        {
            _config.LogFileName = logFileName;
            init(_config);
        }

        public XrmLogger(XrmLoggerConfig config)
        {
            this._config = config;
            init(config);
        }

        public Boolean doLogging
        {
            set
            {
                this._dologging = value;
            }
            get
            {
                return this._dologging;
            }
        }

        private string getDateTimeString()
        {
            return DateTime.Now.ToString("dd.MM.yyyy HH:mm:ss.fff");
        }

        private string getDateString()
        {
            return DateTime.Now.ToString("yyyy-MM-dd");
        }

        private string getFileName()
        {
            return String.Format(_config.LogFileName, this.getDateString(), _config.LogFileGroup);

        }

        private void init(XrmLoggerConfig config)
        {
            string _LogDirectoryName = config.LogDirectoryName.EndsWith(@"\") ? config.LogDirectoryName : config.LogDirectoryName + @"\";

            string path = String.Format("{0}{1}", _LogDirectoryName, this.getFileName());
            _fileStream = new FileStream(path, FileMode.Append, FileAccess.Write, FileShare.ReadWrite);
        }

        public void close()
        {
            _fileStream.Close();
        }

        private void writeFile(string message)
        {
            string line = this.getDateTimeString() + message + Environment.NewLine;
            byte[] info = new UTF8Encoding(true).GetBytes(line);
            this._fileStream.Write(info, 0, info.Length);
            this._fileStream.Flush();
        }

        public void valert(string msg)
        {
            if (this._dologging && _config.logLevel <= LogLevel.VERBOSE)
            {
                writeFile(" :: VERBOSE :: " + msg);
            }
        }

        public void ialert(string msg)
        {
            if (this._dologging && _config.logLevel <= LogLevel.INFO)
            {
                writeFile(" :: INFO :: " + msg);
            }
        }

        public void walert(string msg)
        {
            if (this._dologging && _config.logLevel <= LogLevel.WARNING)
            {
                writeFile(" :: WARNINIG :: " + msg);
            }
        }

        public void ealert(string msg)
        {
            if (this._dologging && _config.logLevel <= LogLevel.ERROR)
            {
                writeFile(" :: ERROR :: " + msg);
            }
        }
    }

}

The Sample Plugin

If you want to write a informational message use ialert, if you want to indicate a warning walert and on errors use the ealert function.

XrmLoggerDemo.cs

using System;
using Microsoft.Xrm.Sdk;

// 
// This is  demo of the XrmLogger class.
// How to use it:
// Create a C:\LOG on the Server and give your CRM Users RW rights
// Compile this class (don't forget to sign it with a strong key!)
// Use the pluginregistrationtool from the CRM.SDK to register the assembly (don't specify sandbox isolation mode! 
// => this plugin works only in onpremise or hosting environments
// deploy it for any message and entity you like
// 

namespace xrmLog
{
    public class XrmLoggerDemo : IPlugin
    {

        public void Execute(System.IServiceProvider serviceProvider)
        {
            IPluginExecutionContext context = (Microsoft.Xrm.Sdk.IPluginExecutionContext)serviceProvider.GetService(typeof(Microsoft.Xrm.Sdk.IPluginExecutionContext));

            XrmLoggerConfig config = new XrmLoggerConfig()
            {
                LogDirectoryName = @"C:\LOG",
                LogFileGroup = "XrmLoggerDemo",
                logLevel = LogLevel.VERBOSE
            };

            XrmLogger xLog = new XrmLogger(config);

            xLog.ialert(String.Format("Entity: {0} => Message: {1} => Stage: {2}", context.PrimaryEntityName, context.MessageName, context.Stage));
        }
    }
}

Download Visual Studio 2012 Project:
XrmLoggerDemo

Leave a Reply

Your email address will not be published. Required fields are marked *


*

You may use these HTML tags and attributes: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>

Microsoft Dynamics CRM 2011 – Problem with setFocus and Lookup Fields

I tried to set the focus on the “from”-Field in the onload event of a phone call activity. Unfortunatley the focus jumps alway to “subject”. This might be a bug, and the only wokaround I found so far is to set a little delay. This is no a very nice solution but better than nothing.

btw: this bug is gone in crm 2013!

 

function onLoadForm() {
   window.setTimeout(focusFrom,100)
}

function focusFrom() {
  var _Control = Xrm.Page.ui.controls.get("from");
  _Control.setFocus();
}

Leave a Reply

Your email address will not be published. Required fields are marked *


*

You may use these HTML tags and attributes: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>

Performance is slow if the AsyncOperationBase table becomes too large in Microsoft Dynamics CRM

When you run Microsoft Dynamics CRM 4.0 or Microsoft Dynamics CRM 2011, the AsyncOperationBase table grows to be very large. When the table contains millions of records, performance is slow.

To resolve this problem, perform a cleanup of the AsyncOperationBase table by running the following script against the<OrgName>_MSCRM database, where the placeholder<OrgName> represents the actual name of your organization.

Warning Before you clean up the data, be aware that completed system jobs have business value in some cases and have to be stored for a long period. Therefore, you should discuss this with your organization’s administration staff first.

System jobs that are affected:

  • SQM data collection. Software Quality Metrics collects data for the customer experience program.
  • Update Contract States SQL job. This job runs one time per day at midnight. This job sets the expired contracts to a state of Expired.
  • Organization Full Text Catalog Index. Populates full text index in db for searching Microsoft Knowledge Base articles in CRM.

If recurring jobs were canceled, they will be removed.

Notes

  • For Microsoft Dynamics CRM 4.0 The SQL script in this Knowledge Base article is a one-time effort only. You can add this as a SQL job to run on a recurring nightly, weekly, or monthly basis. As your CRM runs, you have to either apply this article weekly, depending on your business needs, or apply the solution by writing custom BULK DELETE jobs. (Refer to our CRM SDK documentation on the BulkDeleteRequest.QuerySet property, on the BulkDeleteRequest class, and on the order of deletion).
  • For Microsoft Dynamics CRM 2011 The SQL script in this Knowledge Base article is a one-time effort only. You can add this as a SQL job to run on a recurring nightly, weekly, or monthly basis. As your CRM runs, you have to either apply this article weekly, depending on your business needs, or apply the solution by using BULK DELETE jobs by defining a job by using the BULK DELETE wizard.
  • Make sure that the AsyncOperation records for workflows and the corresponding records are deleted from theWorkflowLogBase object.
  • Make sure that all the corresponding bulkdeletefailure records are deleted.
  • Make sure that only the following Async operation types are deleted if the state code of the types is 3 and the status code of the types is 30 or 32:
    • Workflow Expansion Task (1)
    • Collect SQM data (9)
    • PersistMatchCode (12)
    • FullTextCatalogIndex (25)
    • UpdateContractStates (27)
    • Workflow (10)
IF EXISTS (SELECT name from sys.indexes
                  WHERE name = N'CRM_AsyncOperation_CleanupCompleted')
      DROP Index AsyncOperationBase.CRM_AsyncOperation_CleanupCompleted
GO
CREATE NONCLUSTERED INDEX CRM_AsyncOperation_CleanupCompleted
ON [dbo].[AsyncOperationBase] ([StatusCode],[StateCode],[OperationType])
GO

declare @DeleteRowCount int
Select @DeleteRowCount = 2000
declare @DeletedAsyncRowsTable table (AsyncOperationId uniqueidentifier not null primary key)
declare @continue int, @rowCount int
select @continue = 1
while (@continue = 1)
begin      
begin tran      
insert into @DeletedAsyncRowsTable(AsyncOperationId)
      Select top (@DeleteRowCount) AsyncOperationId from AsyncOperationBase
      where OperationType in (1, 9, 12, 25, 27, 10) AND StateCode = 3 AND StatusCode in (30, 32)     
       Select @rowCount = 0
      Select @rowCount = count(*) from @DeletedAsyncRowsTable
      select @continue = case when @rowCount <= 0 then 0 else 1 end      
        if (@continue = 1)        begin
            delete WorkflowLogBase from WorkflowLogBase W, @DeletedAsyncRowsTable d
            where W.AsyncOperationId = d.AsyncOperationId             
 delete BulkDeleteFailureBase From BulkDeleteFailureBase B, @DeletedAsyncRowsTable d
            where B.AsyncOperationId = d.AsyncOperationId
 delete WorkflowWaitSubscriptionBase from WorkflowWaitSubscriptionBase WS, @DeletedAsyncRowsTable d
 where WS.AsyncOperationId = d.AsyncOperationID 
            delete AsyncOperationBase From AsyncOperationBase A, @DeletedAsyncRowsTable d
            where A.AsyncOperationId = d.AsyncOperationId             
            delete @DeletedAsyncRowsTable      
end       
commit
end
--Drop the Index on AsyncOperationBase
DROP INDEX AsyncOperationBase.CRM_AsyncOperation_CleanupCompleted

 

Original Url

Leave a Reply

Your email address will not be published. Required fields are marked *


*

You may use these HTML tags and attributes: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>

Cleaning up CRM Sync Entry tables

If you’re CRM system has been up and running for a long period of time and your users have the CRM Outlook client installed (or have had it installed in the past) you’ll find many tables starting in SyncEntry_ as well as SubscriptionStatistics_ in your database. These tables are used to track items that are synchronized down to users client machines, however, when users get new client machines or leave the company the tables are left behind indefinitely.  This script has become a cleanup step we run with our customers prior to upgrading or periodically to prune out old sync data.

Important NOTES about this script:

  • Always make a backup before running this script against your DB.
  • Running scripts that modify your database are not supported, thus we’ve gone to great lengths to test this and make sure that all entries are cleaned out.  As of today this script can successfully run on:
    • CRM 4.0 UR7 and greater
    • CRM 2011 RTM through CRM 2011 Update Rollup 8
  • Do not modify any parameters or joins in this script or you could risk data damage
  • The script currently deletes all sync data older than 90 days
  • If a users sync entry data were to be deleted, the next time the user attempts to sync their client, the data would be rebuilt automatically
  • To test the script you can edit the “SET @execute = 1” and set it to a “0”, this will allow the script to run in a read-only mode and print out the SQL statements it would run to delete the tables.

 

Declare 
      @SyncEnt    varchar(60),
      @syncId     uniqueidentifier,
      @SQL        nvarchar(MAX), 
      @execute bit

--To run the deletions set this to 1, if it is 0 it will only print the statements
SET @execute = 1 

DECLARE CRMSync_cursor CURSOR FOR

SELECT Replace(SyncEntryTableName,'SyncEntry_','') as SyncEntryGUID, SubscriptionId from subscription 
where LastSyncStartedOn < GetDate()-90 or LastSyncStartedOn is NULL

OPEN CRMSync_cursor
FETCH NEXT FROM CRMSync_cursor INTO @SyncEnt, @syncId
WHILE @@Fetch_Status = 0
BEGIN
      IF  EXISTS (SELECT * FROM sys.objects WHERE object_id = OBJECT_ID('subscriptionStatistics_'+@SyncEnt) AND type in (N'U'))
      BEGIN
            SET @SQL = 'DROP TABLE SubscriptionStatistics_' +(@SyncEnt)
            IF @execute=1 EXEC sp_executesql @SQL
            IF @execute=0 PRINT @SQL
            IF @execute=1 PRINT 'Dropped table: SubscriptionStatistics_'+(@SyncEnt)+ ' with error: ' + CAST(@@ERROR as varchar(255))
      END
      ELSE
            IF @execute=1 PRINT 'SubscriptionStatistics table does not exist for subscriptionID: ' + CAST(@syncId as varchar(50))

      IF  EXISTS (SELECT * FROM sys.objects WHERE object_id = OBJECT_ID('SyncEntry_'+@SyncEnt) AND type in (N'U'))
      BEGIN
            SET @SQL = 'DROP TABLE SyncEntry_' +(@SyncEnt)
            IF @execute=1 EXEC sp_executesql @SQL
            IF @execute=0 PRINT @SQL
            IF @execute=1 PRINT 'Dropped table: SyncEntry_'+(@SyncEnt) + ' with error: ' + CAST(@@ERROR as varchar(255))
      END
      ELSE
            IF @execute=1 PRINT 'SyncEntry table does not exist for subscriptionID: ' + CAST(@syncId as varchar(50))

      SET @SQL = 
            'delete from SubscriptionManuallyTrackedObject where subscriptionId = ''' + CAST(@syncId as varchar(50)) + ''';' +
            'delete from subscriptionclients where subscriptionId = ''' + CAST(@syncId as varchar(50)) + ''';' +
            'delete from Subscriptionsyncinfo where subscriptionId = ''' + CAST(@syncId as varchar(50)) + ''';' +
            'delete from subscription where subscriptionId = ''' + CAST(@syncId as varchar(50)) + ''''
      IF @execute=1 EXEC sp_executesql @SQL
      IF @execute=0 PRINT @SQL
      IF @execute=1 PRINT 'Dropped subscription table data for subscriptionId: ' + CAST(@syncId as varchar(50)) + ' with error data: ' + CAST(@@ERROR as varchar(255))
      FETCH NEXT FROM CRMSync_cursor INTO @SyncEnt, @syncId
END
CLOSE CRMSync_cursor
DEALLOCATE CRMSync_cursor
Original url

Leave a Reply

Your email address will not be published. Required fields are marked *


*

You may use these HTML tags and attributes: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>

Configuring CRM 2011 IFD

A really, really good article on how to configure CRM 2011 IFD:

http://dynamics.co.il/configuring-crm-2011-ifd/

 

Leave a Reply

Your email address will not be published. Required fields are marked *


*

You may use these HTML tags and attributes: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>