Wednesday, June 20, 2012

Check Out the New SSIS Integration Toolkit for Microsoft Dynamics CRM 2011 from KingswaySoft!

I had the privilege over the last month or two to test out KingswaySoft's new SSIS Integration Toolkit for Microsoft Dynamics CRM.  It works for both CRM 4.0 and CRM 2011 and does a great job.

It gives you an easy to use and hook up CRM destination and CRM source that can be used to easily unleash the full force of SSIS combined with CRM without having to use script tasks or web service tasks.

It's free to try and is currently $895 to buy for a perpetual license or $495 for a one year subscription license.  The trial is fully functional.

I have put together a small sample SSIS package that inserts into CRM accounts and related contacts into CRM from flat files.  This sample is available below.

Keep in mind that I am definitely not an SSIS guru but I did manage to get this sample to work.  Please do not ask me questions related to SSIS as it makes my head hurt.  Please deflect all such questions to the maker of this program Daniel Cai (https://plus.google.com/115440641969940382242/posts).



Get My Sample Package Here:


Get The Tool Here!: http://www.kingswaysoft.com/products/ssis-integration-toolkit-for-microsoft-dynamics-crm

-

Thursday, June 14, 2012

Export a Data Import Map in Microsoft Dynamics CRM 2011 Using VB.NET

This illustration shows how to export a data import map in Microsoft Dynamics CRM 2011 in code using VB.NET.   This example will be given in SOAP (JScript) and in C# (.NET).

Ok, here is what the code look like!
In VB.NET:

Dim req As New ExportMappingsImportMapRequest()
req.ExportIds = False
req.ImportMapId = New Guid("455B72E2-9863-4984-AEF2-A26C16AFBAD6")
Dim resp As ExportMappingsImportMapResponse = DirectCast(service.Execute(req), ExportMappingsImportMapResponse)


Thats all there is to it!

I hope this helps!

Tuesday, June 12, 2012

Export a Saved Import Map in Microsoft Dynamics CRM 2011 Using C# or JScript

This illustration shows how to export a saved import map in Microsoft Dynamics CRM 2011 with ExportMappingsImportMapRequest.  This example will be given in Jscript (SOAP) and in C# (.NET).
    Ok, here is what the code looks like!
    First in C#:

    ExportMappingsImportMapRequest req = new ExportMappingsImportMapRequest();
    req.ExportIds = false;
    req.ImportMapId = new Guid("455B72E2-9863-4984-AEF2-A26C16AFBAD6");
    ExportMappingsImportMapResponse resp = (ExportMappingsImportMapResponse)service.Execute(req);
    

    If you need help instantiating a service object in .NET within a plugin check out this post:
    http://mileyja.blogspot.com/2011/04/instantiating-service-object-within.html

    Now here is the Jscript nicely formatted by the CRM 2011 SOAP formatter. Available at: http://crm2011soap.codeplex.com/

    Now in Jscript


    This example is asynchronous, if you want to learn how to make JScript SOAP calls synchronously please visit this posthttp://mileyja.blogspot.com/2011/07/using-jscript-to-access-soap-web.html

    
    if (typeof (SDK) == "undefined")
       { SDK = { __namespace: true }; }
           //This will establish a more unique namespace for functions in this library. This will reduce the 
           // potential for functions to be overwritten due to a duplicate name when the library is loaded.
           SDK.SAMPLES = {
               _getServerUrl: function () {
                   ///<summary>
                   /// Returns the URL for the SOAP endpoint using the context information available in the form
                   /// or HTML Web resource.
                   ///</summary>
                   var ServicePath = "/XRMServices/2011/Organization.svc/web";
                   var serverUrl = "";
                   if (typeof GetGlobalContext == "function") {
                       var context = GetGlobalContext();
                       serverUrl = context.getServerUrl();
                   }
                   else {
                       if (typeof Xrm.Page.context == "object") {
                             serverUrl = Xrm.Page.context.getServerUrl();
                       }
                       else
                       { throw new Error("Unable to access the server URL"); }
                       }
                      if (serverUrl.match(/\/$/)) {
                           serverUrl = serverUrl.substring(0, serverUrl.length - 1);
                       } 
                       return serverUrl + ServicePath;
                   }, 
               ExportMappingsImportMapRequest: function () {
                   var requestMain = ""
                   requestMain += "<s:Envelope xmlns:s=\"http://schemas.xmlsoap.org/soap/envelope/\">";
                   requestMain += "  <s:Body>";
                   requestMain += "    <Execute xmlns=\"http://schemas.microsoft.com/xrm/2011/Contracts/Services\" xmlns:i=\"http://www.w3.org/2001/XMLSchema-instance\">";
                   requestMain += "      <request i:type=\"b:ExportMappingsImportMapRequest\" xmlns:a=\"http://schemas.microsoft.com/xrm/2011/Contracts\" xmlns:b=\"http://schemas.microsoft.com/crm/2011/Contracts\">";
                   requestMain += "        <a:Parameters xmlns:c=\"http://schemas.datacontract.org/2004/07/System.Collections.Generic\">";
                   requestMain += "          <a:KeyValuePairOfstringanyType>";
                   requestMain += "            <c:key>ImportMapId</c:key>";
                   requestMain += "            <c:value i:type=\"d:guid\" xmlns:d=\"http://schemas.microsoft.com/2003/10/Serialization/\">455b72e2-9863-4984-aef2-a26c16afbad6</c:value>";
                   requestMain += "          </a:KeyValuePairOfstringanyType>";
                   requestMain += "          <a:KeyValuePairOfstringanyType>";
                   requestMain += "            <c:key>ExportIds</c:key>";
                   requestMain += "            <c:value i:type=\"d:boolean\" xmlns:d=\"http://www.w3.org/2001/XMLSchema\">false</c:value>";
                   requestMain += "          </a:KeyValuePairOfstringanyType>";
                   requestMain += "        </a:Parameters>";
                   requestMain += "        <a:RequestId i:nil=\"true\" />";
                   requestMain += "        <a:RequestName>ExportMappingsImportMap</a:RequestName>";
                   requestMain += "      </request>";
                   requestMain += "    </Execute>";
                   requestMain += "  </s:Body>";
                   requestMain += "</s:Envelope>";
                   var req = new XMLHttpRequest();
                   req.open("POST", SDK.SAMPLES._getServerUrl(), true)
                   req.setRequestHeader("Accept", "application/xml, text/xml, */*");
                   req.setRequestHeader("Content-Type", "text/xml; charset=utf-8");
                   req.setRequestHeader("SOAPAction", "http://schemas.microsoft.com/xrm/2011/Contracts/Services/IOrganizationService/Execute");
                   var successCallback = null;
                   var errorCallback = null;
                   req.onreadystatechange = function () { SDK.SAMPLES.ExportMappingsImportMapResponse(req, successCallback, errorCallback); };
                   req.send(requestMain);
               },
           ExportMappingsImportMapResponse: function (req, successCallback, errorCallback) {
                   ///<summary>
                   /// Recieves the assign response
                   ///</summary>
                   ///<param name="req" Type="XMLHttpRequest">
                   /// The XMLHttpRequest response
                   ///</param>
                   ///<param name="successCallback" Type="Function">
                   /// The function to perform when an successfult response is returned.
                   /// For this message no data is returned so a success callback is not really necessary.
                   ///</param>
                   ///<param name="errorCallback" Type="Function">
                   /// The function to perform when an error is returned.
                   /// This function accepts a JScript error returned by the _getError function
                   ///</param>
                   if (req.readyState == 4) {
                   if (req.status == 200) {
                   if (successCallback != null)
                   { successCallback(); }
                   }
                   else {
                       errorCallback(SDK.SAMPLES._getError(req.responseXML));
                   }
               }
           },
           _getError: function (faultXml) {
               ///<summary>
               /// Parses the WCF fault returned in the event of an error.
               ///</summary>
               ///<param name="faultXml" Type="XML">
               /// The responseXML property of the XMLHttpRequest response.
               ///</param>
               var errorMessage = "Unknown Error (Unable to parse the fault)";
               if (typeof faultXml == "object") {
                   try {
                       var bodyNode = faultXml.firstChild.firstChild;
                       //Retrieve the fault node
                       for (var i = 0; i < bodyNode.childNodes.length; i++) {
                           var node = bodyNode.childNodes[i];
                           //NOTE: This comparison does not handle the case where the XML namespace changes
                           if ("s:Fault" == node.nodeName) {
                           for (var j = 0; j < node.childNodes.length; j++) {
                               var faultStringNode = node.childNodes[j];
                               if ("faultstring" == faultStringNode.nodeName) {
                                   errorMessage = faultStringNode.text;
                                   break;
                               }
                           }
                           break;
                       }
                   }
               }
               catch (e) { };
            }
            return new Error(errorMessage);
         },
     __namespace: true
    };
    


    To understand how to parse the response please review my post on using the DOM parser.
    Now you can call the SDK.SAMPLES.ExportMappingsImportMapRequest function from your form jscript handler.


    Thats all there is to it!
    -

    Friday, June 8, 2012

    Book an Appointment in Microsoft Dynamics CRM 2011 Using C# or Jscript

    This illustration shows how to book an appointment in Microsoft Dynamics CRM 2011 with BookRequest.  This example will be given in Jscript (SOAP) and in C# (.NET).
      Ok, here is what the code looks like!
      First in C#:

      WhoAmIRequest userRequest = new WhoAmIRequest();
      WhoAmIResponse userResponse = (WhoAmIResponse)_serviceProxy.Execute(userRequest);
      
      // Create the ActivityParty instance.
      ActivityParty party = new ActivityParty
      {
          PartyId = new EntityReference(SystemUser.EntityLogicalName, userResponse.UserId)
      };
      
      // Create the appointment instance.
      Appointment appointment = new Appointment
      {
          Subject = "Test Appointment",
          Description = "Test Appointment created using the BookRequest Message.",
          ScheduledStart = DateTime.Now.AddHours(1),
          ScheduledEnd = DateTime.Now.AddHours(2),
          Location = "Office",
          RequiredAttendees = new ActivityParty[] { party },
          Organizer = new ActivityParty[] { party }
      };
      
      // Use the Book request message.
      BookRequest req = new BookRequest
      {
          Target = appointment
      };
      BookResponse resp = (BookResponse)service.Execute(req);
      Guid _appointmentId = resp.ValidationResult.ActivityId;
      
      
      This C# code was adapted from MSDN examples located at http://msdn.microsoft.com/en-us/library/microsoft.crm.sdk.messages.bookrequest.aspx

      If you need help instantiating a service object in .NET within a plugin check out this post:
      http://mileyja.blogspot.com/2011/04/instantiating-service-object-within.html

      Now here is the Jscript nicely formatted by the CRM 2011 SOAP formatter. Available at: http://crm2011soap.codeplex.com/

      Now in Jscript


      This example is asynchronous, if you want to learn how to make JScript SOAP calls synchronously please visit this posthttp://mileyja.blogspot.com/2011/07/using-jscript-to-access-soap-web.html

      if (typeof (SDK) == "undefined")
         { SDK = { __namespace: true }; }
             //This will establish a more unique namespace for functions in this library. This will reduce the 
             // potential for functions to be overwritten due to a duplicate name when the library is loaded.
             SDK.SAMPLES = {
                 _getServerUrl: function () {
                     ///<summary>
                     /// Returns the URL for the SOAP endpoint using the context information available in the form
                     /// or HTML Web resource.
                     ///</summary>
                     var ServicePath = "/XRMServices/2011/Organization.svc/web";
                     var serverUrl = "";
                     if (typeof GetGlobalContext == "function") {
                         var context = GetGlobalContext();
                         serverUrl = context.getServerUrl();
                     }
                     else {
                         if (typeof Xrm.Page.context == "object") {
                               serverUrl = Xrm.Page.context.getServerUrl();
                         }
                         else
                         { throw new Error("Unable to access the server URL"); }
                         }
                        if (serverUrl.match(/\/$/)) {
                             serverUrl = serverUrl.substring(0, serverUrl.length - 1);
                         } 
                         return serverUrl + ServicePath;
                     }, 
                 BookRequest: function () {
                     var requestMain = ""
                     requestMain += "<s:Envelope xmlns:s=\"http://schemas.xmlsoap.org/soap/envelope/\">";
                     requestMain += "  <s:Body>";
                     requestMain += "    <Execute xmlns=\"http://schemas.microsoft.com/xrm/2011/Contracts/Services\" xmlns:i=\"http://www.w3.org/2001/XMLSchema-instance\">";
                     requestMain += "      <request i:type=\"b:BookRequest\" xmlns:a=\"http://schemas.microsoft.com/xrm/2011/Contracts\" xmlns:b=\"http://schemas.microsoft.com/crm/2011/Contracts\">";
                     requestMain += "        <a:Parameters xmlns:c=\"http://schemas.datacontract.org/2004/07/System.Collections.Generic\">";
                     requestMain += "          <a:KeyValuePairOfstringanyType>";
                     requestMain += "            <c:key>Target</c:key>";
                     requestMain += "            <c:value i:type=\"a:Entity\">";
                     requestMain += "              <a:Attributes>";
                     requestMain += "                <a:KeyValuePairOfstringanyType>";
                     requestMain += "                  <c:key>subject</c:key>";
                     requestMain += "                  <c:value i:type=\"d:string\" xmlns:d=\"http://www.w3.org/2001/XMLSchema\">Test Appointment</c:value>";
                     requestMain += "                </a:KeyValuePairOfstringanyType>";
                     requestMain += "                <a:KeyValuePairOfstringanyType>";
                     requestMain += "                  <c:key>description</c:key>";
                     requestMain += "                  <c:value i:type=\"d:string\" xmlns:d=\"http://www.w3.org/2001/XMLSchema\">Test Appointment created using the BookRequest Message.</c:value>";
                     requestMain += "                </a:KeyValuePairOfstringanyType>";
                     requestMain += "                <a:KeyValuePairOfstringanyType>";
                     requestMain += "                  <c:key>scheduledstart</c:key>";
                     requestMain += "                  <c:value i:type=\"d:dateTime\" xmlns:d=\"http://www.w3.org/2001/XMLSchema\">2012-06-08T09:42:45.8655676-05:00</c:value>";
                     requestMain += "                </a:KeyValuePairOfstringanyType>";
                     requestMain += "                <a:KeyValuePairOfstringanyType>";
                     requestMain += "                  <c:key>scheduledend</c:key>";
                     requestMain += "                  <c:value i:type=\"d:dateTime\" xmlns:d=\"http://www.w3.org/2001/XMLSchema\">2012-06-08T10:42:45.8655676-05:00</c:value>";
                     requestMain += "                </a:KeyValuePairOfstringanyType>";
                     requestMain += "                <a:KeyValuePairOfstringanyType>";
                     requestMain += "                  <c:key>location</c:key>";
                     requestMain += "                  <c:value i:type=\"d:string\" xmlns:d=\"http://www.w3.org/2001/XMLSchema\">Office</c:value>";
                     requestMain += "                </a:KeyValuePairOfstringanyType>";
                     requestMain += "                <a:KeyValuePairOfstringanyType>";
                     requestMain += "                  <c:key>requiredattendees</c:key>";
                     requestMain += "                  <c:value i:type=\"a:EntityCollection\">";
                     requestMain += "                    <a:Entities>";
                     requestMain += "                      <a:Entity>";
                     requestMain += "                        <a:Attributes>";
                     requestMain += "                          <a:KeyValuePairOfstringanyType>";
                     requestMain += "                            <c:key>partyid</c:key>";
                     requestMain += "                            <c:value i:type=\"a:EntityReference\">";
                     requestMain += "                              <a:Id>6e219f51-0310-4c4d-8c60-1c524e2ba7b3</a:Id>";
                     requestMain += "                              <a:LogicalName>systemuser</a:LogicalName>";
                     requestMain += "                              <a:Name i:nil=\"true\" />";
                     requestMain += "                            </c:value>";
                     requestMain += "                          </a:KeyValuePairOfstringanyType>";
                     requestMain += "                        </a:Attributes>";
                     requestMain += "                        <a:EntityState i:nil=\"true\" />";
                     requestMain += "                        <a:FormattedValues />";
                     requestMain += "                        <a:Id>00000000-0000-0000-0000-000000000000</a:Id>";
                     requestMain += "                        <a:LogicalName>activityparty</a:LogicalName>";
                     requestMain += "                        <a:RelatedEntities />";
                     requestMain += "                      </a:Entity>";
                     requestMain += "                    </a:Entities>";
                     requestMain += "                    <a:EntityName i:nil=\"true\" />";
                     requestMain += "                    <a:MinActiveRowVersion i:nil=\"true\" />";
                     requestMain += "                    <a:MoreRecords>false</a:MoreRecords>";
                     requestMain += "                    <a:PagingCookie i:nil=\"true\" />";
                     requestMain += "                    <a:TotalRecordCount>0</a:TotalRecordCount>";
                     requestMain += "                    <a:TotalRecordCountLimitExceeded>false</a:TotalRecordCountLimitExceeded>";
                     requestMain += "                  </c:value>";
                     requestMain += "                </a:KeyValuePairOfstringanyType>";
                     requestMain += "                <a:KeyValuePairOfstringanyType>";
                     requestMain += "                  <c:key>organizer</c:key>";
                     requestMain += "                  <c:value i:type=\"a:EntityCollection\">";
                     requestMain += "                    <a:Entities>";
                     requestMain += "                      <a:Entity>";
                     requestMain += "                        <a:Attributes>";
                     requestMain += "                          <a:KeyValuePairOfstringanyType>";
                     requestMain += "                            <c:key>partyid</c:key>";
                     requestMain += "                            <c:value i:type=\"a:EntityReference\">";
                     requestMain += "                              <a:Id>6e219f51-0310-4c4d-8c60-1c524e2ba7b3</a:Id>";
                     requestMain += "                              <a:LogicalName>systemuser</a:LogicalName>";
                     requestMain += "                              <a:Name i:nil=\"true\" />";
                     requestMain += "                            </c:value>";
                     requestMain += "                          </a:KeyValuePairOfstringanyType>";
                     requestMain += "                        </a:Attributes>";
                     requestMain += "                        <a:EntityState i:nil=\"true\" />";
                     requestMain += "                        <a:FormattedValues />";
                     requestMain += "                        <a:Id>00000000-0000-0000-0000-000000000000</a:Id>";
                     requestMain += "                        <a:LogicalName>activityparty</a:LogicalName>";
                     requestMain += "                        <a:RelatedEntities />";
                     requestMain += "                      </a:Entity>";
                     requestMain += "                    </a:Entities>";
                     requestMain += "                    <a:EntityName i:nil=\"true\" />";
                     requestMain += "                    <a:MinActiveRowVersion i:nil=\"true\" />";
                     requestMain += "                    <a:MoreRecords>false</a:MoreRecords>";
                     requestMain += "                    <a:PagingCookie i:nil=\"true\" />";
                     requestMain += "                    <a:TotalRecordCount>0</a:TotalRecordCount>";
                     requestMain += "                    <a:TotalRecordCountLimitExceeded>false</a:TotalRecordCountLimitExceeded>";
                     requestMain += "                  </c:value>";
                     requestMain += "                </a:KeyValuePairOfstringanyType>";
                     requestMain += "              </a:Attributes>";
                     requestMain += "              <a:EntityState i:nil=\"true\" />";
                     requestMain += "              <a:FormattedValues />";
                     requestMain += "              <a:Id>00000000-0000-0000-0000-000000000000</a:Id>";
                     requestMain += "              <a:LogicalName>appointment</a:LogicalName>";
                     requestMain += "              <a:RelatedEntities />";
                     requestMain += "            </c:value>";
                     requestMain += "          </a:KeyValuePairOfstringanyType>";
                     requestMain += "        </a:Parameters>";
                     requestMain += "        <a:RequestId i:nil=\"true\" />";
                     requestMain += "        <a:RequestName>Book</a:RequestName>";
                     requestMain += "      </request>";
                     requestMain += "    </Execute>";
                     requestMain += "  </s:Body>";
                     requestMain += "</s:Envelope>";
                     var req = new XMLHttpRequest();
                     req.open("POST", SDK.SAMPLES._getServerUrl(), true)
                     req.setRequestHeader("Accept", "application/xml, text/xml, */*");
                     req.setRequestHeader("Content-Type", "text/xml; charset=utf-8");
                     req.setRequestHeader("SOAPAction", "http://schemas.microsoft.com/xrm/2011/Contracts/Services/IOrganizationService/Execute");
                     var successCallback = null;
                     var errorCallback = null;
                     req.onreadystatechange = function () { SDK.SAMPLES.BookResponse(req, successCallback, errorCallback); };
                     req.send(requestMain);
                 },
             BookResponse: function (req, successCallback, errorCallback) {
                     ///<summary>
                     /// Recieves the assign response
                     ///</summary>
                     ///<param name="req" Type="XMLHttpRequest">
                     /// The XMLHttpRequest response
                     ///</param>
                     ///<param name="successCallback" Type="Function">
                     /// The function to perform when an successfult response is returned.
                     /// For this message no data is returned so a success callback is not really necessary.
                     ///</param>
                     ///<param name="errorCallback" Type="Function">
                     /// The function to perform when an error is returned.
                     /// This function accepts a JScript error returned by the _getError function
                     ///</param>
                     if (req.readyState == 4) {
                     if (req.status == 200) {
                     if (successCallback != null)
                     { successCallback(); }
                     }
                     else {
                         errorCallback(SDK.SAMPLES._getError(req.responseXML));
                     }
                 }
             },
             _getError: function (faultXml) {
                 ///<summary>
                 /// Parses the WCF fault returned in the event of an error.
                 ///</summary>
                 ///<param name="faultXml" Type="XML">
                 /// The responseXML property of the XMLHttpRequest response.
                 ///</param>
                 var errorMessage = "Unknown Error (Unable to parse the fault)";
                 if (typeof faultXml == "object") {
                     try {
                         var bodyNode = faultXml.firstChild.firstChild;
                         //Retrieve the fault node
                         for (var i = 0; i < bodyNode.childNodes.length; i++) {
                             var node = bodyNode.childNodes[i];
                             //NOTE: This comparison does not handle the case where the XML namespace changes
                             if ("s:Fault" == node.nodeName) {
                             for (var j = 0; j < node.childNodes.length; j++) {
                                 var faultStringNode = node.childNodes[j];
                                 if ("faultstring" == faultStringNode.nodeName) {
                                     errorMessage = faultStringNode.text;
                                     break;
                                 }
                             }
                             break;
                         }
                     }
                 }
                 catch (e) { };
              }
              return new Error(errorMessage);
           },
       __namespace: true
      };
      
      


      To understand how to parse the response please review my post on using the DOM parser.
      Now you can call the SDK.SAMPLES.BookRequest function from your form jscript handler.


      Thats all there is to it!
      -

      Thursday, June 7, 2012

      My Favorite Pasta Sauce Recipe

      Sorry, no CRM today,  only the best pasta sauce recipe EVER!!!   I used to use it to impress the ladies on dates.  Now that I am married I use it to impress our friends when I invite them over.   This recipe was given to me by a old friend from high school named Devon Gifford.

      Bowties w/ Sausage & Vodka Sauce

      Servings           4
      Prep Time        10 min
      Cook Time        40 min

      Ingredients

      6          ounces Bowties Pasta (can substitute with any spiral/tubed pasta)

      6          ounces uncooked, loose ground spicy Italian sausage (SuperTarget is the best – not in casing.  Be sure your guests can handle the heat, otherwise get mild sausage)

      2          Tablespoons fresh garlic, finely chopped

      ½         Cup onion, finely diced

      1          14 oz Can diced tomatoes (ensure can only contains tomatoes and no other herbs, vegetables or seasonings other than salt)

      3          Tablespoons vodka

      1          Cup whipping cream

      ¼         Cup peas (defrosted if frozen)

      6 – 8    Leaves of fresh basil, finely chopped (there is no substitute for this!  It is a majority of the flavor.)

      Grated hard Italian cheese (Parmesan, Romano or Asiago)

      Instructions

      In a medium pot, cook pasta, drain and set aside.

      Heat a large sauce pan over medium-high heat for several minutes.  Add Italian sausage and break apart into small pieces with wooden spoon while frying.  Cook for 10 minutes or until sausage is cooked through and fat has been rendered out of it.  Ideally, the sausage will be almost dry and very dark brown but not burnt.  Reduce heat to low. 

      With a slotted spoon, remove the cooked sausage while leaving the rendered fat in the pan.  Place the cooked sausage in a bowl and set aside.  In same sauce pan and using the rendered fat, sauté garlic and onion on low heat until onion is limp and translucent, about 5 minutes.  Be caution not to burn the onion and garlic.

      Add entire can of tomatoes and increase heat to medium-high.  Using wooden spoon, chop and stir tomatoes.  Cook on medium-high heat until virtually all liquid from the tomatoes has been evaporated, about 10 minutes.

      While pan is still hot, add the 3 Tablespoons of vodka.  Ignite the fumes and swirl mixture while burning off alcohol. (For best results – the pan should be hot enough that the vodka turns to steam once it hit the pan.  Be sure not to catch anything on fire.)

      Once fire burns out, add the cream, peas and chopped basil followed by the cooked sausage and pasta.  Reduce heat to medium and stir to coat pasta.  Cook until flavors have combined and cream is slightly reduced – about 5 to 10 minutes.

      Serve immediately with grated cheese on top.  Accompanied well with a good Italian bread and olive oil with balsamic vinegar and parmesan for dipping.


      Happy Thursday!

      Monday, June 4, 2012

      Richard Knudson's New Book is Out!

      Another CRM MVP, Richard Knudson, just released his new book "Building Business With CRM".  It's a great book because it mixes how to apply Microsoft Dynamics CRM to business while still incorporating a lot of the technical knowledge you would expect from a more technically focused book.

      I am not receiving any monetary proceeds from this book but my name is buried in it somewhere because I assisted in the editing process.

      Check it out at the link below:

      http://www.crmbizbook.com/