Welcome to MSDN Blogs Sign in | Join | Help

Speeding up your CCF Workflow

CCF Workflows takes several seconds to start for the first time. This is to do with the fundamental .Net feature called JIT. When you first launch the workflow, CCF creates the workflow engine for the first time and starts the engine before working on your request. This eats up so much of processor... remember the JIT compiler compiles the IL instructions only when required?

One workaround for this problem would be warm up your workflow before the user actually uses this. This will help in faster response time from CCF as the required last minute compilations are already done for you and all you will do is to use the cached binary instructions instead of IL.

As always, below is the sample code that does exactly what is mentioned in the above sentence:

Find the line in your desktop.cs file

customerWorkflowManager = (IWorkflowManager)this.GetHostedApp("Customer Workflow Manager");

add the following lines below that statement.

//Modified for warming up the workflow

ThreadStart ts = new ThreadStart(ExecuteAsyncWorkflow);
Thread wfThread = new Thread(ts);

wfThread.Start(data);

//end of modifications

Finally, add this method.

private void ExecuteAsyncWorkflow()
{
    System.Reflection.MethodInfo method = customerWorkflowManager.GetType().GetMethod(
"DoAction", new Type[] { typeof(Microsoft.Ccf.Csr.Action), typeof(string) }); Microsoft.Ccf.Csr.Action action = new Action(-100, "Human Workflow Automation", "");
//TODO: Fill the below ALL CAPS words with proper values or better yet, make a web service call to get a valid workflow xml.
// Refer to custom workflow manager project on how to use the web service call.
    string data = @"
<Workflow>
<StepName>NAME HERE</StepName>
<HostedApplicationId>ID FROM DATABASE HERE</HostedApplicationId>
<HostedApplicationName>VALID APPLICATION NAME HERE</HostedApplicationName>
<Action>VALID ACTION NAME HERE</Action>
</Workflow>"
;
    try
    {
        method.Invoke(customerWorkflowManager, new object[] { action, data });
    }
    catch (Exception) { }
}

This is fairly simple code could save you up to 20 seconds. The tradeoff with this approach (of course all good things have an asterix) you are adding more load during the start up, though the time your solution loads will not change as this is a background thread, but the CPU is now split with one additional thread.

Let me know if you have a better solution to this problem.

Posted by Sidhartha | 1 Comments
Filed under: ,

Adding CCF Agents Programmatically

One of the most annoying features for admins with managing CCF is adding Agents. Updating/Deleting are less annoying as they are not in bulk most of the times. Even manually adding Agents using CCF Admin Console is not productive when adding more than few users.

CCF Admin Console, internally uses WCF web services (in CCF 2008) to perform database related activities. You can write a custom program consuming these web services to ease your configuration. This is much faster way of handling the agents in the database.

Warning: You should not get carried away with this approach and communicate to the database directly bypassing the webservices or if there is no web service for a desired database operation. This might cause long term problem when the CCF team changes their database. In worst case, it might even void your support.

Now that we know what we should and shouldn't do, let me show some sample code to do the same.

Every installation of CCF 2008 (server) installs several web services and the one we are interested here is AgentWS. Since we know the web service now, it is a simple WCF webmethod invokation.

Following code illustrates how to add user to the database.

            AgentWSClient agentService = new AgentWSClient("BasicHttpBinding_AgentWS");

            try
            {
                AgentDetails agentDetail = new AgentDetails();
                agentDetail.FirstName = "John";
                agentDetail.LastName = "Smith";
                agentDetail.DomainId = "contoso\\jsmith";
                agentDetail.AgentType = 1; //1 - Agent, 2 - Admin

                agentService.Add(agentDetail);
            }
            finally
            {
                agentService.Close();
            }

As simple as this...

You can probably write a small application that will fetch the user information from the Active Directory or any other list of users and add them all into the CCF database.

Posted by Sidhartha | 2 Comments
Filed under: ,

Reason behind AdapterContextChange()'s failure

I know very few people are working in CCF currently. But here is an undocumented life saver.

