Nilesh Baviskar

  Home  |   Contact  |   Syndication    |   Login
  3 Posts | 1 Stories | 6 Comments | 0 Trackbacks

News

Archives

Thursday, August 25, 2011 #

So far I found that MEF is going well with presentation layer with following benefits.
1.     DI (Dependency Injection)
2.     Third party extensibility (Note that all parties involved should use MEF or need wrappers)
3.     Auto discovery of Parts (Extensions)
4.     MEF allows tagging extensions with additional metadata which facilitates rich querying and filtering
5.     Can be used to resolve Versioning issues together with “DLR and c# dynamic references” or “type embedding”
After bit of research I'm concluding following
1.     MEF for the Core Services - cost of changes are not justifying the benefits. Also this is big decision and may affect the service layer in good or bad way so needs lot of study. MEF V2 (Waiting for stable version) might be better in this case but little worried about using MEF V1 here.
2.     MEF for the Function service performs - MEF might add the value but it’s very specific to the service function. We need to go deep into requirement of service to take that decision.
Your views, thoughts and suggestions are appreciated.

Here is the screenshot of final output.

OutPut

Here is what PoC is doing
1.       There is Silverlight application hosted in CRM on custom area-subarea. This in-turn needs two web resources
2.       There is Custom button added in Ribbon
3.       Third web resource is hosting JavaScript function
4.       On click of Custom Button on CRM Ribbon function in JavaScript web resource is called which in-turn calls the method in C# code of Silverlight application. String input is passed to this method
5.       C# method is converting the input string to upper case and returning it.
6.       Finally alert is displayed with upper case string.
Here are the details to create the PoC
1.       Created new solution in CRM
2.       Created new Area and Sub-Area for this PoC by editing Site Map XML. Here is the snapshot of XML changes.
<AreaId="RibbonPoCArea"
            Title="Custom Area For RibbonPoC"
            ShowGroups="false"
            Icon="/_imgs/ico_18_9600.png">
        <GroupId="RibbonPoCGroup">
          <SubAreaId="RibbonPoCSubArea"
                   Title="Custom Sub-Area For RibbonPoC"
                   Url="$webresource:new_/RibbonPocTestPage.html" />
        </Group>
      </Area>
 
3.       Added custom button in Application Ribbon. Here is the updated XML for Ribbon
<RibbonDiffXml>
    <CustomActions>
      <CustomActionId="RibbonPoc.BasicHomeTab.Tools.CustomButton.CustomAction"
                    Location="Mscrm.BasicHomeTab.Tools.Controls._children"                     
                    Sequence="101">
        <CommandUIDefinition>
          <ButtonId="RibbonPoc.BasicHomeTab.Tools.CustomButton"
                  Command="RibbonPoc.BasicHomeTab.Tools.CustomButton.Command"
                  Sequence="101"
                  LabelText="$LocLabels:RibbonPoc.Form.CustomButton.LabelText"
                  ToolTipTitle="$LocLabels:RibbonPoc.Form.CustomButton.LabelText"
                  ToolTipDescription="$LocLabels:RibbonPoc.Form.CustomButton.ToolTip"
                  TemplateAlias="isv"Image16by16="/_imgs/ico_18_129.gif"
                  Image32by32="/_imgs/resourcecenter_24x24.gif" />
        </CommandUIDefinition>
      </CustomAction>
    </CustomActions>
    <Templates>
      <RibbonTemplatesId="Mscrm.Templates"></RibbonTemplates>
    </Templates>
    <CommandDefinitions>
      <CommandDefinitionId="RibbonPoc.BasicHomeTab.Tools.CustomButton.Command">
        <EnableRules />
        <DisplayRules />
        <Actions>
          <JavaScriptFunctionFunctionName="CallSilverlightMethod"
                              Library="$webresource:new_RibbonTestJavaScript" />
        </Actions>
      </CommandDefinition>
    </CommandDefinitions>
    <RuleDefinitions>
      <TabDisplayRules />
      <DisplayRules />
      <EnableRules />
    </RuleDefinitions>
    <LocLabels>
      <LocLabelId="RibbonPoc.Form.CustomButton.ToolTip">
        <Titles>
          <Titlelanguagecode="1033"description="Custom Button." />
        </Titles>
      </LocLabel>
      <LocLabelId="RibbonPoc.Form.CustomButton.LabelText">
        <Titles>
          <Titlelanguagecode="1033"description="Custom Button" />
        </Titles>
      </LocLabel>
    </LocLabels>
 </RibbonDiffXml>
 
4.       Created Silverlight Application. You can look at my Article Hosting Silverlight 4.0 application in Dynamics CRM 2011
Here is important C# code. Note System.Windows.Browser.HtmlPage.RegisterScriptableObject("SilverlightCode", this); and [ScriptableMember]
public partial class MainPage : UserControl
    {
        public MainPage()
        {
            InitializeComponent();
            System.Windows.Browser.HtmlPage.RegisterScriptableObject("SilverlightCode", this);                               
        }
 
        // After the Frame navigates, ensure the HyperlinkButton representing the current page is selected
        private void ContentFrame_Navigated(object sender, NavigationEventArgs e)
        {
            foreach (UIElement child in LinksStackPanel.Children)
            {
                HyperlinkButton hb = child as HyperlinkButton;
                if (hb != null && hb.NavigateUri != null)
                {
                    if (hb.NavigateUri.ToString().Equals(e.Uri.ToString()))
                    {
                        VisualStateManager.GoToState(hb, "ActiveLink", true);
                    }
                    else
                    {
                        VisualStateManager.GoToState(hb, "InactiveLink", true);
                    }
                }
            }
        }
 
        // If an error occurs during navigation, show an error window
        private void ContentFrame_NavigationFailed(object sender, NavigationFailedEventArgs e)
        {
            e.Handled = true;
            ChildWindow errorWin = new ErrorWindow(e.Uri);
            errorWin.Show();
        }
 
        private void button1_Click(object sender, RoutedEventArgs e)
        {
            MessageBox.Show(CustomMethod("Silverlight Button Clicked !"));
        }
 
        //This method will be called from JavaScript on click of Ribbon Button
        //This method needs to be Public
        [ScriptableMember]
        public string CustomMethod(string message = "")
        {
            //MessageBox.Show(message, "Message", MessageBoxButton.OK);
            return message.ToUpper();
        }
    }
 
