Warning: Creating default object from empty value in /nfs/c02/h05/mnt/29116/domains/dynamics4.com/html/wp-content/plugins/contact-form-7/includes/controller.php on line 131

Warning: Cannot modify header information - headers already sent by (output started at /nfs/c02/h05/mnt/29116/domains/dynamics4.com/html/wp-content/plugins/contact-form-7/includes/controller.php:131) in /nfs/c02/h05/mnt/29116/domains/dynamics4.com/html/wp-content/plugins/wordpress-seo/frontend/class-frontend.php on line 959

Warning: Cannot modify header information - headers already sent by (output started at /nfs/c02/h05/mnt/29116/domains/dynamics4.com/html/wp-content/plugins/contact-form-7/includes/controller.php:131) in /nfs/c02/h05/mnt/29116/domains/dynamics4.com/html/wp-includes/feed-rss2.php on line 8
dynamics four http://dynamics4.com CRM & ERP Solution Specialist located in Atlanta Georgia Tue, 06 Aug 2013 14:40:11 +0000 en-US hourly 1 http://wordpress.org/?v=3.6 Cloud Computing and Microsoft CRM http://dynamics4.com/cloud-computing-and-microsoft-crm/ http://dynamics4.com/cloud-computing-and-microsoft-crm/#comments Mon, 01 Jul 2013 17:28:25 +0000 jklaws http://dynamics4.com/?p=3450 Cloud Computing and Microsoft CRM Online   The corporate world has been transitioning to the cloud since 2009, which causes people to ask a pertinent question, “What exactly is cloud ...

The post Cloud Computing and Microsoft CRM appeared first on dynamics four.

]]>
Cloud Computing and Microsoft CRM Online

 

The corporate world has been transitioning to the cloud since 2009, which causes people to ask a pertinent question, “What exactly is cloud computing, and what does it mean for my business?”

Simply stated, the “cloud” is an internet based network.  Cloud computing enables consumers with different platforms (PaaS), software (Saas), data access (hosted on remote virtual servers), and storage services (online backup) via the Web instead of a traditional devoted operating system.

Cloud storage has been around for decades…  Every time you send an email or attachment, you are storing personal and business information in a remote network.  Accessing webmail, through hosted sites like Gmail, Yahoo!, and Hotmail, from multiple locations is considered a form of cloud computing. Uploading digital pictures and movies on sites such as Flickr and Photobucket means you’re hosting images in the cloud. Social networks such as Facebook and Twitter, are examples of high scaled cloud based applications users can personalize and constantly update. Common examples of major platforms-as-a-service are Amazon, Google, MS, and Apple.

The Cloud allows computing on a global scale. No longer is your music, media, data, and documents tied to a particular computer at a specific location. In the business world, computing in the cloud allows for incredible flexibility and bottom-line savings as it is economical in terms of both cost and customer hardware resources.

Your company has access to its data through web-native software applications, aka, Software-as-a-service (SaaS), which allows business applications to be centrally hosted by a third party. Your company no longer has  to build their own IT infrastructure in order to host databases or software and you can access your data from virtually any Web connection, though any computer or web-enabled device, just by logging into your account.

There are other added benefits to SaaS. Because cloud services are web-based, most work on multiple platforms, including Linux, Macintosh, and Windows computers.  The company no longer has the responsibility of running and maintaining demanding applications because the vendors manage scalability and consistently upgrade the software. On-demand software services provided by the vendors reduces the need of on-premise management, and allows for considerable cost savings due to reduced needs for specialized staffing, power consumption, and expensive hardware.

SaaS applications like Microsoft Dynamics CRM (Customer relationship management) also offer features that let its users collaborate, comment on tasks and plans, and share data & documents from any location. Microsoft has a key competitive advantage because almost everyone is familiar with MS program management tools.

 

The post Cloud Computing and Microsoft CRM appeared first on dynamics four.

]]>
http://dynamics4.com/cloud-computing-and-microsoft-crm/feed/ 0
Moving to the cloud – hype or reality? http://dynamics4.com/moving-to-the-cloud-hype-or-reality/ http://dynamics4.com/moving-to-the-cloud-hype-or-reality/#comments Fri, 28 Jun 2013 04:15:10 +0000 Bill Ryan http://dynamics4.com/?p=3445 Moving to the cloud – hype or reality Cloud is the new buzzword.  There’s a lot to it, including a lot of hype.  I can’t help of thinking back to ...

The post Moving to the cloud – hype or reality? appeared first on dynamics four.

]]>
Moving to the cloud – hype or reality

Cloud is the new buzzword.  There’s a lot to it, including a lot of hype.  I can’t help of thinking back to my early days of grad school when I had to read Fad Surfing in the Boardroom which describes essentially how grown men that run companies act just like teenagers running out to get the new Air Jordans or LeBron James Nikes.  There’s no shortage of people and companies running out trying to get in the cloud. Virtually every web company is now advertising some form of a Cloud based product.

Software as a service and cloud based services are nothing new.  The allure is that you don’t have to have an IT department, you don’t have to spend a fortune on hardware and you don’t have to spend a fortune maintaining your IT Assets.  Cloud advocates and sales people will tout the fact they have data centers that can survive a nuclear blast and everything but a zombie apocalypse. They tout underground data centers with armed guards, highly skilled staff, encrypted data and strict policies and procedures that keep employees from looking at your data and they tout redundant backbones.  And they typically promise it pretty cheap.  Well, if all of that was completely true and there weren’t other factors, using the Cloud would seem like a no-brainer.

It’s true that most data centers have redundancy  but there’s this. Without rubbing anyone’s nose in it, more than a few people with unbreakable backbones found out their backbones were made of balsa wood the first time they got challenged.  If you have  a long enough of a memory, you’ll recall 9/11 and how fragile much of this bullet proof infrastructure was (in fact, I believe Savvis was one of the only companies that was actually able to keep things running). Sure that was over 10 years ago and things have gotten better, but Squirrels remain one of the biggest problems for network and power outages.  Most data centers can manage a paramilitary attack, but not a family of squirrels.

But physical security isn’t what worries most people. I’ve been in countless presentations where people were pitching cloud based hosting. And in most cases, the sales people were rather condescending or at least ‘full of themselves’ when it came to how secure and redundant their data centers were.  One was arrogant enough to tell a CTO “I realize you’re one of those people that need the emotional comfort blanket of having your data hosted in-house, but there was  a time you were scared to leave the house without your blanket and pacifier – now you barely remember them, the same will happen here.” (Condescending to people may work for some sales pitches, it failed miserably this time). Most people aren’t worried about paramilitary militias or zombies, they’re more worried about leverage. They don’t like your service, have a billing dispute or some other problem and you have all their data.  That isn’t a negotiating position most people ever want to be in . And while most people won’t bring it up in front of sales reps, ask most CTO or CIO’s one on one and they’ll cite this as the biggest concern they have  (or at least one of them).

The other major concern is that all the Service Level agreements in the world don’t carry with them the same feeling of control that being able to call up a subordinate does.  Generally speaking, you promote people you trust b/c of competence and integrity.

Will you have that same level of trust with some guy on the other end of the phone you never met?   Maybe, maybe not. It really depends and depends on large part on past experience. Sadly, you only have to be wrong once for it to be a big problem. It’s very easy to find false comfort in the promises of service level agreements and things working well b/c they haven’t been battle tested.  The worst time to find out there are serious problems and vulnerabilities is when there’s a disaster and people need your product the most. And that’s been how people have found out the bad news in more than a few cases.  But in the case of your employee, his future depends on getting your system back up. His livelihood and next paycheck depend on it.  If you’re one of thousands of customers at a big cloud company, they may or may not care. Your employee is likely to take a more hands on approach, take a seeing is believing approach and practice disaster recovery (let’s hope she does anyway).

It may seem that this is an anti-cloud screed and that’s certainly not the case.  There’s a lot of benefits to moving to the cloud. Take CRM Online. In a few minutes you can be up and running, with Click Dimensions installed and users can be working in the system about 30 minutes after signing up. You can even try it up front for 30 days first.  The price is quite reasonable too and if you’re a doctor’s office or smaller company without a big IT staff, the CRM Online option is hard to write off.  We personally use CRM Online and have had a very good experience with it.  We’re using SharePoint online and it’s been seamless. We’re using Office 365 and I personally was very skeptical about it at first but have become it’s biggest advocate – to say I love it would be a complete understatement.  Take Salesforce.com as another example. Their growth has been explosive to say the least. They have quite a few happy customers and growth rates anyone would be jealous of. And a big part is their whole “No Software” mantra.  Just like Microsoft CRM Online, they handle all the setup and maintenance. They handle security. They handle upgrades. And issues like cross-browser compatibility that plague application developers are non-issues for uses of Microsoft CRM Online

The point however is that there’s a lot of hype and a lot of tangible benefit surrounding the Cloud. Choices made about this issue shouldn’t be made lightly and shouldn’t be made without a lot of weighing the pros and cons.  Whichever approach you favor, make sure you have someone aggressively play Devil’s advocate in your discussion and really think it through.

And when you’re done, make sure you read this.

Keywords: Dynamics4, Dynamics Four, Dynamicsfour, Cloud Hosting, Data Center, Data Center outages, Crm Online, Office 365, SharePoint Online, Atlanta, South East, Greenville, CRM Services South East

 

The post Moving to the cloud – hype or reality? appeared first on dynamics four.

]]>
http://dynamics4.com/moving-to-the-cloud-hype-or-reality/feed/ 0
FetchXml vs. QueryExpression http://dynamics4.com/fetchxml-vs-queryexpression/ http://dynamics4.com/fetchxml-vs-queryexpression/#comments Thu, 27 Jun 2013 05:16:22 +0000 Development Team http://www.williamgryan.mobi/?p=599 FetchXml vs. QueryExpression “FetchXml vs. QueryExpression, which should I use?” seems to be a question every new Microsoft Crm 2011 developer asks. Depending on who you’ll ask, you’ll get wildly disparate ...

The post FetchXml vs. QueryExpression appeared first on dynamics four.

]]>
FetchXml vs. QueryExpression

FetchXml vs. QueryExpression, which should I use?” seems to be a question every new Microsoft Crm 2011 developer asks. Depending on who you’ll ask, you’ll get wildly disparate responses and ones that seem pretty definitive.  Developers who have been doing MSCRM development for a while tend to advocate using FetchXml from what I can see. I surmise this has something to do with the fact FetchXml has been around the longest and now. Furthermore in MSCRM 2011, you can actually build your query visually and have the FetchXml output generated for you (see below):
FetchXml Button