The Context that is passed in the NotifyContextChange event is a reference to the object and not a value. Most of you might already know this as it is a reference type.
If you have a requirement to update the context based on some user activity and notify the context change to all the applications, you simply invoke the ContextChange or AdapterContextChange methods with appropriate values.

However, there is a catch here. You cannot use the same context reference and trigger/fire the change across all the applications.
Let us take a look at this sample code for clarification.

The following code will *NOT* work:

private Context _context;

public void CustomerUpdated(string firstName, string lastName)
{
    _context["CustomerFirstName"] = firstName;
    _context["CustomerLastName"] = lastName;
               
    this.AdapterContextChange(_context);

    base.DocumentComplete(url);
}

public override bool NotifyContextChange(Context context)
{
    _context = context;
    return base.NotifyContextChange(context);
}

This is because, when you invoke the context change method internally current context and the passed context are checked for their reference instead of the value. Since both of them share the same reference.

To rectify the problem, you should explicitly copy the context into a different object and update that.

This code will work:

private Context _context;

public void CustomerUpdated(string firstName, string lastName)
{
    _context["CustomerFirstName"] = firstName;
    _context["CustomerLastName"] = lastName;
               
    this.AdapterContextChange(_context);

    base.DocumentComplete(url);
}

public override bool NotifyContextChange(Context context)
{
    _context = new Context(context.ContextInformation);
    return base.NotifyContextChange(context);
}
I was not comfortable with this design decision at the beginning as we end up duplicating the context in every adapter we develop. 
But, after some thought, this seems to be chosen to gain performance - the tradeoff, however, was memory.
Posted by Sidhartha | 1 Comments
Filed under: ,

Method Overloading in WCF

WCF solves so many complex problems of the distributing computing, yet when it comes to simple things like overloading it fumbles. This is not what I say, but what I heard from some developers at my customer's site. So, I thought how can such a small thing could not be achievable in WCF when the underlying platform (.Net framework) support this by design.

After little thought, it is all clear why it does not work the same way it works with compiled languages. Its all the underlying protocols that makes it complex. But, it is not really tough to get this to work.

Here is the sample I worked on to get method overloading with WCF working like it does in C#.

Step 1: I've created a simple service contract with two methods using method overloading.

[ServiceContract()]
public interface IMathService
{
    [OperationContract]
    int AddNumbers(int num1, int num2);
    [OperationContract]
    double AddNumbers(double num1, double num2);
}

Step 2: Tried to run the service and this is what I WCF told me...

image

Step 3: As WCF does not allow me to do this in a direct way, I looked at the OperationContract attribute to see if there is something that can help me. To my confidence, there is this property called Name. I've set this value to a unique value for both of these methods. now, the service contract looks like this:

[ServiceContract()]
public interface IMathService
{
    [OperationContract(Name="AddIntegers")]
    int AddNumbers(int num1, int num2);
    [OperationContract(Name="AddDoubles")]
    double AddNumbers(double num1, double num2);
}

Step 4: Tried to run the service and this is what WCF told me now...

image

The problem seemed to be solved. And indeed it is, the generated proxy does not show any discrimination towards these methods. They'll be just like any other method (overloaded methods). Now its all over... Method overloading in WCF is very much possible and is very much simple.

I don't think I need to attach the source code as the only required change is already discussed here.

Note: If you are using WCF always think about messages and not methods. So, this situation should not end up in your design docs. If at all you end up here, you can use the approach suggested in this blog. Thanks Michael for pointing out this.

Posted by Sidhartha | 7 Comments
Filed under: ,

Getting away with client Config in WCF

Introduction

This weblog provides a solution to one of the most common problem faced by developers while developing their projects involving WCF Services using Visual Studio.

Background

To give you a little background on what this is all about… when you are working with WCF services and using Visual Studio for development, it becomes annoying as to how Visual Studio screws up the configuration settings. This is a small problem with an even smaller solution. Hence, it might be the smallest article.

Using the code

There are two attachments with this weblog.

  1. NoConfigWS.zip is the web service. You've to unzip the contents and create a web application using IIS. I'll not go into the details on doing that, you can figure out on your own if you are not familiar by searching the www.
  2. NoConfigClient.zip is the client that consumes the NoConfigWS web service. This is a console application that communicates with the service and displays the results back. All you need to do is to unzip the contents and change the app.config file to point your service.

