Wednesday, January 16, 2013

Custom CRM user admin role unable to assign roles to other users in MS CRM 2011


Recently our project was migrated from MS CRM 4.0 to MS CRM 2011. The migration went quite smooth without much issue. We are using the backward compatible web services provided by the 2011 system in our plugins. So we didn't have to change much in the plugin area.

One of the functionality in our system is that there is a separate team who deals with creation of users and then assigning roles and teams to them. We cannot assign System Administrator role to this users as they will be able to see the entire system and can perform the CRUD operation on any records they want. So in CRM 4.0, we just defined the required privileges in the business management tab for the role to create a user, assign roles and assign teams. So the users cannot see the data in the system other than the users.

Once we migrated to MS CRM 2011, we found that this is not working as the way it was working in MS CRM 4.0. The system was throwing an error message while trying to assign a role to the user. While searching on Google regarding this issue, I came across the below blog:


In CRM 2011 for security reasons a user must have same or higher privileges than the role they are assigning to other user. This is the reason why the user admin role is not able to assign roles to the user.

Microsoft has released a fix in UR 10. By applying this fix, we can make this work but with few restrictions.
·     The users should have a role equivalent to System Administrator ( Role Copied from System Administrator).
·     The User access mode and license type should be administrative so that the user can only see the administrative section and not the data. This means a full user will not be able to manage the user or the other way, the administrative user will not able to work on data records.


But in our case we were not able to go with this solution as there were some users who have the user admin role and other data entry roles in the system. So those users cannot be of administrative license type.

Currently we tried an unsupported implementation by modifying 2 files: dlg_role.aspx and dlg_removeroles.aspx. In the window.onload of dlg_role.aspx, we captured the roles to add/remove in 2 separate arrays. Then we retrieved the system user id of the user having System Administrator CRM role. Using the system administrator user context, we added the roles in the addroles array and removed roles in the removeroles array to/from the CRM user using AssociateEntityrequest and DisAssociateEntityRequest XmlHttp calls.

//Getting the Admin User Id
var AdminUserResponse = RetrieveAdminUserId();
if(AdminUserResponse!=null && AdminUserResponse.text!=null)
adminUserId = AdminUserResponse.text;