Additionally, back when MSCRM 4.0 was out and this feature was not available, Stunnware had a really handy FetchXml generator which let you visually construct queries.  I’ve heard all sorts of other bits of conventional wisdom but the more I dug into the veracity of these claims (like “It’s much faster”) the less I was able to verify them (the MSCRM 4.0 documentation indicates that QueryExpression is faster b/c it doesn’t need to be parsed, but I’ve heard a few vets swear the opposite is true). I’m not saying they’re not true, just saying that particularly when using MSCRM Online, I haven’t been able to see much of a difference one way or the other in terms of performance [Actually, it's probably more precise to say that too many other factors come into play that make it really hard to isolate things down to this level).

Using the QueryExpression class seems a little more modern, a little more Object Oriented if you will and b/c you get intellisense support, seems to be a little more precise but each of these reasons is subjective (other than the Intellisense part). As I just said though, 'precise' is in the eye of the beholder (if you're getting a FetchXml query back from MSCRM, there's no real issue with precision other than perhaps if you made a copy/paste error) and you're more like to make a mistake using a hand-generate QueryExpression instance.

The one area I"ve been able to detect that FetchXml has an advantage over QueryExperssion is in terms of Aggregates. At least as far as I know, if you need to use an aggregate expression, such as Sum, Count or Average, there is no direct implementation in the QueryExpression class. If you use the OrganizationServiceContext and go with the Entity Approach, you can certainly use LINQ semantics to generate aggregates in your code.  Somewhere along the lines the aggregates are getting translated to SQL and my point here was trying to get to the bottom of how things worked.

There is one message and one method that can translate FetchXml to a QueryExpression and a QueryExpression to FetchXml. Unsurprisingly, they're named the FetchXmlToQueryExpressionRequest / FetchXmlToQueryExpressionResponse and the QueryExpressionToFetchXmlRequest and QueryExpressionToFetchXmlResponse.  So I wanted to do a little digging. Here's what I did.  I started off with a basic Aggregate Expression in FetchXml (taking the code straight from the SDK on computing an Average for the EstimatedValue property of an Opportunity).  When I passed this in to the RetrieveMultiple method of the OrganizationService, I noticed that I always got back exactly on Entity.  Using the AliasedValue class (Microsoft.Xrm.Sdk.AliasedValue) I noticed that the aliased field contained the computed aggregate. Nothing earth shattering there, and exactly what I expected.

CrmConnection Connection = new CrmConnection("CrmMain");
IOrganizationService Service = new OrganizationService(Connection);
String Estimatedvalue = @"<fetch distinct='false' mapping='logical' aggregate='true'><entity name='opportunity'><attribute name='estimatedvalue' alias='estimatedvalue_avg' aggregate='avg' /> </entity></fetch>";
FetchExpression FetchQuery = new FetchExpression(Estimatedvalue);
EntityCollection Results = Service.RetrieveMultiple(FetchQuery);
if(null != Results && Results.Entities.Count > 0){
 Console.WriteLine("Average: {0}", ((Money)((AliasedValue)Results[0]["estimatedvalue_avg"]).Value).Value);   
}

Now, I tried using the same FetchXml expression in conjunction with the OrganizationService 's FetchXmlToQueryExpression extension method.

FetchXmlToQueryExpressionRequest ConversionRequest = new FetchXmlToQueryExpressionRequest();
QueryExpression ConversionResponse = Service.FetchXmlToQueryExpression(Estimatedvalue) as QueryExpression ;
EntityCollection QueryExpressionResults = Service.RetrieveMultiple(ConversionResponse);
foreach (Entity queryExpressionResult in QueryExpressionResults.Entities)
{
    Console.WriteLine("Opportunityid:{0}", queryExpressionResult["opportunityid"].ToString());
}
Console.WriteLine("Total Opportunities from Converted QueryExpression: {0}", QueryExpressionResults.Entities.Count.ToString());

What I got back was a little surprising:
FetchXml to QueryExpression FetchXmlToQueryExpressionRequest
Notice that now, I got back exactly 15 entities (this happens to be the exact # of Total Opportunities, both open and closed).  There was only one field and the QueryExpression didn't have much to it other than the EntityName.

So I decided to do the exact same thing, just in reverse (using teh QueryExpressionToFetchXmlRequest and QueryExpressionToFetchXmlResponse messages).  And what I got back makes sense, but I was a little surprised (see above). I got back the exact same items in the exact same order, same count same everything.  Why was I surprised and what did this say? Well, I was sort of expecting to see some magic computation going on, thinking that maybe something was being done in the FetchXml request that simply wasn't publicly exposed in the QueryExpression class.  But no, that didn't happen.  B/c I'm using CrmOnline, I wasn't able to run profiler and see exactly what Query is being generated, but I have a strong suspicion that when FetchXml is used directly, it does actually fire a SELECT AVERAGE(FIELD) FROM TABLE in Sql (the SDK code comments imply as much).  That's b/c we only get one value back from the original query. When we run it either of the other two ways , using QueryExpressionToFetchXmlRequest or FetchXmlToQueryExpression, b/c there's no direct aggregate operator in a QueryExpression, you get back all of the corresponding records. To simulate an aggregate, you'd need to iterate through the collection and run the aggregate on your own.  At this point that's my suspicion, but looks like some more investigation is in order when I can use a OnPrem instance of MSCRM and see exactly what's happening behind the scenes.

For now, I'll have to say I'm taking a middle of the road approach. I prefer using the QueryExpression class for *Most* things b/c personally I know it better and think it's a little cleaner- but let me emphasize as strongly as possible, that's just *my* opinion.  I say 'Most' b/c if there's an aggregate, it seems more straightforward to just build a FetchXml aggregate expression and use it.

If you're interested, here are a few discussions lingering around the web on the issue:

 

If you have any questions or comments, leave them in the post or contact our team at devteam@dynamics4.com

————————————————————–

KeyWords: Dynamics4.com,  Microsoft.Xrm.Sdk.Entity,  Dynamics CRM 2011,    RelatedEntitiesLinkEntityLinkEntities, CrmConnection, OrganizationService, FilterExpression,  QueryExpressionCriteria, ,ColumnSetFetchXmlToQueryExpressionResponse , FetchXmlToQueryExpressionRequest , QueryExpressionToFetchXmlRequest , QueryExpressionToFetchXmlResponse,FetchXml, Dynamics4, CRM South East, Atlanta, Greenville, Miami

The post FetchXml vs. QueryExpression appeared first on dynamics four.

]]>
http://dynamics4.com/fetchxml-vs-queryexpression/feed/ 0
Changing Search Behavior in Microsoft CRM 2011 – Part 1 http://dynamics4.com/changing-search-behavior-in-microsoft-crm-2011-part-1/ http://dynamics4.com/changing-search-behavior-in-microsoft-crm-2011-part-1/#comments Thu, 20 Jun 2013 08:52:01 +0000 Bill Ryan http://www.williamgryan.mobi/?p=596 Changing Search Behavior in Microsoft CRM 2011 In this post, we’re going to show you how you can completely take over Search Functionality in Microsoft CRM 2011 in a fully ...

The post Changing Search Behavior in Microsoft CRM 2011 – Part 1 appeared first on dynamics four.

]]>
Changing Search Behavior in Microsoft CRM 2011