Problem

This sample will help you in understanding the situation better.

Before updating (add service reference and changing some values):

        <binding 
          name="BasicHttpBinding_IHelloWorldService" 
          closeTimeout="00:01:00"
          openTimeout="00:01:00" 
          receiveTimeout="00:10:00" 
          sendTimeout="00:01:00"
          allowCookies="false" 
          bypassProxyOnLocal="false" 
          hostNameComparisonMode="StrongWildcard"
          maxBufferSize="131072" 
          maxBufferPoolSize="524288" 
          maxReceivedMessageSize="131072"
          messageEncoding="Text" 
          textEncoding="utf-8" 
          transferMode="Buffered"
          useDefaultWebProxy="true">
          <readerQuotas 
            maxDepth="32" 
            maxStringContentLength="8192" 
            maxArrayLength="16384"
            maxBytesPerRead="4096" 
            maxNameTableCharCount="16384" />
          <security mode="None">
            <transport 
              clientCredentialType="None" 
              proxyCredentialType="None"
              realm="" />
            <message 
              clientCredentialType="UserName" 
              algorithmSuite="Default" />
          </security>
        </binding>

After changes (and updating service reference):

        <binding 
          name="BasicHttpBinding_IHelloWorldService" 
          closeTimeout="00:01:00"
          openTimeout="00:01:00" 
          receiveTimeout="00:10:00" 
          sendTimeout="00:01:00"
          allowCookies="false" 
          bypassProxyOnLocal="false" 
          hostNameComparisonMode="StrongWildcard"
          maxBufferSize="65536" 
          maxBufferPoolSize="524288" 
          maxReceivedMessageSize="65536"
          messageEncoding="Text" 
          textEncoding="utf-8" 
          transferMode="Buffered"
          useDefaultWebProxy="true">
          <readerQuotas 
            maxDepth="32" 
            maxStringContentLength="8192" 
            maxArrayLength="16384"
            maxBytesPerRead="4096" 
            maxNameTableCharCount="16384" />
          <security mode="None">
            <transport 
              clientCredentialType="None" 
              proxyCredentialType="None"
              realm="" />
            <message 
              clientCredentialType="UserName" 
              algorithmSuite="Default" />
          </security>
        </binding>

This becomes really painful if you had to change some settings (for example maxReceivedMessageSize or maxStringContentLength) to a different value than the default for some of the services. Every time you update the service reference, Visual Studio will replace the updated settings with the defaults. For situations where multiple developers are developing the services, this problem only grows exponentially.

Solution

There are several solutions for this problem, and the one presented here is just one of them.

Whatever is your development environment to create your services, whether using Visual Studio or directly generate it, SVCUtil will generate multiple constructors for the proxy class (five to be exact). Guess what, one of them takes a Binding and EndpointAddress as parameters.

        public HelloWorldServiceClient()
        {
        }
        
        public HelloWorldServiceClient(string endpointConfigurationName) : 
                base(endpointConfigurationName)
        {
        }
        
        public HelloWorldServiceClient(string endpointConfigurationName, string remoteAddress) : 
                base(endpointConfigurationName, remoteAddress)
        {
        }
        
        public HelloWorldServiceClient(string endpointConfigurationName, EndpointAddress remoteAddress) : 
                base(endpointConfigurationName, remoteAddress)
        {
        }
        
        public HelloWorldServiceClient(Binding binding, EndpointAddress remoteAddress) : 
                base(binding, remoteAddress)
        {
        }
        
Now, our job is to create these objects and create the proxy by passing these objects. 
            HelloWorldServiceClient helloWorldClient = new HelloWorldServiceClient(
                ProxyHelper.GetBinding(),
                ProxyHelper.GetEndpoint() );

GetBinding() and GetEndpoint() are helper methods I’ve used to create those objects. The complete source code for this article is available as a download.

With this approach, your final Config file would be as clean as follows:

<configuration>
  <appSettings>
    <add key="webserviceURL" value="http://localhost/NoConfigWS/Service.svc"/>
  </appSettings>
</configuration>

Summary