SLCode
 
Here is important HTML code. Note the <object id="SLFromJS"…
<body>
    <form id="form1" runat="server" style="height:100%">
    <div id="silverlightControlHost">
        <object id="SLFromJS" data="data:application/x-silverlight-2," type="application/x-silverlight-2" width="100%" height="100%">
 <param name="source" value="ClientBin/RibbonPoC.xap"/>
 <param name="onError" value="onSilverlightError" />
 <param name="background" value="white" />
 <param name="minRuntimeVersion" value="4.0.50401.0" />
 <param name="autoUpgrade" value="true" />
 <a href="http://go.microsoft.com/fwlink/?LinkID=149156&v=4.0.50401.0" style="text-decoration:none">
   <img src="http://go.microsoft.com/fwlink/?LinkId=161376" alt="Get Microsoft Silverlight" style="border-style:none"/>
 </a>
    </object><iframe id="_sl_historyFrame" style="visibility:hidden;height:0px;width:0px;border:0px"></iframe></div>
    </form>
</body>
 
5.       Hosted Silverlight Application in CRM. For this we need to create two web resources – one to host HTML and second for XAP.
6.       Created one more web resource to host JavaScript function. Developer tools (F12) in IE 8 helped me to find exact location of my Silverlight Object (SLFromJS) in HTML DOM. Here is the JavaScript – Note window.frames['contentIFrame'].document.forms['form1'].SLFromJS;
function CallSilverlightMethod(sender) {
    alert('Inside JS1!');
    var slc = window.frames['contentIFrame'].document.forms['form1'].SLFromJS;
    alert('Inside JS2!');
    if (slc != null) {
        alert('Inside if!');
        alert(slc.Content.SilverlightCode.CustomMethod('Msg From JavaScript'));
        alert('Going out of if!');
    }
    alert('Out of if!');
}
 
My CRM solution looks like following now
CRMSolu
7.       Done! Now test the work by opening link of HTML web resource.
Thanks to following blog posts which I referred.
http://www.a2zmenu.com/Blogs/Silverlight/Calling-Silverlight-Method-from-JavaScript.aspx
http://stackoverflow.com/questions/478078/accessing-a-form-that-is-in-an-iframe
 

 


Wednesday, August 24, 2011 #

Requirement was to create Multi-tenant WCF Service for custom development using WCF and Dynamics CRM 2011. There is just one SVC file but multiple URLs to access it e.g. in Dyanamics Organization Org1 have URL http://CRMserver_name/Org1/XRMServices/2011/Organization.svc and Org2 have http://CRMserver_name/Org2/XRMServices/2011/Organization.svc

I was trying this using URL Routing but problem is, it is creating REST services which we don’t want. We should be able to access these services just like normal WCF service. So if we add the URL http://CRMserver_name/Org1/XRMServices/2011/Organization.svc in WCF test client it should work.

After some research I’m going with safe way to achieve Multi-tenant WCF Services. I’m going to create separate WCF projects for each tenant and host them in separate IIS virtual dirs.

e.g. For Tenant1 - http://localhost/ Tenant1/Service1.svc and for Tenant2 - http://localhost/ Tenant2/Service1.svc
Note that SVC name is same. Then I’m removing IService1.cs from Tenant2 and adding existing file as link from Tenant1. So my solution explorer looks like following.

Solution Explorer

Here WcfService3 is for Tenant1 and WcfService1 is for Tenant2 (Sorry for this confusing names. I might fix it if someone need). So my WcfService3 properties look like following.

Project Prop

Now for both http://localhost/ Tenant1/Service1.svc and http://localhost/ Tenant2/Service1.svc service code is at single place which is in WcfServices3->Service1.svc.cs. Following code will create the database connection object for specific method call.

private static string dbServerName = "YourDBServerName";
        private static SqlConnection dbConnection = createDBConnection();
       
        private static SqlConnection createDBConnection()
        {
            Uri fullUri = OperationContext.Current.IncomingMessageHeaders.To;
            Uri baseAddress = new Uri(@"http://localhost");
            UriTemplate template = new UriTemplate(@"/{orgName}/Service1.svc");
 
            // retrieve the value of the artist segment
            UriTemplateMatch match = template.Match(baseAddress, fullUri);
            String orgName = match.BoundVariables["orgName"];       
 
            SqlConnection objConnection = new SqlConnection();
            objConnection.ConnectionString = createConnString(orgName);
 
            return objConnection;
        }
 
        private static string createConnString(string orgName)
        {
            return (String.Format("Data Source={0};Initial Catalog={1}_MSCRM;User Id=UserId1;Password=PasswordForUserId1;", dbServerName, orgName));
        }
 
Hope this helps someone.
-Nilesh