In this post, we’re going to show you how you can completely take over Search Functionality in Microsoft CRM 2011 in a fully supported manner.  The default behavior of the Search Box is to Search for the term you enter against the Entities on the grid below it. It uses a BeginsWith predicate so you have to know the first few letters of the Entity you’re looking for.  While this is sufficient most of the time, there are plenty of cases where you might only know another piece of the name (Think of a Law Partnership that has several names, and you can only remember the name of your attorney whose name appears at the end of the list.  Having a true LIKE based search vs a BeginsWith search would be quite handy.  For now I’ll show you how easy that is to do.  However you can do so much more. You can easily modify this to include a LIKE Based Search along with finding matches in all related entities including all relationship types.  So yes, you could search for “Dev” and it would find any say, Account with “Dev” anywhere in the accountname, but it would also find any contact with Bill in specified fields, and opportunity, and Article and any other entity you wanted to include. I’ll show you the easy way first and cover the more complex scenarios in later articles

There’s a common belief/perception that within MSCRM, you can’t change any of the Out of the Box behavior in a supported manner. You can modify all sorts of things and you can build custom solutions to meet your needs, but when it comes to changing the default behaviors, not much can be done. That’s the belief anyway.  We’ve had many clients request customized searches and each request is different, but they all stem from the nature of the way MSCM’s search works and needing a more expansive mechanism…. Most involved using SQL Server Full-Text Search , building a Full-Text index on the Entity’s underlying attributes fields that need to be search and then displaying the results in an ASP.NET Grid, a JavaScript grid or Silverlight Control.

Another common technique is to build custom entities that contain pieces of meta-data about your target and allow those to be searched (they’d usually contain a hyperlink or similar hook to the content that’s desired).

One reason people think OOB Search can’t be changed is the fact you aren’t allowed to modify the underlying ASP.NET pages or much of the out of the box content in any supported manner. And while Unsupported to those new to MSCRM, sounds like it’s optional, it’s always usually a very big no-no and means you’re likely going to cause yourself a bunch of headaches down the road.  For client engagements, unless the clients specifically tell you to do something unsupported after you’ve explained all the downsides and warned them against it, you may be asked to do it anyway.  Most people aren’t willing to take the risks once they understand them, but there are always exceptions.

Just to make sure everyone understands what we’re talking about, below is a picture of a typical Account screen with the Search box in the Upper Right Hand corner:
Custom Search MSCRM 2011

In this case, if you searched for “Bes” you’d get one hit returned, Best o’ Things. But if you searched for “Goods” you’d get nothing returned. That’s what we’ll address here.

The feature that enables you to pull all of this off is the message support that you can register Plugins to. I personally have been doing MSCRM development for quite a while, written more Plugins than I can count, and it never really dawned on me that this was possible.  In this post, I changed the Plugin Registration tool to display the available messages a little differently. My friend and former co-worker Nirnay Patel had a post which goes through all the messages.  If you’ve registered a plugin, you’ve seen all of those, but in almost every case you only think of using a few of them, namely Create, Update & Delete.  But there are so many more and well, it only makes sense that they’re there for a reason.  We also know that everything that’s done in the CRM Application does its thing by calling the OrganizationService or DiscoveryService.  That’s where the light went off. What method must be fired when you execute a Search?  RetrieveMultiple comes to mind. So I checked the message list and low and behold, RetrieveMultiple is in fact a supported message. Not only is it supported for a very large # of Entities, one of them includes UserQuery.

So, the key to everything here is to build a plugin that fires on the RetrieveMultiple event.  Here’s the first snippet that gets us there:

public class SearchPlugin : IPlugin
{
  /// <summary>
  /// String literal containing the value "Query"
  /// </summary>
  public const String QueryLiteral = "Query";
  public const String LIKE = "%";
  public void Execute(IServiceProvider serviceProvider)
  {
    #region
    String ParentEntity = String.Empty;
    String OriginalSearch = String.Empty;
    #endregion
    // Obtain the execution context from the service provider.    
    var ContextInstance = (IPluginExecutionContext)serviceProvider.GetService(typeof(IPluginExecutionContext));
    // Get a reference to the Organization service.    
    IOrganizationService ServiceInstance =((IOrganizationServiceFactory)serviceProvider.GetService(typeof(IOrganizationServiceFactory))).CreateOrganizationService(ContextInstance.InitiatingUserId);
    // NOTICE that the InputParameters Contains the word Query
    if (ContextInstance.Depth < 2 && ContextInstance.InputParameters.Contains(QueryLiteral) &&
   ContextInstance.InputParameters[QueryLiteral] is QueryExpression)    
 {        QueryExpression QueryPointer = (ContextInstance.InputParameters[QueryLiteral] as QueryExpression);

The first part of this should be nothing new, but the portions of code in Bold will probably be new.  As you can see, we’re getting a reference to an InputParameter, but instead of TARGET, or Entity or any of the usual items, it’s a QueryExpression. So the first thing we’re doing is grabbing a reference to the QueryExpression.

Now is where things get interesting. What we’re going to do is actually Modify the QueryExpression from the InputParameter. So when the Plugin Finishes firing, a new QueryExpression will be returned to CRM and the modified QueryExpression is the one that will be executed.  Now, keep in mind we’re only making one small modification to our QueryExpression but we could do all sorts of things. You can use a configuration entity like I discussed in yesterday’s post to provide the names of other entities you wanted to search, you can give it the names of the attributes you wanted to search and specify the relationships between them all.  B/c you have access to the QueryExpression before it executes, you can add LinkEntity items, FilterExpression, ConditionExpression and just about anything else.  You can even perform intermediate queries against other entities to get the IDs (Guids) of them to set as part of values, effectively hard coding in references to other entities. With that much power, there’s pretty much nothing that’s off the table. You can include related entities, you can include entities that are grandchildren, greatgrandchildren etc. You can even get non-related entities. And you can configure this so that the behavior can be modified by End users.  This in essence would be providing the end users with a fully configurable way to manage search. Everything else is configurable, Search is very important, so why shouldn’t it be something people can change right?  Anyway, here’s the remaining part of the code:

if (null != QueryPointer)
{
 // Check if the request is coming from any Search View 
 // We know this b/c Criteria isn't null and the Filters Count > 1
 if (null != QueryPointer.Criteria && QueryPointer.Criteria.Filters.Count > 1)
 {
    ParentEntity = ContextInstance.PrimaryEntityName;
    OriginalSearch = QueryPointer.Criteria.Filters[1].Conditions[0].Values[0].ToString();
    OriginalSearch = String.Format(CultureInfo.CurrentCulture,
                     "{0}{1}", LIKE,OriginalSearch);
 }
   ConditionExpression NewCondition = null;
   FilterExpression NewFilter = null;
   if (null != QueryPointer.Criteria)
   {
    //Change the default BeginsWith to Contains/Like in the basic search query
   foreach (FilterExpression FilterSet in QueryPointer.Criteria.Filters)
   {
        foreach (ConditionExpression ConditionSet in FilterSet.Conditions)
        {
           if (ConditionSet.Operator == ConditionOperator.Like)
           {
              ConditionSet.Values[0] = OriginalSearch;
           }
        }
    }
}}
ContextInstance.InputParameters[QueryLiteral] = QueryPointer;

It looks like a lot is going on here, but keep this in mind. In the default QueryExpression, we basically have a predicate that says “Where AccountName LIKE SearchValue%”. The wildcard is included in the value that’s being set on the ConditionSet.  So all we’re doing is changing that value to include a Wildcard at the beginning as well – so it now looks like “WHERE AccountName LIKE %SearchValue%” (And honestly, if you step through the code as it executes, you’ll see that this is literally what’s being done.  So we look for the LIKE Condition and change the value.  That’s it.  Then, we simply return this updated QueryExpression to the CRM Instance.

As I said, we could modify just about anything. We could conceivably change the search term altogether if we wanted (although not sure why you’d want to).  More common scenarios might be to add a filter condition to put an AND AdministrativeViewingOnly = False if the UserId isn’t an administrator (there are other ways , and probably better ones to accomplish restricting access, but I”m just trying to drive home what all can be done>).

The most common ask is to have related entities searched and have them searched in a specific way.  Accounts + Contacts for instance. Opportunity + OpportunityProducts. You get the idea.  In addition to these, you may have a few other Child Entities that you want to include, and you can easily include the entities and define what fields are searched in configuration and then have the QueryExpression dynamically updated to include all of these.  It’s very powerful and with the proper tooling you could give the clients an amazingly powerful search screen that they can configure as they please.

Provided I have time, I’ll show you how this is done by creating a configuration Entity, a User Friendly Form, and then some decision logic inside the Plugin that completely modifies the QueryExpression in ways a little more substantive than just changing the Search Value.

The full code is provided below for your reference. It is also available for download here:

using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Xml;
using Microsoft.Xrm.Sdk;
using Microsoft.Xrm.Sdk.Messages;
using Microsoft.Xrm.Sdk.Query;
using System.Globalization;

namespace Xrm.Plugins
{
    public class SearchPlugin : IPlugin
    {
        /// <summary>
        /// String literal containing the value "Query"
        /// </summary>
        public const String QueryLiteral = "Query";
        public const String LIKE = "%";
        public void Execute(IServiceProvider serviceProvider)
        {
            #region
            String ParentEntity = String.Empty;
            String OriginalSearch = String.Empty;

            #endregion
            // Obtain the execution context from the service provider. 
            var ContextInstance = (IPluginExecutionContext)serviceProvider.GetService(typeof(IPluginExecutionContext));
            // Get a reference to the Organization service. 
            IOrganizationService ServiceInstance =
                ((IOrganizationServiceFactory)serviceProvider.GetService(typeof(IOrganizationServiceFactory))).
                    CreateOrganizationService(ContextInstance.InitiatingUserId);
            // Critical Point here - NOTICE that the InputParameters Contains the word Query
            if (ContextInstance.Depth < 2 && ContextInstance.InputParameters.Contains(QueryLiteral) &&
                   ContextInstance.InputParameters[QueryLiteral] is QueryExpression)
            {
                QueryExpression QueryPointer = (ContextInstance.InputParameters[QueryLiteral] as QueryExpression);
                //Verify the conversion worked as expected - if not, everything else is useless
                if (null != QueryPointer)
                {
                     // Check if the request is coming from any Search View 
                    // We know this b/c Criteria isn't null and the Filters Count > 1
                    if (null != QueryPointer.Criteria && QueryPointer.Criteria.Filters.Count > 1)
                    {
                        ParentEntity = ContextInstance.PrimaryEntityName;
                        OriginalSearch = QueryPointer.Criteria.Filters[1].Conditions[0].Values[0].ToString();
                        OriginalSearch = String.Format(CultureInfo.CurrentCulture,
                                         "{0}{1}", LIKE,OriginalSearch);
                    }
                    ConditionExpression NewCondition = null;
                    FilterExpression NewFilter = null;
                    if (null != QueryPointer.Criteria)
                    {
                        //Change the default 'BeginsWith'Operator to 'Contains/Like' operator in the basic search query
                        foreach (FilterExpression FilterSet in QueryPointer.Criteria.Filters)
                        {
                            foreach (ConditionExpression ConditionSet in FilterSet.Conditions)
                            {
                                if (ConditionSet.Operator == ConditionOperator.Like)
                                {
                                    ConditionSet.Values[0] = OriginalSearch;
                                }
                            }
                        }
                    }
                }
                ContextInstance.InputParameters[QueryLiteral] = QueryPointer;
            }
        }
    }
}

 

————————————————————–

KeyWords: Dynamics4, Dynamics4.com, QueryByAttribute, QueryExpression, OrganizationContext, Plugin,IServiceProvider,  Plugin Assembly, Microsoft CRM Search, ConditionExpression, FilterExpression, IPluginExecutionContextIOrganizationServiceFactory, ConditionSet, FilterSet, CrmConnection, IOrganizationService  William Ryan, Bill Ryan, QueryByAttribute, Customize MSCRM Search, Customize Dynamics CRM Search, Customizing Microsoft Dynamics CRM 2011 Search,  Dynamics4, CRM South East, Atlanta, Greenville, Miami

The post Changing Search Behavior in Microsoft CRM 2011 – Part 1 appeared first on dynamics four.

]]>
http://dynamics4.com/changing-search-behavior-in-microsoft-crm-2011-part-1/feed/ 0
MSCRM 2011 – Using Custom Configuration Entities http://dynamics4.com/mscrm-2011-using-custom-configuration-entities/ http://dynamics4.com/mscrm-2011-using-custom-configuration-entities/#comments Sat, 15 Jun 2013 03:33:07 +0000 Bill Ryan http://www.williamgryan.mobi/?p=592 MSCRM 2011 – Using Custom Configuration Entities MSCRM 2011 is an extremely flexible application, so much so that it can easily be used as a development platform to build applications ...

The post MSCRM 2011 – Using Custom Configuration Entities appeared first on dynamics four.

]]>
MSCRM 2011 – Using Custom Configuration Entities

MSCRM 2011 is an extremely flexible application, so much so that it can easily be used as a development platform to build applications that have nothing to do with Customer Relationship Management.  The fact you can build custom entities that are automatically integrated with the security model, reporting framework or workflow runtime should come as a surprise to no one. Because of its flexibility though, I frequently find ways to do things I never would have thought of doing when looking at MSCRM 2011 from an application developer point of view.  I want to use this post to introduce a concept which will serve as the foundation for a few really cool tricks I’ve learned recently.

So we all know that you can use Custom Entities to model your business entities (and that includes creating them as Activities if need be).  However you can also use Custom Entities to store configuration information that’s consumed by other applications.  Why would you want to do this?

  • Since MSCRM 2011 is its own application, you have very limited access to what you can modify in the web.config file. For all intents and purposes, whether you’re writing a MSCRM Plugin, MSCRM Workflow, ASP.NET application or other, web.config is off limits.
  • Plugins have a mechanism to include configuration information, but this is limited to MSCRM 2011 Plugins and each section is confined to the specific MSCRM plugin or workflow its registered to.
  • By storing this information in a Custom Entity, you can use MSCRM’s built in Security Model to control access so only the intended parties can see or edit them.
  • By storing this information in a Custom Entity, you have immediate designer support and an editable data entry form
  • By storing this information in a Custom Entity, you can query it from Javascript, a MSCRM Plugin, MSCRM Workflow or pretty much another other application you choose. This means you can share the information across several domains and allows you to keep it all consolidated in one place. [Note that depending on your circumstances you may want to have just one Configuration Entity, or you may want to have several of them.  The data you're storing and the security needs of the application will ultimately dictate which path you take]

With this in mind, let’s build a foundation to working with this configuration information.  For this example, we’ll assume that we’re just using one custom Entity(named ryan_configurationset).  I’m including a primary method and an overloaded one. The primary one assumes the one Entity scenario, the overloaded one lets you specify the Entity name as well as the schema names for the Key field and Value field of the Custom Microsoft.Xrm.Sdk.Entity.  Because MSCRM Entities have their attributes implemented as a Key/Value pair I’ll follow that format here.  For each Configuration item we want to store, I’ll use a Key value implemented as a String, and a Value item implemented as a String as well. You could certainly augment this to be much more elaborate if you were so inclined so it supported several different types as the Value component, but storing configuration information as Strings makes things easy and will suffice for most applications you need. So without further ado, the first thing we’ll do is create a custom Entity in MSCRM named configurationset [since the publisher I'm using has a prefix of ryan_ my custom entity will be named ryan_configurationset  . The Key Field is named keyname [ryan_keyname with my publisher] and the Value field is named itemvalue [ryan_itemvalue, again with my publisher]:

Microsoft.Xrm.Sdk.Query.QueryByAttribute

The first step, just for the sake of simplicity is to create a class (I’ll name it XrmSdkHelpers) with two properties defined: one should be a Microsoft.Xrm.Client.CrmConnection , the other an Microsoft.Xrm.Sdk.IOrganizationService (this is just standard C# class construction here and I chose to implement the CrmConnection and OrganizationService as properties. You can just as easily pass them in to the method or via the constructor or one of several other methods. This implementation is just for illustration purposes):

private CrmConnection connectionInstance;
private IOrganizationService orgService;
public CrmConnection ConnectionInstance
{
   get
   {
      if (null == connectionInstance)
      {
         connectionInstance = new CrmConnection("CrmMain");
      }
      return connectionInstance;
   }
}
public IOrganizationService OrgService
{
   get
   {
   if (null == orgService)
      {
        // Put in check to make sure CrmConnection is valid
        orgService = new OrganizationService(ConnectionInstance);
      }
      return orgService;
   }
}

With those in place we’ll move on to the real functionality. I’m creating a method with a return type of String named GetConfigurationValue.  The idea is that there’s a base method that accepts a single parameter, the name of the Key Field who’s attribute you want returned.  This method just uses hard coded entity and attribute names [for ryan_configurationset, ryan_keyname and ryan_itemvalue). So as you can guess,  the other method accepts a key name, an entity name, key schema name and the item schema name.  The values you'll use for these should be self-evident. So if you opt for just one configuration entity, you'd use the first overload, if you want to use multiple ones, you'd use the second one.  The code to facilitate everything  is shown below. I'll post it first and walk through it and explain it afterward.

public String GetConfigurationValue(String keyName)
{
    return GetConfigurationValue(keyName, "ryan_configurationset", "ryan_keyname", "ryan_itemvalue");
}
public String GetConfigurationValue(String keyName, String entityName, String keySchemaName, String itemSchemaName)
{
 #region validation
 if (String.IsNullOrWhiteSpace(keyName))
 {
    throw new ArgumentNullException("keyName", "Value can not be null.");
 }
 #endregion
String KeyValue = null;
QueryByAttribute Query = new QueryByAttribute(entityName);
Query.AddAttributeValue(keySchemaName, keyName);
Query.ColumnSet = new ColumnSet(new String[] { itemSchemaName });
try
{
  EntityCollection Configs = 
        OrgService.RetrieveMultiple(Query) as EntityCollection;
     if (null != Configs && Configs.Entities.Count > 0)
     {
       KeyValue = Configs.Entities[0][itemSchemaName].ToString();
     }
   }
   catch (FaultException<Microsoft.Xrm.Sdk.OrganizationServiceFault> badAttributeRequest)
   {
   StringBuilder ErrorMsg = new StringBuilder();
   ErrorMsg.AppendFormat(CultureInfo.CurrentCulture,
      “GetConfigurationValue for {0} failed.rn”, keyName);
   ErrorMsg.AppendFormat(CultureInfo.CurrentCulture,
      “Details: {0} rn.”, badAttributeRequest.ToString());
   Logger.Write(ErrorMsg);
   }
   return KeyValue;
}

So what are we doing here? It's pretty simple. We need the name of the Custom Entity that contains the configuration information. We need the name of the KeyValue (the actual data that's stored in the Key field).  If you have both of these, then you do the following:

  • Instantiate a new QueryByAttribute passing in the Entity name to the constructor
  • Add the Attribute name and value to the QueryByAttribute by using the AddAttributeValue method
  • Define a new ColumnSet specifying the name of the Value field to be returned.  (You can optionally use the overloaded constructor for the ColumnSet passing true into the constructor. This will however return all fields [the equivalent of using SELECT * in SQL] from the Entity. Because MSCRM adds several fields to every custom entity for internal use, it's inefficient to use this approach. Since the Entity only contains two custom Attributes, it's easy to mistakenly assume that the overloaded ColumnSet approach is easier than specifying all of the required columns , but if you actually try it, you'll see that there are actually quite a few Attributes that will be returned, all of which contain data that is of no value to this particular method).
  • Define an EntityCollection as a container for the QueryByAttribute and call the RetrieveMultiple method of the OrganizationService
  • Check the EntityCollection container and make sure that it is not null, then verify that the Count property of the Entities collection is greater than 0.  Because this is a RetrieveMultiple method, it's possible that more than one item is returned. Since the method only returns a scalar value this would present a problem of determining which value you should use. If you define the Entity name correctly however, and don't reuse Key names in the Entity (which would make very little sense and would fail validation if you were using a web.config or app.config file), you'll only get one record back if there's a match, or 0 if there is no matching key.
  • Once you've verified that there's at least one Entity in the Entities collection, reference the first item of the collection and the corresponding attribute name.
  • At the beginning of the method I included validation to check for a KeyName.  In a production application, I'd verify each of the parameters here but for the sake of readability I just cut it short. Additionally, b/c this is a method of the OrganizationService, it could possibly fail and result in an exception being throw with the most likely type being a FaultException<Microsoft.Xrm.Sdk.OrganizationServiceFault>.  There's more information on how to handle this at MSDN.  Once the exception is trapped, a StringBuilder is used to construct an informational message that is in turn used by the Logger (Part of the Enterprise Library's Logging block).  This is purely optional and will vary depending on your requirements.  The exception handling here is just for illustration purposes - so don't take it as an example of what to do in production (in fact eating an exception and not rethrowing it is generally a really bad idea)

Once you have this code in place, you'd simply need to open up the Form in MSCRM and add whatever configuration values you need. It's a good idea to follow a easy to remember naming convention for your key names b/c in many cases, you'll end up with several items in it. You don't want to have to flip back and forth between your code and the MSCRM application each time you're trying to reference one of the items. In any case, that's pretty much all that needs done to have a working configuration system.  I'll be using this as part of some other posts I"m planning so will refer back to it frequently. Finally, for your reference, the completed code listing is provided at below.  Additionally, the C# code file is available for Download here:

namespace WilliamRyan.Xrm.Plugins
{
    #region references
    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Text;
    using Microsoft.Xrm.Sdk;
    using System.ServiceModel;
    using Microsoft.Xrm.Sdk.Query;
    using Microsoft.Xrm.Client;
    #endregion
    /// <summary>
    /// Handles interaction with a MSCRM 2011 OrganizatonService
    /// </summary>
    public class XrmSdkHelper
    {

        #region
        private CrmConnection connectionInstance;
        private IOrganizationService orgService;
        #endregion
        #region properties
        /// <summary>
        /// Instance of the <see cref="Microsoft.Xrm.Client.CrmConnection"/>
        /// </summary>
        public CrmConnection ConnectionInstance
        {
            get
            {
                if (null == connectionInstance)
                {
                    connectionInstance = new CrmConnection("CrmMain");
                }
                return connectionInstance;
            }
        }

        /// <summary>
        /// Instance of the <see cref="Microsoft.Xrm.Sdk.IOrganizationService"/>
        /// </summary>
        public IOrganizationService OrgService
        {
            get
            {
                if (null == orgService)
                {
                    // Put in check to make sure CrmConnection is valid
                    orgService = new OrganizationService(ConnectionInstance);
                }
                return orgService;
            }
        }
        #endregion

        #region methods
        /// <summary>
        /// Calls the overloaded version of GetConfigurationValue
        /// </summary>
        /// <param name="keyName">Name of the Key Field used in the configuration entity</param>
        /// <returns><see cref="System.String"/> value containing the Configuration value if one
        /// is found.</returns>
        public String GetConfigurationValue(String keyName)
        {
            return GetConfigurationValue(keyName, "ryan_configurationset", "ryan_keyname", "ryan_itemvalue");
        }

        /// <summary>
        /// Queries a configuration entity to find a match for a given key field.
        /// </summary>
        /// <param name="keyName">Name of the Key field of the configuration <see cref="Microsoft.Xrm.Sdk.Entity"/>.</param>
        /// <param name="entityName">Name of the Configuration <see cref="Microsoft.Xrm.Sdk.Entity"/>. </param>
        /// <param name="keySchemaName">Schema name of the key field in the configuration <see cref="Microsoft.Xrm.Sdk.Entity"/>.</param>
        /// <param name="itemSchemaName">Schema name of the value field in the configuration <see cref="Microsoft.Xrm.Sdk.Entity"/>.</param>
        /// <returns><see cref="System.String"/> value containing the Configuration value if one
        /// is found.</returns>
        public String GetConfigurationValue(String keyName, String entityName, String keySchemaName, String itemSchemaName)
        {
            #region validation
            if (String.IsNullOrWhiteSpace(keyName))
            {
                throw new ArgumentNullException("keyName", "Value can not be null.");
            }

            if (String.IsNullOrWhiteSpace(entityName))
            {
                throw new ArgumentNullException("entityName", "Value can not be null.");
            }

            if (String.IsNullOrWhiteSpace(keySchemaName))
            {
                throw new ArgumentNullException("keySchemaName", "Value can not be null.");
            }

            if (String.IsNullOrWhiteSpace(itemSchemaName))
            {
                throw new ArgumentNullException("itemSchemaName", "Value can not be null.");
            }
            #endregion
            String KeyValue = null;

            QueryByAttribute Query = new QueryByAttribute(entityName);
            Query.AddAttributeValue(keySchemaName, keyName);
            Query.ColumnSet = new ColumnSet(new String[] { itemSchemaName });
            try
            {
                EntityCollection Configs =
                            OrgService.RetrieveMultiple(Query) as EntityCollection;
                if (null != Configs && Configs.Entities.Count > 0)
                {
                    KeyValue = Configs.Entities[0][itemSchemaName].ToString();
                }
            }
            catch (FaultException<OrganizationServiceFault> badAttributeRequest)
            {
                StringBuilder ErrorMsg = new StringBuilder();
                ErrorMsg.AppendFormat(CultureInfo.CurrentCulture,
                                      "GetConfigurationValue for {0} failed.rn",
                                      keyName);
                ErrorMsg.AppendFormat(CultureInfo.CurrentCulture,
                                      "Details: {0} rn.",
                                      badAttributeRequest.ToString());
                Logger.Write(ErrorMsg);
            }
            return KeyValue;
        }
        #endregion
    }
}

 

————————————————————–

KeyWords: Dynamics4, Dynamics4.com, QueryByAttribute,OrganizationServiceFault, CrmConnection 2011, Microsoft.Xrm.Client.CrmConnection,  Microsoft.Xrm.Sdk.Entity, Microsoft.Xrm.Sdk.EntityCollection, OrganizationService, RetrieveAttributeRequest, RetrieveAttributeResponse, QueryByAttribute, RetrieveMultiple, ColumnSet, Enterprise Library 5.0, Logging Application Block,     William Ryan, Bill Ryan, QueryByAttribute

The post MSCRM 2011 – Using Custom Configuration Entities appeared first on dynamics four.

]]>
http://dynamics4.com/mscrm-2011-using-custom-configuration-entities/feed/ 1
PicklistAttributeMetadata – Interacting with MSCRM 2011 Picklist Entities http://dynamics4.com/picklistattributemetadata-interacting-with-mscrm-2011-picklist-entities/ http://dynamics4.com/picklistattributemetadata-interacting-with-mscrm-2011-picklist-entities/#comments Wed, 22 May 2013 02:56:47 +0000 Bill Ryan http://www.williamgryan.mobi/?p=587 PicklistAttributeMetadata PicklistAttributeMetadata is the primary vehicle that allows you to retrieve information about Microsoft Dynamics CRM 2011 (MSCRM) Picklist values.  Every notable Picklist property that you’d need to interact with ...

The post PicklistAttributeMetadata – Interacting with MSCRM 2011 Picklist Entities appeared first on dynamics four.

]]>
PicklistAttributeMetadata

PicklistAttributeMetadata is the primary vehicle that allows you to retrieve information about Microsoft Dynamics CRM 2011 (MSCRM) Picklist values.  Every notable Picklist property that you’d need to interact with is available using it. And fortunately, it’s quite simple to  use.

A common scenario you run into is creating a form or control in ASP.NET / Silverlight that mimics the behavior of an actual MSCRM form.  So let’s say that one of the attributes you need to simulate is a Picklist.  You have two options. The quick and dirty (and lame) approach is to hard code both the label and values for each item.  If your app is used in multiple locales that happen to use different languages, then you have to not only populate the control using the primary language, but you’d need to make sure it’s populated correctly using each of localized values as well.  This is first and foremost lame.  It’s error prone b/c you have to ensure that you match each value in each respect or there will be a mismatch when you attempt to use values you retrieved from the control or form.  You’d also be very vulnerable to changes in environments (where the values are different) or changes made after deployment. It also means you’d be duplicating effort, not just now but in the future any time values changed.  Additionally, it would be very easy for someone to make a change to the existing picklist and without realizing it, break any app that was dependent on matching values.  I’ll leave it at this, hard coding is pretty much always a bad idea and in this case, it’s extremely bad for many reasons.

If you aren’t familiar with them already, please take a quick look at using the CrmConnection (Microsoft.Xrm.Client.CrmConnection ) and IOrganizationService / OrganizationService (Microsoft.Xrm.Client.Services.OrganizationService) as  refresher as the OrganizationService is the basis of all of the api interaction I’ll be discussing.  So start out by instantiating a new instance of the CrmConnection, then instantiating a new OrganizationService instance, passing in the CrmConnection you just created to the OrganizaitonService ‘s constructor.

CrmConnection ConnectionInstance = new CrmConnection("MSCRM");
IOrganizationService ServiceInstance = new OrganizationService(ConnectionInstance);

So the first step you’ll take after creating the CrmConnection and OrganizationService is to create a new RetrieveAttributeRequest (Microsoft.Xrm.Sdk.Messages.RetrieveAttributeRequest). If you have multiple Picklist s that you want to simulate, I’d recommend making this a function and using parameters so that the code is repeatable, but that’s an app specific design consideration that is not really relevant to this discussion.

RetrieveAttributeRequest NewAttributeRequest = new RetrieveAttributeRequest
{
    EntityLogicalName = "contact",
    LogicalName = "shippingprovider",
    RetrieveAsIfPublished = true
};

Creating a new RetrieveAttributeRequest is pretty much as simple as it looks. The only thing to keep in mind is that the EntityLogicalName is the name of the Entity containing the specific attribute defined as a Picklist.  The RetrieveAttributeRequest.LogicalName is the name of the specific attribute.  The RetrieveAttributeRequest.RetrieveAsIfPublished property does exactly what you’d expect it to – if the modifications to the picklist have been published, they’ll be returned if the RetrieveAsIfPublished is set to true and it’ll ignore than if RetrieveAsIfPublished is set to false.

With the RetrieveAttributeRequest created, you simply need to pass it to the Execute method of the OrganizationService you defined. To do much with it though, you’ll need to follow the Request/Response design pattern used throughout the MSCRM 2011 SDK and capture the return information with a RetrieveAttributeResponse (Microsoft.Xrm.Sdk.Messages.RetrieveAttributeResponse) item:

RetrieveAttributeResponse PicklistResponse = (ServiceInstance.Execute(NewAttributeRequest) as RetrieveAttributeResponse);

Although not necessary, I recommend using the as operator to perform the case and then checking whether or not the value is null to see if it was successful (all caveats about exception handling and logging are obviously in place here).  Once you confirm the RetrieveAttributeRequest call was successful, you can use the several properties of the RetrieveAttributeResponse to interact with the metadata.

The RetrieveAttributeResponse has several useful properties that you’ll want to take advantage of. One of the more useful ones is called unsurprisingly, RetrieveAttributeResponse.AttributeMetadata.

if (null != PicklistResponse)
{
   PicklistAttributeMetadata PicklistMetaDetails = (PicklistResponse.AttributeMetadata as PicklistAttributeMetadata);
   if (null != PicklistMetaDetails)
   {
   ...
   }
}

Once you’ve verified the existence of the RetrieveAttributeResponse, you simply reference the AttributeMetadata (which also entails another cast operation, which I’d similarly recommend using the As operator to perform).

The PicklistAttributeMetadata contains several pieces of important information that you’ll want to interact with and in this case, you’d want to reference the OptionSet property (PicklistAttributeMetadata.OptionSet ).  Each item in the OptionSet is held in the Options collection which has a ToArray (non-generic) implementation out of the box.  Once you’ve referenced this, there are several pieces of pertinent data exposed as properties which you can use to customize the behavior of your new page or controls.

Each of the Option values are typed as OptionMetadata (Microsoft.Xrm.Sdk.Metadata.OptionMetadata) items.  These properties include the Label property, and Value property among others, but these two are the ones you’ll likely be using most.  The Label and Value properties work just like you’d expect them to, the Label is the text defined to be shown in the Picklist, the Value is the integer value that it represents. However there’s one thing worth noting.  If you only have your labels defined in one language, and are 100% sure that you’ll never ever have another language defined, there’s little risk to referencing the Label property as it stands. But in most cases, you either will have multi-language support or won’t be so sure of the future, so it’s safer to use the Label.UserLocalizedLabel property which provides the text that’s actually localized to the specific user. Using the UserLocalizedLabel however does mean that youll need to be cognizant of the user context the operations are being performed as but that’s going to be the case for many other reasons outside of this. Plugins or Workflows will already have this awareness factored in so it’ll only be a concern if you’re using a generalized account that needs to be aware of different locales. The relevant pieces are shown below:

if (null != PicklistResponse)
{
   PicklistAttributeMetadata PicklistMetaDetails = (PicklistResponse.AttributeMetadata as PicklistAttributeMetadata);
   if (null != PicklistMetaDetails)
   {
     OptionMetadata[] OptionsList = PicklistMetaDetails.OptionSet.Options.ToArray();
     if (null != OptionsList && OptionsList.Length > 0)
     {
        // Bind to control, iterate, do with what you will here
        // using OptionMetadata.Label or
        // OptionMetadata.Label.UserLocalizedLabel.Label
        // OptionMetadata.Value etc.
     }
  }
}

The fully assembled code is shown below:

CrmConnection ConnectionInstance = new CrmConnection("MSCRM");
IOrganizationService ServiceInstance = new OrganizationService(ConnectionInstance);

RetrieveAttributeRequest NewAttributeRequest = new RetrieveAttributeRequest
{
   EntityLogicalName = "contact",
   LogicalName = "shippingprovider",
   RetrieveAsIfPublished = true
};
RetrieveAttributeResponse PicklistResponse = (ServiceInstance.Execute(NewAttributeRequest) as RetrieveAttributeResponse);
if (null != PicklistResponse)
{
   PicklistAttributeMetadata PicklistMetaDetails = (PicklistResponse.AttributeMetadata as PicklistAttributeMetadata);
   if (null != PicklistMetaDetails)
   {
     OptionMetadata[] OptionsList = PicklistMetaDetails.OptionSet.Options.ToArray();
     if (null != OptionsList && OptionsList.Length > 0)
     {
       // Bind to control, iterate, do with what you will here
       // using OptionMetadata.Label or
       // OptionMetadata.Label.UserLocalizedLabel.Label
       // OptionMetadata.Value etc.
     }
   }
}

 

 

————————————————————–

KeyWords: Dynamics4, Dynamics4,com,  MSCRM 2011, Microsoft Dynamics CRM 2011, CrmConnection 2011, OrganizationService, RetrieveAttributeRequest, RetrieveAttributeResponse, PicklistAttributeMetadata, OptionSet, OptionMetadata,

The post PicklistAttributeMetadata – Interacting with MSCRM 2011 Picklist Entities appeared first on dynamics four.

]]>
http://dynamics4.com/picklistattributemetadata-interacting-with-mscrm-2011-picklist-entities/feed/ 1
Managed Solution vs Unmanaged Solution Best Practices CRM 2011 http://dynamics4.com/managed-solution-vs-unmanaged-solution-best-practices-crm-2011/ http://dynamics4.com/managed-solution-vs-unmanaged-solution-best-practices-crm-2011/#comments Sat, 11 May 2013 05:50:54 +0000 Bill Ryan http://www.williamgryan.mobi/?p=351 Managed Solution vs Unmanaged Solution Best Practices CRM 2011 Managed Solution vs Unmanaged Solution Best Practices CRM 2011 is an argument that has been had quite a few times since ...