This, IMO, is much cleaner for your end user to maintain and by the way, who better knows the advanced Config settings than the developer (and ofcourse, the advanced network administrators)

Once done, you can rest in peace… I mean work on your services without ever worrying about Visual Studio playing with your Config file anymore.

Posted by Sidhartha | 1 Comments
Filed under: ,

Attachment(s): Downloads.zip

WSDL for WCF Service

This can be tricky at times.

If all you want is the basic communication WSDL, you can get it by appending ?WSDL to the .svc file. This will output the required WSDL. A sample for this would be something like: http://localhost/MyService/Service.svc?WSDL

But sometimes you want to see everything that is produced. Especially, if you are used to ASMX way of development, you'd want to see the complete WSDL instead of the communicaiton portion. Here is where WCF strikes.

There is not just one, but several url's you need to browse before getting the required stuff.

First check in the content if you have any additional WSDL, or you can simply do a brute-force method.

http://localhost/MyService/Service.svc?WSDL=WSDL0 will give you additional information. There might be times (depending on your service) you should be looking for WSDL1. No point playing around as this is what the most you can go through.

Now, all you have is the complete WSDL, though in different files. If you look into the content, you still miss the type information (if you are using any custom data contracts). This is where your probing power will help you.

Look into the WSDL0 file to check for the XSD links. If you are lazy, again retort to the brute-force.

http://localhost/MyServoce/Service.svc?XSD=XSD0 will give you first level information of types. Depending on your case, you might want to look for XSD1 and XSD2 to get the complete information. Again, no point in playing around as this the max you can get.

When are done with this, you've all the information requried.

Posted by Sidhartha | 4 Comments
Filed under: ,

No More DLL Hell - The Song

Spike Xavier & Dan Wahlin came up with this song while carpooling to their work... 
Is starts like a rock song, then loses track. They have however tried to keep onto the same genre, but...

They realized that they created a new genre of music and promply gave it the name Heavy .Netal

Check this link for the song

Posted by Sidhartha | 1 Comments

Google... I'm really feeling lucky today :)