//Xmlhttp Associate Request
function AssociateEntities(relationshipSchemaName, entity1Name, entity1Id, entity2Name, entity2Id) {
var xmlReq = "<?xml version=\"1.0\" encoding=\"utf-8\"?>"
+ "<soap:Envelope xmlns:soap=\"http://schemas.xmlsoap.org/soap/envelope/\""
+ " xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" xmlns:xsd=\"http://www.w3.org/2001/XMLSchema\">"
+ "<soap:Header><CrmAuthenticationToken xmlns=\"http://schemas.microsoft.com/crm/2007/WebServices\"><AuthenticationType xmlns=\"http://schemas.microsoft.com/crm/2007/CoreTypes\">0</AuthenticationType><OrganizationName xmlns=\"http://schemas.microsoft.com/crm/2007/CoreTypes\">" + Xrm.Page.context.getOrgUniqueName() +"</OrganizationName><CallerId xmlns=\"http://schemas.microsoft.com/crm/2007/CoreTypes\">" + adminUserId + "</CallerId></CrmAuthenticationToken></soap:Header>"
+ " <soap:Body>"
+ "<Execute xmlns=\"http://schemas.microsoft.com/crm/2007/WebServices\">"
+ "<Request xsi:type=\"AssociateEntitiesRequest\">"
+ "<Moniker1>"
+ "<Name xmlns=\"http://schemas.microsoft.com/crm/2006/CoreTypes\">" + entity1Name + "</Name>"
+ "<Id xmlns=\"http://schemas.microsoft.com/crm/2006/CoreTypes\">" + entity1Id + "</Id>"
+ "</Moniker1>"
+ "<Moniker2>"
+ "<Name xmlns=\"http://schemas.microsoft.com/crm/2006/CoreTypes\">" + entity2Name + "</Name>"
+ "<Id xmlns=\"http://schemas.microsoft.com/crm/2006/CoreTypes\">" + entity2Id + "</Id>"
+ "</Moniker2>"
+ "<RelationshipName>" + relationshipSchemaName + "</RelationshipName>"
+ "</Request>"
+ "</Execute>"
+ "</soap:Body>"
+ "</soap:Envelope>";
ExecuteSOAP("/MSCRMServices/2007/CrmService.asmx", xmlReq, http://schemas.microsoft.com/crm/2007/WebServices/Execute);
}

//Xmlhttp DisAssociate Request
function DisAssociateEntities(relationshipSchemaName, entity1Name, entity1Id, entity2Name, entity2Id) {
var xmlReq = "<?xml version=\"1.0\" encoding=\"utf-8\"?>"
+ "<soap:Envelope xmlns:soap=\"http://schemas.xmlsoap.org/soap/envelope/\""
+ " xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" xmlns:xsd=\"http://www.w3.org/2001/XMLSchema\">"
+ "<soap:Header><CrmAuthenticationToken xmlns=\"http://schemas.microsoft.com/crm/2007/WebServices\"><AuthenticationType xmlns=\"http://schemas.microsoft.com/crm/2007/CoreTypes\">0</AuthenticationType><OrganizationName xmlns=\"http://schemas.microsoft.com/crm/2007/CoreTypes\">" + Xrm.Page.context.getOrgUniqueName() +"</OrganizationName><CallerId xmlns=\"http://schemas.microsoft.com/crm/2007/CoreTypes\">" + adminUserId + "</CallerId></CrmAuthenticationToken></soap:Header>"
+ " <soap:Body>"
+ "<Execute xmlns=\"http://schemas.microsoft.com/crm/2007/WebServices\">"
+ "<Request xsi:type=\"DisassociateEntitiesRequest\">"
+ "<Moniker1>"
+ "<Name xmlns=\"http://schemas.microsoft.com/crm/2006/CoreTypes\">" + entity1Name + "</Name>"
+ "<Id xmlns=\"http://schemas.microsoft.com/crm/2006/CoreTypes\">" + entity1Id + "</Id>"
+ "</Moniker1>"
+ "<Moniker2>"
+ "<Name xmlns=\"http://schemas.microsoft.com/crm/2006/CoreTypes\">" + entity2Name + "</Name>"
+ "<Id xmlns=\"http://schemas.microsoft.com/crm/2006/CoreTypes\">" + entity2Id + "</Id>"
+ "</Moniker2>"
+ "<RelationshipName>" + relationshipSchemaName + "</RelationshipName>"
+ "</Request>"
+ "</Execute>"
+ "</soap:Body>"
+ "</soap:Envelope>";
ExecuteSOAP("/MSCRMServices/2007/CrmService.asmx", xmlReq, http://schemas.microsoft.com/crm/2007/WebServices/Execute);
}

//Looping through the roles to Add
for (var i=0; i<addRoleCount; i++)
{
AssociateEntities("systemuserroles_association", "systemuser", _a[0], "role", rolesAdded[i]);
}

//Looping through the roles to remove.
for (var i=0; i<removeRoleCount; i++)
{
DisAssociateEntities("systemuserroles_association", "systemuser", _a[0], "role", rolesRemoved[i]);
}

As this is an unsupported way of doing, Please suggest us if anybody have any suggestions doing it in a supported way. Thank you so much for reading my blog and hearty welcome for all your suggestions and comments.

Friday, January 11, 2013

Standard Way of Writing an Event in C#


In .Net there is a standard pattern to write an event. System.EventArgs is the main player in this pattern. This acts as a base class for conveying message through an event.

EventArgs is a pre-defined class in the .Net Framework with no members other than EventArgs.Empty static member. So let’s see how to create an event in a standard way using the EventArgs.

First of all we need to create a class which is subclassing the EventArgs class. Consider the temperature example in my previous post Delegates and Events in C# :

public class TemperatureChangedEventArgs :EventArgs
{
   public readonly double OldTemperature;
   public readonly double NewTemperature;

   public TemperatureChangedEventArgs(double oldTemp, double newTemp)
  {
     OldTemperature = oldTemp;
     NewTemperature = newTemp;
  }

}

Typically the EventArgs class exposes the data as either a read only field or as a property. Define what all information required to pass along with the event as a property or read only field. In this example, whenever the temperature change event occurs, along with the event, we need to pass the old temperature and new temperature. So those data are stored as read only fields in the EventArgs subclass. The constructor of the class should accept the required information and then assign it to the EventArgs class members.

Now we have our EventArgs subclass in place. Now we need to define a delegate for the event. The delegate should meet the following rules:

1. The return type should be void
2. The name of the delegate should end with EventHandler
3. The event should accept 2 arguments - one is the instance of the broadcaster and second one is the instance of the EventArgs subclass.

So we need to create a delegate like:

public delegate void TemperatureChangedEventhandler(object sender,
TemperatureChangedEventArgs e);

In .Net Framework there is a generic delegate which matches this criteria EventHandler<>.

So let’s now rewrite our TemperatureBroadcaster class from the previous post Delegates and Events in C# :

Three things to take care while writing the class
1. Create an Event using the EventHandler<>.
      public event EventHandler<TemperatureChangedEventArgs> TemperatureChanged; 

2.  Create a protected virtual method which invokes the event. The name of the method should be like appending an On with the event instance name.

    protected virtual void OnTemperatureChanged(TemperatureChangedEventArgs e)
    {
      if (TemperatureChanged != null)
      {
        TemperatureChanged(this, e);
      }
    }

3. Call the protected virtual method from the set accesser of the changing property, Temperature property in our case.
   
     OnTemperatureChanged(new TemperatureChangedEventArgs(oldTemperature, temperature));

So this is how our TemperatureBroadcaster class looks like:

public class TemperatureBroadcaster
{

  //EventHandler<> is a generic delegate which satisfies the rules mentioned earlier.
  public event EventHandler<TemperatureChangedEventArgs> TemperatureChanged;
  double temperature;

  public TemperatureBroadcaster()
  {
  }

  // The protected virtual void method which invokes the event.
  protected virtual void OnTemperatureChanged(TemperatureChangedEventArgs e)
  {
    if (TemperatureChanged != null)
    {
      TemperatureChanged(this, e);
    }
  }

  public double Temperature
  {
    get
    {
      return temperature;
    }
    set
    {
      if (temperature == value)
      return;
      double oldTemperature = temperature;
      temperature = value;
      //calling the method to invoke the event when the temperature is changed.
      OnTemperatureChanged(new TemperatureChangedEventArgs(oldTemperature, temperature));
    }
  }

}

If in your case you don’t need to pass any information along with the event, then you can use the EventArgs.Empty as the parameter. This will avoid unnecessary instance creation of the EventArgs Subclass

 //Use the pre-defined EventHandler
 public event EventHandler TemperatureChanged;

//change the protected method to accept EventArgs as parameter
protected virtual void OnTemperatureChanged(EventArgs e)
{
  if (TemperatureChanged != null)
  {
    TemperatureChanged(this, e);
  }
}

// from the set accessor of the changing property, pass EventArgs.Empty
OnTemperatureChanged(EventArgs.Empty);


I hope this post was helpful for you. Thanks for reading by blog.

                                                                                                               --Ratheesh Rahulan


Wednesday, January 9, 2013

Delegates and Events in C#

Delegates and Events in C#

Delegates

Delegate is an object which can be used to call any method with the same method declaration. Specifically it defines the method's parameter types and return types.

Below is an example which defines a delegate type
delegate int Converter(int val);

Converter  is compatible with any method which takes an int value as parameter and returns an int value.
static int ConvertCMtoMM(int val) { return val * 10; }

Onces you assign the delegate variable with a method name as value, a delegate instance is created
Converter con = ConvertCMtoMM;

You can invoke this method like :
int convertedValue = con(100);

So this is a way of decoupling the caller from the actual method. The caller calls the delegate and the delegate calls the method.

Multicast Delegates

 A delegate instance can be targeted to multiple methods which is called multicast delegate. Considering the above delegate, we can add more references to the delegate by using += and we can remove references using -=.

        static void Main(string[] args)
        {
            Converter con = ConvertCMtoMM;
            con+= ConvertMetertoCM;
            con+=ConvertKMtoMeter;
            con -= ConvertCMtoMM;
            int value = con(1000);
        
        }

        static int ConvertCMtoMM(int val) { return val * 10; }
        static int ConvertMetertoCM(int val) { return val * 100; }
        static int ConvertKMtoMeter(int val) { return val * 1000; }

Invoking con will now call all the 3 methods one after other in the order in which they are added to the delegate. If the return type for the multicast delegate is not a void, then the delegate will return only the value from the last method being called by the delegate. In most cases, multicast delegates will be used with methods having void return types.

Events

In the delegate scenario, there are basically 2 types of participants.
1. Broadcaster
2. Subscriber

Broadcaster will be the owner of the delegate field and he decides when to do the broadcast.
Subscribers are the audience for the broadcast. They can decide when they want to watch the broadcast or when they don't want to watch using the += and -= on the broadcaster's delegate field.

Events can be considered as a pattern which are made for the broadcasting subscription policy. So the question is we are able to do it with the delegates, then why do we need an event.

The main purpose of the event is to prevent the subscribers from interfering in the broadcast. Their job is to only listen or if they don't want, then unsubscribe and go.

If a delegate is used in place of an event for a broadcasting, the following things can happen:
1. Clear all subscribers by setting the delegate instance to null.
con = null // this will clear all the subscriptions.
2. Do the broadcast by invoking the delegate.
con(1000);
3. Replace other subscribers by reassigning by using = instead of +=.
con = ConvertCMtoMM;

Now lets see how to create an event. It is very simple if you know how to create a delegate. Just add event keyword before the delegate name.

    //delegate
    public delegate void TemperatureChangedHandler(double oldTemperature, double newTemperature); 
    public class TemperatureBroadcast
    {
        // this class might get the temperature reading from a device output which sets the temperature value;
        public TemperatureBroadcast()
        {
            temperature = 28.00;
        }

        private double temperature;
        public event TemperatureChangedHandler TemperatureChanged;  //event
        public double Temperature {
            get
            {
                return temperature;
            }
            set
            {
                if (value == temperature)
                    return;
                double oldTemperature = temperature;
                temperature = value;
                if (TemperatureChanged != null) //check if there are subscribers listening for the temperature  
                                                                 //variations.
                {
                    TemperatureChanged(oldTemperature, temperature);
                }
            }
        }
    }


static void Main(string[] args)
        {
            TemperatureBroadcast broadcast = new TemperatureBroadcast();
            broadcast.TemperatureChanged  +=new TemperatureChangedHandler(Broadcast_TemperatureChanged); //subscribing to temperature broadcast.
        
        }

//Anytime the temperature is changed, this subscriber is notified as long as he is subscribed.
        static void Broadcast_TemperatureChanged(double oldTemperature, double newTemparature)
        {
            Console.WriteLine("The temparature has changed from {0} to {1}", oldTemperature, newTemparature);
        }

Now to try out the issues which can happen with a delegate in case of broadcast scenario instead of an event, remove the event keyword from the delegate instance and try calling the delegate from the main method 

TemperatureChanged(100, 200);

All other subscribers will get this temperature information broadcast by a subscriber.
Even you will be able to assign TemperatureChanged = null which will remove all the subscribers.
Also you can replace other subscribers by reassigning the method to the delegate TemperatureChanged = broadcast_TemperatureChanged;

Thanks everybody for reading my blog...
Ratheesh Rahulan