The post Managed Solution vs Unmanaged Solution Best Practices CRM 2011 appeared first on dynamics four.

]]>
Managed Solution vs Unmanaged Solution Best Practices CRM 2011

Managed Solution vs Unmanaged Solution Best Practices CRM 2011 is an argument that has been had quite a few times since the advnet of MSCRM 2011. The choice of Managed Solution vs Unmanaged Solution can have a huge impact on how deployments go so it’s an important distinction. There’s a lot about development that left much to be desired in previous versions of Microsoft Dynanics CRM.  B/c of how strange it was, new developers frequently abandoned traditional development practices and just did things in a largely one-off fashion.  Microsoft Dynamics CRM 2011 introduced many developer friendly features and concepts, among them were Managed Solutions and UnManaged Solutions.

<SoapBox Process=‘AutoStart’ Completion=‘Automatic’>

My fundamental guidance is this – you can read all the posts you can, dissect solutions as I do in this article, but here’s the takeaway. Development and test all you want with Unmanaged Solutions. When you’re ready to package up your product and either deploy it or ship it, Make the Final Release Version a Managed Solution. Just like we use Debug builds for development and do a special Release compile for our final product, the same should be done here.  The physical reasons are different but the functional ones are identical. So when it’s all said and done, which you should use amounts to “Both” . Using Managed Solutions for deployments that aren’t finalized will lead to unnecessary headaches. Using Unmanaged Solutions for final deployments will result in sloppy and potentially problematic installs (you can get either wrong, Managed Solutions aren’t a magic bullet – But they do allow you to minimize known risks and pretty much prevent such risks.  Some problems may be inevitable but safeguarding against the ones you can control is the only prudent course of action. If there’s one thing I want you to take away, it’s this. You may have valid reasons for doing one or the other but don’t do it based on superstition of water cooler talk. Development goals regarding deployment are pretty universal – do it, do it right, make it predictable, make sure it works, if it fails, make it easy to diagnose and correct. To that end, Unmanaged Solutions throughout, Managed Solutions for the final product are the winning combination for pretty much everyone. This isn’t the final world on everything related to solutions, I’ll explain ‘why’ later and show how to do it and automate it with TFS Build and MSBuild, but this is the groundwork needed before going much further.

</SoapBox>

The term Solution is one that’s used all over the place but it’s a proper noun in this sense. Moreoever, although it’s a proper noun, it’s one that’s used in a different context from the other Proper Noun Solution used in Visual Studio Development. Anyway, I want to start a discussion about Solution Management and Dispel some major nonsense that’s prevalent in the CRM Development community.

In theoretical terms , the question “Shoudl I or Shouldn’t I use Solutions” is an idiotic one. You are going to use a solution whether you want to or not, whether you realize it or not. By default, when you install CRM, a solution, not surprisingly named the “Default Solution” is created.  When someone asks if they should use a Solution or not, what they really mean is “Should I use a solution OTHER than the Default Solution“.

I’ll cut to the chase on things and work my way backward from here.  Also, I’ve read more discussions on the net about the issue than I care to recall and I’ll link to them at the bottom (it’s a waste of time for the most part unless you really just feel compelled to prove something to yourself and are a glutton for punishment).  Remember though, just b/c it’s on the Internet, just b/c it’s on a blog and the blogger has a fancy title, just b/c he seems certain of himself, just b/c she has tons of commenters who agree, that doesn’t mean someone is right. [Note that I'm referring to myself just as others in many of these cases - so I'm not just saying, I'm YELLING, Don't just take my word for it - Look for yourself!!!! ] These can all be  good indicators, but people are wrong frequently and commentors can at time be little more than an echo chamber. Additionally, there are often prevailing myths repeated over and over that are completely untrue. If you don’t believe me, I’d use the example of “Benefits of Stored Procedures” as my best example. For a long time, it was taken as axiomatic that Stored Procedures were preferable to in-line SQL (particularly with SQL Server) b/c of Security, Performance and Maintainability.  Everywhere you turned, every forum, blog post after blog post, you’d hear someone parrotting the line about Stored Procedures being a best Practice. One day, my friend Frans Bouma whipped out his PimpHand, puts some weights in it for good measure, and just issued a collective Bitch Slap to all the Stored Proc Fetishists out there.  It caused lots of butthurt, there was much gnashing of teeth, but what there wasn’t was a Legitimate Rebuttal to Frans post. And what made Frans bitch slapping so worthy is that he didn’t just use his opinion or experience, he went straight to the Sql Server Books Online documentation and took it straight from the horses mouth. Although this post is about Managed Solutions vs Unmanaged Solutions, I’d like to quote from his post to illustrate the point (b/c in the rest of this point, I’m going to copy my brilliant Dutch Friend’s technique):

—————————————————————————————————————————————————————-

Everyone who thinks stored procedures are pre-compiled, say “Aye!”. Whoa, what a noise! For all of you who said “Aye!” a few seconds ago: open SqlServer’s Books Online (v7 or v2000, doesn’t matter), search for “cache execution plan”. You’ll find fine articles like “Execution Plan Caching and Reuse” and “SQL Stored Procedures”. Let me just quote some lines from the “SQL Stored Procedures” article:

SQL Server 2000 and SQL Server version 7.0 incorporate a number of changes to statement processing that extend many of the performance benefits of stored procedures to all SQL statements. SQL Server 2000 and SQL Server 7.0 do not save a partially compiled plan for stored procedures when they are created. A stored procedure is compiled at execution time, like any other Transact-SQL statement. SQL Server 2000 and SQL Server 7.0 retain execution plans for all SQL statements in the procedure cache, not just stored procedure execution plans.

————————————————————————————————————————————————————–

One last thing before going too much further. I’ll posit that there’s the conventional wisdom going around about Solutions (Managed Solutions and UnManaged Solutions Alike) that says something to the effect of “If you’re a 3rd party vendor, use Managed Solutions b/c you have to, otherwise use Unmanaged Solutions b/c Managed Solutions aren’t well suited to in house development”.  Here’s a few samples:

Each of the discussions brings up valid points and does a good job highlighting the differences.  My problem isn’t with any of the discussions. However different people can walk away from reading the exact same thing and have completely different conclusions. After reading any of those posts or coming here, I hope you understand the basic distinctions. I hope you’re hear to see why I have a different opinion and what proof I offer to support that opinion.

So the consensus seems to be that if you’re a 3rd party vendor or ISV, use Managed Solutions (mostly b/c you have to). If you’re in-house, use unmanaged solutions b/c Managed Solutions aren’t ready for prime time, don’t apply to your development scenario or are too punitive in the way they behave. I’m calling Bulls*** on each of those. Again, just to be diplomatic, I have tremendous respect for everyone I linked to above (in fact you may well notice I’m a featured blogger on one of the sites – or was anyway in a previous life). I think they all cover the issue correctly. The commenters are where things are going off the track (and those are frequently cited) and those for some reason, tend to set the tone of the conventional wisdom.

Managed Solutions Are Only Good for 3rd Party Vendors and ISV’s:

This one annoys me the most.  It’s true that if you want to sell things in the MarketPlace, you need to use Managed Solutions.  But if you have to draw a naive conclusion from that fact alone, wouldn’t you assume that it’s a “GOOD THING” since Microsoft forces them to use them? The retort I frequently hear is “Well maybe that’d be a safe assumption, but the reason is b/c it has to be that way to distribute it.”.  Well, that’s not true.  I can send you an installer package or give you an unmanaged solution and you can easily install it. There are API methods that handle installation and they don’t discriminate (if you’re not familiar with it, the ImportSolutionRequest and ImportSolutionResponse messages handle this for you). We know that everything that happens in CRM happens through the CRM Web Services so there’s nothing magic about clicking the Import Solution or Export Solution buttons. They trigger internal web service calls and on importing, the ImportSolutionRequest is what’s behind the button click. My point is that you can just as easily pass a byte array from an UnManaged Solution to the ImportSolutionRequest call as you can a Managed Solution’s byte array – so the whole “Deployment” issue is a non-starter. That leaves us at “Microsoft determined it’s the most effective way to distribute content” as a starting point.

But let’s look at this another way.  There might be things that could benefit a child but not his parents. There could be things that benefit a husband and not his wife.  But what’s different in this regard? If getting X deployed in your environment if I built it, what’s different if you built it? Either way it needs deployed easily, correctly and effectively. It needs to work, it needs to fail gracefully if it fails and it needs to let us know how things went (so we can confirm things went well, or track down where they didn’t).

Some will make the case (which is not very compelling) that Managed Solutions are used to protect intellectual property. Well, as flimsy as it is, I would argue the same applies as a benefit to in-house shops and ISVs.  The reasons may be different, but the fewer people that know the inner workings of the software, the better in terms of security.  In either regard the Public API is visible and how to use the Black Box is known – so the only difference is seeing the inner workings.  If it’s in house, development will have those details so there’s no downside in this regard.  This means that 3rd party vendors can keep prying eyes out but allow them all the info they need to use the stuff, and inhouse shops can do the same while allowing people that *should* see the inner workings see what they need.

The Whole Value of CRM is Customizing Entities – With Managed Solutions People are Prohibited from Doing that

This is one of those points so utterly dumb I am almost embarrassed to refute it.  Few people should have direct access to the Deployed Project. Users should be able to use it, but as far as being able to change schema, dlls or the like, the fewer the better. if you don’t concede this point, I really can’t help you. If you’re making changes on Production, you’re doing it wrong. Production shouldn’t be your sandbox. It shouldn’t be a place to experiment. It should be the place where things go that are tested and verified.  So, in development, you use Unamanged solutions and change them all day long. Then in production, you use a Managed Solution to stop people (and access control should be doing this primarily) from doing this. The Managed Solution is the 2nd part of a structured insurance policy.    This is a feature not a bug, and if you can’t see that, you’re looking at it incorrectly.

Managed Solutions Mean Data Loss

You have the ability to fully uninstall a Managed Solution and if you do, it does what it’s supposed to – it removes what was added in the first place.  So yes, this could mean data loss.  However data loss isn’t the issue, it’s unwanted or unexpected data loss. Even if it really risk unwanted data loss, the database should be getting backed up regularly so you should have backups with the pre-rollback version. But even granting this can be misleading. When you uninstall something, you typically want it removed which is what uninstalling a managed solution does. Let’s say we had an inhouse system and we found out it was computing incorrectly and letting people take out more money than they had. We decide that it needs removed and rebuilt. Do you want to let this exist in any capacity until the new version is rolled out? No, you want it stopped. The confusion comes from people thinking that Bugs and Service Pack type additions entail a full rollback. That’s not the case.  You use Managed Solutions (and UnManaged ones) in a Structured or Scaffolded approach – you add to them. If a field is removed in a later version, that’s by design and it shouldn’t be there anymore, otherwise you’d leave the field in. If the data needs stored “just in case” then you’d keep a backup – but the field should be gone from the production system. If not, then it shouldn’t be removed in the first place. DUH.I could go on and on about every possible solution but suffice to say I’ve dealt with this inside and out and there’s only data loss if you do it wrong, handle things incorrectly or are completely careless.  Changes should be made through updated versions, not removal of the old system and reapplying it. If you do this, data loss won’t be an issue for you. (If you want to argue some one off scenario with me – please do so in the comments. I admit I didn’t cover every possible example but I’m prepared to if someone wants to argue)

Managed Solutions are Too Rigid

By this they usually mean that at some point, just b/c you put something in a Managed Solution, it can never be changed. This simply isn’t true. Just b/c it’s in a Managed Solution doesn’t mean it’s wholly locked down, If it’s locked down it has to be done so by design, intentionally, so the author(s) will know exactly what they’re locking down (again, it doesn’t happen by default or magic).

Managed Solutions Don’t Work for Large or Complicated Enterprise Installs

This is another one of those myth’s (I believe started by a commentor at Gonzalo’s excellent blog. I’ll note it wasn’t Gonzalo making the point.  Ok, so some guy on a blog comment says he had problems with the feature, so clearly the feature is the problem.  If that was true, then the whole 3rd Party/ISV market would cease to exist and what was there would be vendors currently being sued out of existence. I don’t want to be accused of pimping anyone’s projects but I can tell you for sure that many products in the market are being used on HUGE, Extremely large, extremely complex installs and working just fine.  Look through the marketplace. Do you see any warnings “Only works on Single Web Server or Database Server”? Nope. Do you see any “Won’t work with Firewall”, or “Won’t work with Load Balancer” or “Won’t work with Clustered Servers”? Me Neither. That’s b/c no such warnings exist. Look around the feedback – while there hvae been problems with certain vendors products (software after all isn’t a bug free endeavor, especially with new products) do you see any that have great feedback saying “Works Killer on our Single Server Simple Install” and then diametrically opposite ones “Absolute Disaster on our Big Complex system”?  Me Neither. That’s b/c they don’t exist. This is 2012 and particularly in an online marketplace, if your stuff sucks and has problems, it’ll be known. There’s other issues with any given product, there are even issues with CRM 2011 itself, but not anything magic with CRM 2011 and Managed Solutions.

This leads me to my main point. Look at all the discussion about the vast differences between Managed Solutions and UnManaged Solutions. They mostly read like a list of bullet points (and that’s fine, most people want a synthesized side by side comparison) . Many of these are just taken verbatim from the SDK documentation or taken from other people’s blogs (Sadly, I’m starting to get popular enough that I’m getting a lot of people plagairizing my content and when searching for other items, I see a lot that’s plagairized as well). Spouting off something when you didn’t do the research and you’re holding yourself out as an Expert (which being a blogger tends to do at least implicitly) is irresponsible. But blogs are free content so it’s buyer beware.  So what is a solution?

MSCRM Solution File

In it’s basic form, it’s a .zip file containing a minimum of 3 .xml files:

  • [Content_Types]
  • customizations
  • solution

The below image shows a basic structure of an unmanaged solution that has only a few entities in it (no web resources, no workflows or plugins)  and for the record, a managed solution showing the same items would look identical:
Managed Solution vs Unmanaged Solution Best Practices CRM 2011
Ok, nothing too dramatic (and we’ll go through what’s in those files shortly. However this shows a relatively trivial solution, most production ones will have other items such as Web Resources and Workflows/Plugins.  So what does a ‘real world’ non-trivial solution look like? See the next image:
Managed Solution vs Unmanaged Solution Best Practices CRM 2011
Pretty much the same thing but with a Workflows and WebResources folder.

The Workflows Folder (Managed Solution | Unmanaged Solution)

If you examine the Workflows Folder show above what you’ll see is a bizarrely named .xaml file. That file(s) (unless you named it yourself when you added it and changed it to something without a Guid identifier in the name) will typically look like some name you gave it and/or the process being performed with a Guid appended to the end of it. CRM does this automatically in part to help insure that the resource name doesn’t run into any conflicts with existing or future items. But yes, it’s just a xaml file with a distinct name as shown below:
Managed Solution vs Unmanaged Solution Best Practices CRM 2011

The WebResources Folder (Managed Solution | Unmanaged Solution)

The Web Resources folder isn’t much different, it contains each of the Web Resources with a Distinct name and a little identifiable information on it:
Unmanaged Solution Contents WebResources Folder
It’s worth repeating at this point that if I had done this with a Managed Solution instead of an Unmanaged Solution, nothing I said so far would be different.

When you dig into the Solution file, it reminds you of what a Visual Studio Solution (.sln) file looks like. What’s that mean? In it, there’s XML that contains information about the projects and their locations that the solution contains. Combined with the Project file (which in turn is just an XML file that provides build instructions about all the contents it contains, via XML of course) MSBuild has all it needs to create the applications or web sites specified in the respective projects and solutions. There’s no exact metaphor equivalent for Projects here, but the Solution.xml file simply contains information about all the items that will be deployed (it has information about missing dependencies and a few other nuances – which are used to help drive the Import Solution UI Sequence). In a nutshell though, you’ll see references to the items shown above, along with the other deployment artifacts:

<RootComponent type="61" schemaName="gaf_acctmgtsolutionconfig" />
<RootComponent type="61" schemaName="gaf_acctmgtsolutionconfig" />
<RootComponent type="61" schemaName="gaf_script_account_fields" />
<RootComponent type="61" schemaName="new_html_twostepper_warning" />
<RootComponent type="61" schemaName="new_icon_accountimportid_16" />
<RootComponent type="61" schemaName="new_icon_accountimportid_32" />
<RootComponent type="61" schemaName="new_icon_area_16" />
<RootComponent type="61" schemaName="new_icon_area_32" />
<RootComponent type="61" schemaName="new_icon_corporatenotes_16" />
<RootComponent type="61" schemaName="new_icon_corporatenotes_32" />
<RootComponent type="61" schemaName="new_icon_coverage_16" />

As you can see, there’s a direct mapping with the only distinction (if I showed you the entire contents it’d probably be a little clearer but too wide to easily show in the blog layout) is the Guid appended at the end – but look through the sample, you’ll see the name equivalence. The main other piece is the Customizations (which show what will happen to the entities). On Import, here’s what happens.

Each of the web resources are examined and corresponding deployment api methods are called on them, putting them in the active system. The customizations are reviewed and the corresponding changes are made to it (ultimately they translate to SQL DDL Statements, ALTER TABLE, CREATE TABLE and the like – I highly encourage you to turn on Sql Profiler on an Import to see this happen real-time if you’re seriously intersted in learning what happens).

So let’s summarize the Import Process. A .zip file is extracted and the items are taken apart. Each one has its own purpose, but basically you have a portion that looks at various text , xaml and compiled code and inserts it into the CRM Database. It also extracts information needed to build SQL Statements, and it uses that to fire changes that need made to the schema. This ends up meaning you have some conversion and a bunch of Web Service calls and SQL Statements being fired. That’s a SOLUTION. That’s what it does.  Managed Solutions vs Unmanaged Solutions to the EXACT SAME THINGS. Where they differ is exclusively on the parameters passed in to those methods which tell it what behavior to allow.  But that’s an implementation detail. That means you could have an Unmanaged Solution that looked exactly alike and behaved almost exactly like a Managed one. That means you could manually edit an Unmanaged solution to be a managed one and vice versa (doing so would be a silly waste of time outside of illustration, but could be done nonetheless to prove the point).

At some point in the future, (as soon as I have time, hopefully tomorrow) I’ll go through the solution elements one by one and show exactly how they work, but my point for this post is to show the differences, and lack thereof of Managed Solutions vs Unmanaged Solutions.  Now that we know what goes into a solution, and what it does, let’s see if they’re really different after all.

I went ahead and created a CrmOnline sandbox at CrmOnline. I created a few small customizations and then made a Solution named “UnManagedSample” (probably should have just named it sample, since the same solution was exported, first as an UnManaged Solution then as a Managed one.  But suffice to say, I used the exact same entities, had the same dependencies met/ignored, used the same settings and did everything so it’d be identical other than making one managed and one unmanaged.  Here’s the output for both. Be honest, can you tell which is which (remember they both have the same name “UnManagedSample” so don’t try to cheat using it).

First off is a side by side showing the structure of the Managed Solution versus the Unmanaged Solution:
Unmanaged Solution versus Managed Solution MSCRM 2011

Here’s the first one:
Unmanaged Solution CRM 2011
Here’s the second one:
Managed Solution CRM 2011

Lest I be accused of cheating by collapsing the elements, note that the file sizes are identical (which could mask differences possibly, but I assure you in this case they are identical. In fact, you can pull down both sets if you’d like to compare them on your own, which I would highly encourage you to do so you don’t just take my word for it:

The main thing I wanted to dispel was the myth that there’s some major differences between the two. As you can see, in this example, they’re virtually identical in every single meaningful way. The differences aren’t even inherent.  Yes, these are trivial solutions but that doesn’t change the nature of anything.  That doesn’t mean there aren’t practical/functional differences, but when you see it from this perspective, all the “Doesn’t work in large deployments” mythology starts to look pretty flimsy, doesn’t it?  So next up (the post isn’t up yet but will be – so i”ll update when it is, the link is already setup though so you can count on it being there – Managed Solutions vs Unmanaged Solutions, Part II) I’ll walk through what each aspect of the solution does and show specific instances of differences between the same solution being implemented with Managed Properties (which is where the real differences lie). Remember this – if Managed Properties aren’t used on the Entities, there’s not any real difference between the two solution types  as I showed above. Once we change the properties to Managed Properties, we’ll start to see some differences, but they’re still quite minor.  So all this arguing is around basically, how many angels can dance on the head of a pin.

 

 

————————————————————–

KeyWords: Dynamics4, Dynamics4.com,  Managed Solution CRM 2011, Unmanaged Solution CRM 2011, Managed vs Unmanaged Solution, CrmConnection,  ImportSolutionRequest, ImportSolutionResponse, PublishAllXmlRequest, PublishAllXmlResponse, CrmConnection,   CrmConnection 2011 , Solution Management Best Practices MSCRM 2011

The post Managed Solution vs Unmanaged Solution Best Practices CRM 2011 appeared first on dynamics four.

]]>
http://dynamics4.com/managed-solution-vs-unmanaged-solution-best-practices-crm-2011/feed/ 6
MSCRM 2011 Plugin Messages & the PluginRegistrationTool http://dynamics4.com/mscrm-2011-plugin-messages-the-pluginregistrationtool/ http://dynamics4.com/mscrm-2011-plugin-messages-the-pluginregistrationtool/#comments Fri, 06 Jul 2012 03:39:33 +0000 Bill Ryan http://www.williamgryan.mobi/?p=313 PluginRegistrationTool My friend Nirnay Patel has a really useful post about titled CRM 2011 Plugin Messages.  I have to agree that a CombBox would be a lot more helpful than ...

The post MSCRM 2011 Plugin Messages & the PluginRegistrationTool appeared first on dynamics four.

]]>
PluginRegistrationTool

My friend Nirnay Patel has a really useful post about titled CRM 2011 Plugin Messages.  I have to agree that a CombBox would be a lot more helpful than an AutoFill TextBox for the Plugin Message List.  Once you’re familiar with the tool it’s not a big deal but when you’re new to it, its not exactly intuitive. In any case, the SDK provides the source code for the MSCRM PluginRegistrationTool.

I thought it was a pretty good suggestion and was bored, so I went ahead and made the change.  I left the AutoFillTextBox in place and just made some quick tweaks (it’s not optimal by any means but gets the job done). The ComboBox sits next to it so there’s no loss of functionality – but if you find it awkward, just remove it. In any case, if you want it, here you go:

http://www.dynamics4.com/downloads/stepregistrationform.resx

http://www.dynamics4.com/downloads/stepregistrationform.cs

http://www.dynamics4.com/downloads/stepregistrationform.designer.cs

Just download the files, copy them over yours (make a backup copy of your files if you don’t have one already – or better yet make sure you have the PluginRegistrationTool in source control) and compile it.  It’s Microsoft’s code initially and all credit goes to them – this is just a suggested modification to the PluginRegistrationTool.  There are quite a few other changes you will likely want to make but that’s for another discussion.

Leave any questions in the Comments section, or write me directly: wgryan@dynamics4.com– /b/ill
————————————————————–

KeyWords:  Dynamics CRM 2011,PluginRegistrationTool, MSCRM 2011 Plugin Messages, RelatedEntities,   CrmConnectionOrganizationServiceContext AssignResponse, AssignRequest, Enterprise Library 5.0, DiscoveryService, Microsoft.Xrm.Client.Services.OrganizationService, Dynamics4, Dynamics4.com

The post MSCRM 2011 Plugin Messages & the PluginRegistrationTool appeared first on dynamics four.

]]>
http://dynamics4.com/mscrm-2011-plugin-messages-the-pluginregistrationtool/feed/ 2
Encrypt CrmConnection – Use the Enterprise Library to Secure your CrmConnection http://dynamics4.com/encrypt-crmconnection-use-the-enterprise-library-to-secure-your-crmconnection/ http://dynamics4.com/encrypt-crmconnection-use-the-enterprise-library-to-secure-your-crmconnection/#comments Tue, 19 Jun 2012 23:06:53 +0000 Development Team http://www.williamgryan.mobi/?p=296 Encrypt CrmConnection + Enterprise Library 5.0 It’s no secret that Microsoft Dynamics CRM 2011 (MSCRM) allows developers to programmaticly pretty much anything an end user can do manually. All of the ...