Long long ago... when people tried "Failure" in the search box and tried their luck (I'm Feeling lucky), they were directed to President Bush related website... How obvious!!!
And they say... history repeats... and here is another proof...

Try "Search" in the searchbox and try your luck. I'm sure you'll Live with the result.

It seems Google... is now feeling the heat... Long LIVE.
Not sure how many days this will survive... So guys try this... fast

Posted by Sidhartha | 5 Comments

Using Windows Form with InfoPath 2003

InfoPath 2003 does not provide any password field. So, storing passwords on the form is not recommended as it is in clear text.

To have something really critical as passwords on the InfoPath forms is not a straightforward task.
I've used a workaround for this.

I'll showup a Windows Form and capture the credentials on that form, encrypt the credentials there and save that encrypted information back on the InfoPath form. This way, the user will never get to read the password in clear text.

Attached is the sample InfoPath form which opens a Windows Form to capture the credentials. I've removed all the encryption code from that to keep it simple and straight forward. Please let me know if there are any better ways to do this or you want more information about this...

Posted by Sidhartha | (Comments Off)
Filed under:

Attachment(s): WinForms.zip

Using user roles with InfoPath 2003

Lab 7 of the InfoPath labs discuss about the using user roles with InfoPath.

This is one of the most useful features in InfoPath. You can do anything with this based on the role. For example, the admin user can be shown some admin views which are not visible for a normal user.

We can even get this information in the attached code by using the following api:

bool userRole = thisApplication.User.IsUserMemberOf(@"Domain\Group");

if (userRole)
{
   //do something cool here
}

However, there is a catch here. This works only if the user who is accessing the form and the group specified in the user roles are of the same domain.

So, the following are the valid and invalid combinations

Invalid Combination:

User: AMERICA\Employees
Group: ASIA\SecurityGroup

Valid Combination:

User: AMERICA\Employees
Group: AMERICA\SecurityGroup

Posted by Sidhartha | 0 Comments
Filed under:

70-528 Conquered!!!

On Friday (1st Sept 2006) I wrote 70-528 (developing web client applications in .net framework 2.0).

The material available on the test was pretty little and the topics to cover were many. Though, I was very sure about the result... I myself was shocked when the machine displayed... "Congratulations you have passed the exam".

I thought that was a dream, but turned out to be a reality. Anyways, the result is encouraging.

This is not a milestone achievement, but when the expectations are none, any positive result is a big achievement.

Regards,
IamJunk.

Posted by Sidhartha | 11 Comments
Filed under: , ,

Dynamically Hide/Disable controls in InfoPath 2003

Working on InfoPath form is an easy affair as long as, developers use it for the sole purpose of its creation (Creating a simple forms to capture data and publish them to a server for sharing). The moment this rule is broken, the increase in the complexity will be directly proportional to the features being implemented.

There are situations where you may need to perform some action based on the currently logged in User, the role of the user or some other parameter.

Recently, I was working on something which required us to do some UI validations based on the user logged in. We cannot directly use the user roles provided as the user names will be stored in a database and we need to set the UI according to the user preferences.

This is not a straight forward task in InfoPath (though, I dont want to call it tedious aswell) as InfoPath does not work that way.

So, we had to work on several work arounds and found a very simple alternative.

In the attached sample, you can type in the any username in the user name textbox and the dropdown will be disabled as soon as the username is identified as the current windows username.

Pretty simple code... just look at how simple InfoPath makes things.

Posted by Sidhartha | 1 Comments
Filed under:

Attachment(s): DynamicallyHide.zip

Open and copy contents from one form to another in InfoPath 2003

Opening a new form from another form in InfoPath is not a straight forward task. If the form is published to a SharePoint document library, we can invoke it by launching it through the browser. If it is published at a network share, you can launch it as an application from your code.

 

The problem starts if we have to pass parameters to the form. Say, if we need to fill some controls on the form, we cannot pass the parameters as command line for the forms published to network share nor can we pass parameters as querystring for the ones published to SharePoint.

 

There are several ways in which you can pass parameters to InfoPath forms. Couple of them includes using XML file to store the details and while loading the new form, reading and populating from that file, using script file to launch and fill parameters to InfoPath (though this apparently has some drawbacks when forms are published to SharePoint).

 

I’ve a simple solution to this in the attached sample, which will allow passing parameters from one InfoPath form to another using the Interop API’s provided with InfoPath SDK.

 

You’ve to publish the parent form to C:\parent.xsn and child form to C:\Child.xsn to run it out of the box.

Posted by Sidhartha | 1 Comments
Filed under:

Attachment(s): OpenNewForm.zip

Using configuration file with InfoPath 2003

If you are working with InfoPath 2003, sooner or later you’ll encounter situations requiring you to obtain information from a config file. If you are using Visual Studio 2003 you can add a config file to the project, but reading from that file is not possible using the configuration reader. You can load that file as an XML file and have to read from that. So, loading data from config file is not a straight forward task. However, there are certain workarounds for this.

 

Instead of using the app.config file, we can create a simple XML file and read from that file using the classes in System.Xml namespace. This also gives you the leisure of having the required schema, so you can load directly into, say, a dataset if the config file is complex.

 

To give a more professional look for this, it would be friendly for the user to provide an InfoPath form that will save the information into the required config xml file.

 

Attached is the sample InfoPath 2003 form (using Visual Studio 2005) which uses config settings taken from the xml file generated using another Config form to send mails.

 

NOTE:

 

This approach requires the forms to be provided with Full-trust and digitally signed

 

Posted by Sidhartha | 0 Comments
Filed under:

Attachment(s): UsingConfig.zip

Hello, World!

I am sure this is not the first time you are looking at that title.

I am pretty excited to start my blog at blogs.msdn.com. Though, this is not the first time I am saying "Hello, World!" in the blogging space, this is a special one for me.

I'm working with Microsoft Global Services, where we help customers build enterprise level applications using .Net and other Microsoft Server technologies. I don't assure that you'll get to know what we are doing in detail, but certainly will know some of the challenges we face here.

So, Stay tuned while I try and do something and share it...

Best Regards,

IamJunk:)

Posted by Sidhartha | 3 Comments
 
Page view tracker