The post Encrypt CrmConnection – Use the Enterprise Library to Secure your CrmConnection appeared first on dynamics four.

]]>
Encrypt CrmConnection + Enterprise Library 5.0

It’s no secret that Microsoft Dynamics CRM 2011 (MSCRM) allows developers to programmaticly pretty much anything an end user can do manually. All of the operations you perform (with very limited exceptions) are supposed to happen through the MSCRM Web Services (OrganizationService, DiscoveryService).  One of the reasons for this mandate is so that MSCRM can enforce access rights through its security model. This makes perfect sense.  If you have a Sql Server account that allows you to read/write and delete records, it would render the security model effectively useless.

Storing PlainText credentials in a code file or .config file is never a good idea but it’s a particularly bad idea with MSCRM Development. That’s b/c whichever way you chose to authenticate, you need to specify quite a bit of information and for OnPrem installs, you have to provide much more information than you otherwise would if you were just connecting to the database directly using a trusted connection.  That information includes a valid UserName, a Domain Name and a Password. Unlike other IDBConnection objects, the CrmConnection is really just syntactical sugar that wraps all the information you need to make web service calls. I’ve beaten the whole “how to use CrmConnection” discussion into the ground but just for a quick reference, here’s what a typical OnPrem CrmConnection value would look like in your .config file:

<?xml version="1.0" encoding="utf-8" ?>
<configuration>
  <connectionStrings>
    <clear/>
    <add name="Primary" connectionString="Url=https://crminstance/OrgName;  Username=**; password=**; />
  </connectionStrings>
</configuration>

B/c MSCRM development is still in its infancy in many ways, it’s very common to see MSCRM developers do things they would never dream of doing when they’re doing other .NET development.  The thing is though, MSCRM development is .NET development in every respect. Any bad development practice with respect to say ASP.NET is a bad practice when done with MSCRM. Sure, b/c there aren’t nearly as many books, articles, applications or experienced developers in the MSCRM world, one can get away with doing ‘bad’ things without worrying about all the negative repercussions you otherwise would, but bad is still bad.  I’ve personally seen a couple of different instances where the above code style was used and cleared a security audit b/c “You have to do it this way in MSCRM.” And in some ways, it’s true. If you encypt the connectionstring directly, you can’t use the CrmConnection.Parse method the way you normally would. You can’t instantiate the CrmConnection with the overloaded constructor that takes in the ConnectionString name.  yes, you can still ‘get there’ but it’s very awkward and completely offsets most of the benefits associated with using the CrmConnection in the first place. Fortunately, there’s a very simple solution that doesn’t carry with it any of the baggage a ‘rolling your own’ approach would. One such solution is using the Enterprise library.

So assume that I have the connectionString setting shown above. Opening up the Enterprise Library 5.0 Configuration Tool you have access to the Database Settings component.  In this case, our CrmConnection ‘s ConnectionString is named “Primary“.  Below is what the config tool looks like:
CrmConnection
-
So first you need to specify the Default Database (in this case named ‘Primary‘). Next, you need to chose the Protection Provider. OOB you can use the RsaProtectedConfigurationProvider to the DataProtectionConfigurationProvider. (You can click through the respective links with help deciding which one is best suited for your scenario. I can say with certainty though, choosing either of them is a more secure solution than not encrypting your CrmConnection so comparatively, you can’t really get it wrong). The  only other option is whether or not you want to force someone to input a password to change the values (I can’t think of a case where you wouldn’t want to password protect it – if you don’t, nothing would stop someone from injecting information in the config file by simply copying and pasting over the existing values).

Once you’ve done this, you just need to Save the configuration and you’re pretty much done.  After you save, the exact same information shown in the first example turns into the following:

<?xml version="1.0"?>
<configuration>
  <configSections>
    <section name="dataConfiguration" type="Microsoft.Practices.EnterpriseLibrary.Data.Configuration.DatabaseSettings, Microsoft.Practices.EnterpriseLibrary.Data, Version=5.0.414.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35" requirePermission="true" />
  </configSections>
<dataConfiguration configProtectionProvider="RsaProtectedConfigurationProvider">
  <EncryptedData Type="http://www.w3.org/2001/04/xmlenc#Element"
    xmlns="http://www.w3.org/2001/04/xmlenc#">
    <EncryptionMethod Algorithm="http://www.w3.org/2001/04/xmlenc#tripledes-cbc" />
    <KeyInfo xmlns="http://www.w3.org/2000/09/xmldsig#">
      <EncryptedKey xmlns="http://www.w3.org/2001/04/xmlenc#">
        <EncryptionMethod Algorithm="http://www.w3.org/2001/04/xmlenc#rsa-1_5" />
        <KeyInfo xmlns="http://www.w3.org/2000/09/xmldsig#">
          <KeyName>Rsa Key</KeyName>
        </KeyInfo>
        <CipherData>
          <CipherValue>iHv/Y1Vn7V6NSZT4Et5adE137Vm8z3vnRxiJSl5SMX6iK1aEjUL8rttdtJMuryn2oEhjup8O20j4KpjoRsVHYNIRyaZ7KRlpkqn5aDbv6rGRHvsOHANE53TYTT77IId7fGUHgAAY159G8WGlCJ73vbSEN/muJsRzB34qaPZ+QfY=</CipherValue>
        </CipherData>
      </EncryptedKey>
    </KeyInfo>
    <CipherData>
      <CipherValue>ynramhCcXmc3M7nAQns+hUErLTBxT5IgK43E/sg9AlbGtMhK1AtL2JN1qNEkRln9NZY2VSOt12k=</CipherValue>
    </CipherData>
  </EncryptedData>
</dataConfiguration>
<connectionStrings configProtectionProvider="RsaProtectedConfigurationProvider">
  <EncryptedData Type="http://www.w3.org/2001/04/xmlenc#Element"
    xmlns="http://www.w3.org/2001/04/xmlenc#">
    <EncryptionMethod Algorithm="http://www.w3.org/2001/04/xmlenc#tripledes-cbc" />
    <KeyInfo xmlns="http://www.w3.org/2000/09/xmldsig#">
      <EncryptedKey xmlns="http://www.w3.org/2001/04/xmlenc#">
        <EncryptionMethod Algorithm="http://www.w3.org/2001/04/xmlenc#rsa-1_5" />
        <KeyInfo xmlns="http://www.w3.org/2000/09/xmldsig#">
          <KeyName>Rsa Key</KeyName>
        </KeyInfo>
        <CipherData>
          <CipherValue>PTBvWPczFyGMrLmFad8xtV1iokZLP6QWnUPv74EMR9u3ZNVyfP5SrcGx+IO6JEkwz0155PAvPmj6UzsAcPudI2Edvflkf4CjifMllwIEksOzK71On3WihURqL6C5KtJ7fdIZpX+Hh7BVI/7ZEuu3aOwrZry0lMeF4UoOIcWnvu4=</CipherValue>
        </CipherData>
      </EncryptedKey>
    </KeyInfo>
    <CipherData>
      <CipherValue>jdSzONKEqttMHeapu16JgkYLP2py9DIrjwshbB0YmMZjztXaaU4DKZBJmF544e4JZhjZ8OWSBmUATZghc7JSfY2rwo2wtfjjL5F/7PUieutty7UJbQUJ6Fu5CZli1REC1KxG8Mct8rluWujV7EstaslR0Zk9cQwK0Mv7S4+sEZ/ZQFOCbSDaK20a1N1zBrokqsXGktPlrhYYwsvP1G+5Z+KZixg4LEs6HaC1uR3/AIuJeII2bNl3h/ZLqHaWP5Rm</CipherValue>
    </CipherData>
  </EncryptedData>
</connectionStrings>
<startup><supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.0"/></startup></configuration>

Now you can use the following code to verify that everything is decrypted correctly and your old code will run correctly without any other modifications:

CrmConnection Primary = new CrmConnection("Primary");
Console.WriteLine(Primary.ServiceUri);
Console.WriteLine(ConfigurationManager.ConnectionStrings[1].ConnectionString);
Console.WriteLine(ConfigurationManager.ConnectionStrings[1].Name);

Running the above code returns the following values:
Console.WriteLine(ConfigurationManager.ConnectionStrings[1].ConnectionString);

ConfigurationManager.ConnectionStrings[1].ConnectionString
Url=http://****:5555/**; Domain=**.com; Username=**; Password=**
ConfigurationManager.ConnectionStrings[1].Name
Primary
For the pedantic, you may notice that the actual values in the initial config section and the output differ – in reality, they match exactly – for security purposes I replaced sensitive information with asterisks.

Leave any questions in the Comments section, or write me directly. wgryan@dynamics4.com– /b/ill
————————————————————–

KeyWords:  Dynamics CRM 2011,RSAProtectedConfigurationProvider,  DataProtectionConfigurationProvider,  William Ryan , Bill Ryan, RelatedEntities,   CrmConnectionMicrosoft.Xrm.Client, Microsoft.Xrm.Client.CrmConnectionOrganizationServiceContext AssignResponse, AssignRequest, Enterprise Library 5.0, DiscoveryService, Microsoft.Xrm.Client.Services.OrganizationService, Dynamics4, Dynamics4.com

The post Encrypt CrmConnection – Use the Enterprise Library to Secure your CrmConnection appeared first on dynamics four.

]]>
http://dynamics4.com/encrypt-crmconnection-use-the-enterprise-library-to-secure-your-crmconnection/feed/ 3
ImportSolutionRequest + PublishAllXmlRequest – Automating your MSCRM 2011 Solution Deployments http://dynamics4.com/importsolutionrequest-publishallxmlrequest-automating-your-mscrm-2011-solution-deployments/ http://dynamics4.com/importsolutionrequest-publishallxmlrequest-automating-your-mscrm-2011-solution-deployments/#comments Mon, 18 Jun 2012 17:56:17 +0000 Bill Ryan http://www.williamgryan.mobi/?p=287 ImportSolutionRequest The concept of a Solution in MSCRM 2011 is not hard to understand.  Essentially, it’s a collection of artifacts wrapped together in a zip file.  Because it shares the ...

The post ImportSolutionRequest + PublishAllXmlRequest – Automating your MSCRM 2011 Solution Deployments appeared first on dynamics four.

]]>
ImportSolutionRequest

The concept of a Solution in MSCRM 2011 is not hard to understand.  Essentially, it’s a collection of artifacts wrapped together in a zip file.  Because it shares the same name with a commonly used item in C# or VB.NET, many people initially get confused when working with them but there’s really not much to learn.

As MSCRM Development gets more common, traditional development practices such as Unit Testing and Continuous Integration will be come more and more common with MSCRM Development. Automating the exporting and importing of Solutions is extremely simple to do and allows you to incorporate the process into your automated build process.  Once you see how amazingly simple it is to do, you’ll be amazed you weren’t doing it all along.

First a little background.  It’s common to have a Development environment that serves as your sandbox.  Once you’ve got your changes built out and tested, you want to push those changes to another environment. Often this involves multiple environments. Working off that theme, I created a simple XML File that allows you to specify multiple target environments (delineated by the <site> element).  For each environment you use, there’s an attribute named ‘deploy’ that allows you to indicate whether nor not the solution should be pushed to that environment or not.  The next element is the <connectionString> element which contains a ConnectionString for a CrmConnection.  The last element used is the location of the Solution file itself. In most cases, you’d probably just have one file that was shared for each environment, but I decided to allow you to specify different Solution files just to make the utility a little more useful.  Below is an example of the XML File that I used for this project (you can certainly change it to fit your scenario. You may not need multiple environments. You may need more details or you may need less.  This is just a starting point for the discussion.

<?xml version="1.0" encoding="utf-8" standalone="yes"?>
<Installations>
  <site deploy="true" environment="Dev">
    <connectionString>Url=http://*******:5555/DEV1; Domain=***.com; Username=*****; Password=******</connectionString>
    <solutionFile>C:Users****DownloadsTest_1_0_0_0.zip</solutionFile>
  </site>
  <site deploy="false" environment="Stage">
    <connectionString>Url=http://********:5555/STAGE1; Domain=***; Username=*****; Password=******</connectionString>
    <solutionFile>C:Users******DownloadsGAFConnectAcctMgmt.zip</solutionFile>
  </site>
  <site deploy="false" environment="QA">
    <connectionString>Url=http://*******:5555/TEST1; Domain=***.com; Username=*****; Password=******/connectionString>
    <solutionFile>C:Users******DownloadsTest_1_0_0_0.zip</solutionFile>   
  </site>
</Installations>

The next piece of code is just a small snippet to loop through the XML and extract the information needed.

ImportSolutionRequest
ImportSolutionRequest

For the sake of brevity I didn’t include much in the way of null checking or verification, but you get the idea:

 

XDocument xmlDoc = XDocument.Load(FilePath);
var ConfigInfo = from c in xmlDoc.Descendants(SiteLiteral )
                 where Boolean.Parse(c.Attribute(DeployLiteral).Value) == true
                 select new{ConnectionString =(String)c.Element(ConnectionStringLiteral), SolutionFile= (String)c.Element(SolutionFileLiteral) };>

The next step is to loop through each of the new anonymous types we created above and extract both the CrmConnection ConnectionString and the Solution File location.  Next, you use the ConnectionString to instantiate a new CrmConnection object using the CrmConnection.Parse method. You then create a new OrganizationService, passing in the CrmConnection to its constructor. Next, you create a Byte Array that contains the contents of the solution file (which is after all just a .zip file).  Once you have each of those pieces, you create a new ImportSolutionRequest instance specifying the CustomizationFile property (which is the byte array referenced above).  Once you have that in place, all you need to do is call the Execute method of the OrganizationService instance, passing in the ImportSolutionRequest to it (in accord with the Request/Response pattern, you may or may not want to use the ImportSolutionResponse – it has some properties you may find useful. Adding an ImportSolutionResponse to contain the response is trivial so I won’t discuss it here, if you’re interested in its specific properties, you can read more about it here.

 

if (null != ConfigInfo)
{
 foreach (var ConfigInstance in ConfigInfo)
 {
  if (File.Exists(ConfigInstance.SolutionFile))
  {
   Byte[] SolutionBytes = File.ReadAllBytes(ConfigInstance.SolutionFile);
    if (!String.IsNullOrWhiteSpace(ConfigInstance.ConnectionString))
    {
     // Create the CrmConnection
     CrmConnection CrmConnect = CrmConnection.Parse(ConfigInstance.ConnectionString);
    //Create the OrgService
    IOrganizationService ServiceInstance = new OrganizationService(CrmConnect);
    //Create the Import Solution Requestion, giving it the Bytes from Solution File
    ImportSolutionRequest ImportRequest = new ImportSolutionRequest() { CustomizationFile = SolutionBytes };
    Console.WriteLine("Attempting to deploy solution...");
    //Import the solution
    try
    {
       try
       {
          OrganizationResponse ImportResponse = ServiceInstance.Execute(ImportRequest);
       }
       catch (Exception ex)

I left out much of the other code so you’ll need to adjust your braces if you copy this directly, but the remainder of the code is just exception handling and output.

Like everything of value in MSCRM Development, creating a CrmConnection and OrganizationService instance allow you to call all the API methods that MSCRM makes available. You just create a new instance of a Request object, in this case an ImportSolutionRequest and pass it to the Execute method.  Viola – if your configuration is valid and you have the permission needed, this will deploy your solution.  If you make this part of your build, you’ll want to make sure you take care to cover all the detailed stuff, making sure you have a valid CrmConnection, making sure the Solution file location exists and is the correct type etc.  But when it’s said and done, there’s really one line of code that’s needed to make this happen.

This alone will suffice to deploy  your solution however there’s a step or two you may also want to add in. Once the solution is imported, you’ll frequently need/want to Publish your changes.  In MSCRM, you have the option of Deploying a small subset of changes, or doing a Deploy All. I think in this scenario, you’d almost always want to perform a publish all operation.  So at the end of each of the loops that deploys the solution, you can use a PublishAllXmlRequest and it’s brother, the PublishAllXmlResponse to make sure the changes for each solution are all published.  Doing this is just as trivial as deploying the solution:

PublishAllXmlRequest PublishRequest = new PublishAllXmlRequest();
PublishAllXmlResponse PublishResponse = ServiceInstance.Execute(PublishRequest) as PublishAllXmlResponse;
Console.WriteLine("Successfully Published Solution Components.");

At this point, each configured solution will be deployed and published provided everything went right. Missing Solution Dependencies, inadequate permissions and/or invalid credentials are the most common things that can cause problems, so make sure you handle exceptions for each of the Execute calls

 

————————————————————–

KeyWords: Dynamics4, Dynamics4.com,  CrmConnectionMicrosoft.Xrm.Client.CrmConnection, ImportSolutionRequest, ImportSolutionResponse, PublishAllXmlRequest, PublishAllXmlResponse, CrmConnection Class,  MSCRM 2011,Microsoft Dynamics CRM 2011, Crm 2011,Dynamics CRM 2011,   William Ryan , Bill Ryan,  CrmConnection.Parse,  Crm 2011 CrmConnection, OrganizationService, OrganizationServiceContext, , Microsoft.Xrm.Client.dll, Microsoft.Xrm.Sdk.dll

The post ImportSolutionRequest + PublishAllXmlRequest – Automating your MSCRM 2011 Solution Deployments appeared first on dynamics four.

]]>
http://dynamics4.com/importsolutionrequest-publishallxmlrequest-automating-your-mscrm-2011-solution-deployments/feed/ 2