Dailycode.info

Short solution for short problems

WCF calls giving error 400 Bad request

I'm working on a large project using WCF. We recently migrated from Remoting and Webservices to WCF. In the beginning everything worked fine. Then I started testing with larger quantities and I got this lovely error: Bad Request Error.

I couldn't find the problem, so I started testing and realized that it had something todo with the packet size. After some googling I found the solution to my problem.

I had to create a custom wsHttpBinding and increase the max recieved message size in there. The default is: 65536 bytes, I increased this to 1000000 bytes (The maximum is 2147483647). Following the advice of some other collegues, this is what I came up with this configuration file :

 

    <bindings>
      <wsHttpBinding>
        <binding name="wsHttp"maxReceivedMessageSize ="10000000"
        messageEncoding="Mtom"maxBufferPoolSize="10000000" >
          <readerQuotasmaxDepth="10000000"
          maxArrayLength="10000000"maxBytesPerRead="10000000"
          maxNameTableCharCount="10000000"maxStringContentLength="10000000"/>
        </binding>
      </wsHttpBinding>
    </bindings>

Then on the client side where I'm using a ChannelFactory I added this code to instantiate the proxy class:

 

var wsHttp = new WSHttpBinding
{
    ReaderQuotas =
    {
        MaxDepth = 10000000,
        MaxArrayLength = 10000000,
        MaxBytesPerRead = 10000000,
        MaxNameTableCharCount = 10000000,
        MaxStringContentLength = 10000000
    },
    MaxReceivedMessageSize = 10000000,
    MessageEncoding = WSMessageEncoding.Mtom,
    MaxBufferPoolSize = 10000000
};
_animalMgrProxy = ChannelFactory<IAnimalManager>.CreateChannel(wsHttp, new EndpointAddress(animalEndPoint));

 

And I got rid of the ugly 400 bad request error. If I have some time left, I can find out what other settings can be fine tuned. I defenately will check this, but the timeline for the first demo is getting short. 


Set Datagridview selection

How to select a row in a datagrid view using c# .Net

Very simple, but to fnid this out, it can still take up some of your precious time.

So I will place the code here to easy the pain. (I got my info here.)

Set the datagridview to fullrowselect. Then use this code to select the row you want. First we scroll the datagrid to the record, then we select the record.

dataGridView1.FirstDisplayedScrollingRowIndex = Row;
dataGridView1.Refresh();
dataGridView1.CurrentCell = dataGridView1.Rows[Row].Cells[0];
dataGridView1.Rows[Row].Selected = true;

 


Temporary no comments

Because of all the SPAM comments,

I decided to disable the comments, until I've implemeted a anti spam extension to the blog.

I did not have time to do this yet.

You can always contact me via the contact form of this blog.


RichTextBox.Rtf

I was trying to manipulate some text in a richt text box. It turned out not to be as easy as I thought. One very good tip is when you are using rich text signs, enter a space between the text and the signs, or else the compiler will not understand the richt text tags.

For example, if you put /bBOLD/b0 it will display nothing, but if you put it like this: /b bold/b0 it will display it correct. To ensure following tags are correctly understood, better to put spaces before and after all tags: /b bold /b0.

Here's a little more advaced example:

StringBuilder sb = new StringBuilder();
sb.Append(@"{\rtf1\ansi Study Nr: \b " + 165 + @" \b0 \par ");
sb.Append(@"Is GLP: \b " + true.ToString() + @" \b0\par ");
sb.Append(@"Nr of CagePartners: \b " + 5 + @" \b0\par");
sb.Append(@"Minimum weight loss: \b " + 5.632 + @" \b0 g\par ");
sb.Append(@"StartDate: \b " + DateTime.Now + @" \b0\par ");
sb.Append(@"Effective animal replace period: \b " + 7 + @" \b0 days\par ");
sb.Append(@"Study supervisor: \b " + "mderaeve" + @" \b0 \par ");
richTextBox1.Rtf = sb.ToString();
 

So we start with {\rtf1\ansi

Then we can enter text and manipulate it.

 

To look for the rich text specifications, look here:

Rich Text Format (RTF) Specification, version 1.6:

http://msdn.microsoft.com/en-us/library/aa140277(v=office.10).aspx

Or

WD: Rich Text Format (RTF) Specification 1.7;

http://support.microsoft.com/kb/86999

 

 
 

 


Quality document management system (QDocs)

Working an a laboratory environment, I have a lot of experience with SOP (Standard Operating Procedures) and WKI (WorK Instructions). In the beginning my company was using a paper based system, because the lab was subjected to GLP and thus a computerized system had to be validated and was not easy to find.

Then the new QA offiver decided to create a new electronical system because the need for collecting signatures electronical was getting high since everything was done on paper.

We decided to use WSS as base system and expand it specially for the needs of the lab conform to GLP. WSS is a free system that comes with any windows server edition, so no extra cost.

The system is now going into its 3rd year and is running version 3 now. Each version contained more functionality since the system was such a succes.

In the following posts I will explain the workflows and functionalities of the system. Some technical explanation is also given.

I decided to make a complete new version with the same principles, because my company owns the initial system, I will start from scratch and improve were I can. The first improvement is that I now use content types and its possible to add new document types. 2 default types are SOP and WKI.

Here is a visual overview of the review process, the approval and publishing workflow that automatically follow after the review workflow will be explained in further posts.

 

So QS will initiate the workflow by creating a new Quality document. The reason why QS initiates is that they can perform a check first if the new document is relevant. Also they will decide on the author and responsible reviewer. The responsible reviewer can be the authors direct supervisor or any subject expert. When QS finishes the initiation of the document, an email is sent to the author to tell him about the creation of the document.

The author can start editing the document. When he finishes he can send it to the respobsible reviewer for review. He can approve or disapprove the review. if he disapproves the review, the document will be resended to the author. Whe the RR finishes and approves the review the document advances in the workflow. The last step in the review process is that the QA department does a last check and assignes persons or groups to be instructed in the document. then they release the document for approval. this will be described in a next post.

 The technology used to create this document management system is WSS, .Net, MS SQL 2005 or 2008 and IRM. All Microsoft based products. The custom development consists out of a Sharepoint workflow, several web parts and custom WSS site features. This makes the technology very strong and flexible for future upgrades.

 

 


Implement Auditing using MessageQueue and WCF

In my new application, I started to implement extensive auditing, for this was required by the business. After some thoughts and a course of WCF, I discoverd that MessageQueue combined with WCF is a perfect solution for auditing. Since auditing can have a lot of inserts, but doens't need a return, we can implement this as one way traffic. From client to server.

So I created a function that writed audit records that have following information: User, Message, Extra information (Original and changed values, or a guid to the record or ...) and a state. The state will make sure the audit records end up in the corresponding audit tables.

If you decorate the operation contract with the IsOneWay=true flag, you can use it for message queue. The service interface looks like this:

 

    [ServiceContract]
    public interface IAuditManager
    {
        [OperationContract(IsOneWay=true)]
        void Audit(AuditRecord auditRec);
    }
 

Then the implementation looks like this: 

public void Audit(LabCollect.ServerComponents.SharedClasses.AuditRecord auditRec)
{
    ColumnCollection columns = new ColumnCollection();
    columns.Add("Message", auditRec.Message);
    columns.Add("EXTRAINFO", auditRec.ExtraInfo);
    columns.Add("TIME_STAMP", auditRec.TimeStamp);
 
    GLPDA.Insert("LA_AUD_"+auditRec.State, columns, _dbString, auditRec.User);
}

The last update is automatically logged by the server. This can be different then the time stamp of the audit record, because in message queue the server time is not always the same time as the record was created by the client.

I used a channelfactory on the client to address the WCF service. I'm not using transactions for writing single audit records. You can initiate the AuditManager like this: 

string auditEndPoint = "net.msmq://localhost/private/LabCollectAudit";
NetMsmqBinding
bindings = new NetMsmqBinding();

 

bindings.ExactlyOnce=false;
_auditMgrProxy = ChannelFactory<IAuditManager>.CreateChannel(bindings, new EndpointAddress(auditEndPoint));

The ExactlyOnce property needs to be set to false when you are not using transactions. So All that is left is to configure the endpoints on the host, which is very simple. By example I show you how to do it using the configuration file:

<system.serviceModel>
    <services>
      <service behaviorConfiguration="LabCollectWCFAuditService.Service1Behavior"
        name="LabCollectWCFAuditService.AuditManager">
        <endpoint address="net.msmq://localhost/private/LabCollectAudit" binding="netMsmqBinding" 
contract="LabCollectWCFAuditService.IAuditManager" bindingConfiguration="MSQueueBinding">
          <identity>
            <dns value="localhost" />
          </identity>
        </endpoint>
        <endpoint address="mex" binding="mexHttpBinding" contract="IMetadataExchange" />
        <host>
          <baseAddresses>
            <add baseAddress="http://localhost:7100/LabCollectWCFAuditService/AuditManager/" />
          </baseAddresses>
        </host>
      </service>
    </services>
    <bindings>
      <netMsmqBinding>
        <binding name="MSQueueBinding" exactlyOnce="false"></binding>
      </netMsmqBinding>
    </bindings>
    <behaviors>
      <serviceBehaviors>
        <behavior name="LabCollectWCFAuditService.Service1Behavior">
          <!-- To avoid disclosing metadata information, 
          set the value below to false and remove the metadata endpoint above before deployment -->
          <serviceMetadata httpGetEnabled="True"/>
          <!-- To receive exception details in faults for debugging purposes, 
          set the value below to true.  Set to false before deployment 
          to avoid disclosing exception information -->
          <serviceDebug includeExceptionDetailInFaults="True" />
        </behavior>
      </serviceBehaviors>
    </behaviors>
  </system.serviceModel>

Do not forget to create the Message queue on your machine or server. When you insert records using the clients and make sure the services on the "server-side" are not running, then you can see them in your mesage queue:


Migrating remoting to WCF

WCF is the sequal for .Net remoting and web services. Its a very powerfull and helpfull strategy. Finally we can forget about all the different implementations and simply use WCF. Configure if you need the TCP or http protocol, Message queue ... . This is great. I started to migrate the LIMS system that uses both Remoting and Web services. The webservices are still to come, but I don't expect problems. The remoting is a different story. Since remoting supports overloading and WCF doesn't, you can rewrite a lot of code and revise all code that consume you're remoting functions.

So the first thing that came up was to get rid overloaded functions by renaming them after their parameters.

This covered most of the work. Then some strange hard to debug error poped up. All my objects had the [DataContract] and [DataMember] defined, but I forgot that enums need a [DataContact] and [EnumMember]. You will need to define the [EnumMember] for each value in the enum. For Example:

After some configuration and the creation of the Channel factory on the client, I got the whole thing to work. Here is the code of the channelfactory implementation:

      /// <summary>
      /// Describes the different types of hardware
      /// </summary>
    [DataContract]
    public enum DeviceTypeEnum
      {
        [EnumMember]
            Plexx = 1,                    //    Plexx RF reader
        [EnumMember]
        Sartorius,                        //    Sartorius Balance
        [EnumMember]
        Mettler,                    //    Mettler Toledo Balance
        [EnumMember]
        MBBS,                             //    MBBS ISO Reader
        [EnumMember]
        Opticon,                    //    Opticon barcode reader
        [EnumMember]
        AllFlex                           //  Allflex RFID reader
      }

 

using System;
using LabCollect.ServerComponents.SharedClasses;
using Deraeve.Connections;
using LabCollect.ServerComponents.Service;
using System.ServiceModel;
 
namespace LabCollect.SmartclientFrame.SubApplications
{
    /// <summary>
    /// Summary description for LabCollectRemotingFactory.
    /// </summary>
    public class LabCollectChannelFactory : IDisposable
    {
 
        // Remoting Object Factory
        private IAnimalManager _animalMgrProxy;
        private IConfigurationManager _configMgrpProxy;
        private IStudyManager _studyMgrProxy;
        private IUserManager _userMgrProxy;
 
        string animalEndPoint = "http://localhost:.../AnimalMgr";
        string configEndPoint = "http://localhost:.../ConfigurationMgr";
        string studyEndPoint = "http://localhost:.../StudyMgr";
        string userEndPoint = "http://localhost:.../UserMgr";
 
        public IAnimalManager AnimalManager
        {
            get 
            {
                if ((_animalMgrProxy as ICommunicationObject).State != CommunicationState.Opened)
                {
                    _animalMgrProxy = ChannelFactory<IAnimalManager>.CreateChannel(
new WSHttpBinding(), new EndpointAddress(animalEndPoint));
                }
                return _animalMgrProxy;
            }
        }
 
        public IConfigurationManager ConfigurationManager
        {
            get 
            {
                if ((_configMgrpProxy as ICommunicationObject).State != CommunicationState.Opened)
                {
                    _configMgrpProxy = ChannelFactory<IConfigurationManager>.CreateChannel(
new WSHttpBinding(), new EndpointAddress(configEndPoint));
                }
                return _configMgrpProxy; 
            }
        }
 
        public IStudyManager StudyManager
        {
            get 
            { 
                if ((_studyMgrProxy as ICommunicationObject).State != CommunicationState.Opened)
                {
                    _studyMgrProxy = ChannelFactory<IStudyManager>.CreateChannel(
new WSHttpBinding(), new EndpointAddress(studyEndPoint));
                }
                return _studyMgrProxy; 
            }
        }
 
        public IUserManager UserManager
        {
            get 
            {
                if ((_userMgrProxy as ICommunicationObject).State != CommunicationState.Opened)
                {
                    _userMgrProxy = ChannelFactory<IUserManager>.CreateChannel(
new WSHttpBinding(), new EndpointAddress(userEndPoint));
                }
                return _userMgrProxy; 
            }
        }
 
        public void InitChannels()
        {
            _animalMgrProxy = ChannelFactory<IAnimalManager>.CreateChannel(
new WSHttpBinding(), new EndpointAddress(animalEndPoint));
            _configMgrpProxy = ChannelFactory<IConfigurationManager>.CreateChannel(
new WSHttpBinding(), new EndpointAddress(configEndPoint));
            _studyMgrProxy = ChannelFactory<IStudyManager>.CreateChannel(
new WSHttpBinding(), new EndpointAddress(studyEndPoint));
            _userMgrProxy = ChannelFactory<IUserManager>.CreateChannel(
new WSHttpBinding(), new EndpointAddress(userEndPoint));
        }
 
        #region IDisposable Members
 
        public void Dispose()
        {
            if ((_animalMgrProxy as ICommunicationObject).State == CommunicationState.Opened)
            {
                (_animalMgrProxy as ICommunicationObject).Close();
            }
 
            if ((_configMgrpProxy as ICommunicationObject).State == CommunicationState.Opened)
            {
                (_configMgrpProxy as ICommunicationObject).Close();
            }
 
            if ((_studyMgrProxy as ICommunicationObject).State == CommunicationState.Opened)
            {
                (_studyMgrProxy as ICommunicationObject).Close();
            }
 
            if ((_userMgrProxy as ICommunicationObject).State == CommunicationState.Opened)
            {
                (_userMgrProxy as ICommunicationObject).Close();
            }
        }
 
        #endregion
    }
}

 

Next challange will be implementing apropriate error handling and also I want to change the audit function to use MessageQueue combined with WCF.

I'll keep you updated on this and have some snippits ready.

 

 

 


Generic Singleton

Generics enables a whole new functionality. Combining this with the power of a singleton class creates something really interesting: a generic singleton class! So this way you can get a singleton instance of any type of class you want.

public class GenericSingleton<T> where T : class, new()

{

    private static T instance;

 

    public static T GetInstance()

    {

        lock (typeof(T))

        {

            if (instance == null)

            {

                instance = new T();

            }

            return instance;

        }

    }

}

Now I can create singleton instances of several classes with the same singleton class. I created a singleton instance of the LabCollectChannelFactory class and initiated the channels.

Deraeve.General.GenericSingleton<LabCollectChannelFactory>.GetInstance().InitChannels();

The LabColectChannelFactory will stay alive as long as the singleton lives. In the channel factory I implemented the IDisposable interface and I clear all channels when the Dispose method is called. This way I do not need to create an instance and initiate the channels each time a WCF call is made. This is only on the client side, the server side can also work with singleton class calls. You can use this singleton class for all your needs. The lock function ensure thread safety!

So now, after I initiated the channelfactory once, I can start calling the WCF functions:

List<User> AllActiveUsers = Deraeve.General.GenericSingleton <LabCollectChannelFactory>.GetInstance().UserManager.GetAllUsers();


Directory copy(program attached to this post can be downloaded)

On request I created a small program that can copy one directory(including subdirectoies and files) to another directory. Nothing special, windows can do that. But The requirement was that only files created after a certain date needed to be copied.
It can be usefull for backup strategies or archiving.
Here a screenshot of this program.
 

 
Feel free to ask for the source code. Here a little snippet (This is a recursive function that loops over all files and directories. This functino is called in a seperate thread, so its not blocking the main thread and it can be stopped at all times.):
 
private void CopyDirStructure(string path, string searchdir)
{
    try
    {
        bool noOutput=false;
        if (GetOutputSize() > 2147400000)
        {
            SetOutput("\n!!!rest concatinated, to many files...!!!\n");
            noOutput=true;
        }
 
        DirectoryInfo dir = new DirectoryInfo(path);
        DirectoryInfo[] subDirs = dir.GetDirectories();
        FileInfo[] files = dir.GetFiles();
        
        int dept = 0;
 
        if (dir.FullName != searchdir)
        {
            if (files.Length > 0)
            {
                CreateDirAtDest(dir, noOutput);
            }
        }
        
        
        
            
        decimal dSize = 0;
        foreach (FileInfo fi in files)
        {
            bool bCopy = true;
            
            if (UseModifyDate())
            {
                if (fi.LastWriteTime < GetModifyDate())
                {
                    bCopy = false;
                }
            }
            if (bCopy)
            {
                if (CopyFileToDest(fi.FullName.ToString(), noOutput))
                {
                    dSize += Math.Round((decimal)fi.Length / 1024, 0, MidpointRounding.ToEven);
                    totalFileCount++;
                }
            }
        }
 
        
        
        totalSize += dSize;
       
        if (subDirs != null)
        {
            dept++;
            foreach (DirectoryInfo sd in subDirs)
            {
                CopyDirStructure(path + @"\\" + sd.Name, searchdir);
                totalDirCount++;
            }
        }
 
        SetDirCount(totalDirCount);
        SetFileCount(totalFileCount);
        SetSize(totalSize);
    }
    catch (Exception ex)
    {
        Console.WriteLine(ex.Message.ToString());
    }
}
The controls are updated after each file, because the copy is running in a seperate thread,
be sure that you invoke the controls before trying to update them:
 
public void SetFileCount(int c)
{
    if (this.lbltotal.InvokeRequired)
    {
        this.lbltotal.BeginInvoke(
            new MethodInvoker(
            delegate() { SetFileCount(c); }));
    }
    else
    {
        this.lbltotal.Text = c.ToString() + " files";
    }
}

Download the program here:

CopyFiles.zip (62.17 kb)


Animal randomization into animal groups and cages

This procedure described below will help you to randomize animals into animal groups according to the needs of a G(x)P environment:

The algorithm is based on a document published by Sage. You can find it here. The title is Randomization of Animals by Computer Program for Toxicity Studies. The process will follow the exact process using 5 different weight range groups. The only deviation on the process is that for we can use variable amount of animals for each group. The document assumed that all animal groups had an equal number of animals assigned.

This process runs separate for males and females.

  1. First the animals are sorted on weight.
  2. Then they are divided into 5 different weight groups.
  3. Each animal is assigned a unique random number within the weight range group.
  4. Then the animal groups will start picking the animals from the weight range groups.  The system calculates based on the number of animals that need to be allocated in the animal groups how many animals need to be taken from each weight range group. Then the first group starts taking the animals from the different weight range groups with the lowest random number. When the first group is finished, the second group starts taken animals from the weight range groups. It will start with the weight range group that follows the group where the first exposure group picked its last animal from and so on.

Here is a simple example:

40 animals are divided into 5 weight range groups, means 8 animals per group.

Animal group 1 needs 20 animals.

Animal group 2 needs 10 animals.

Animal group 3 needs 10 animals.

Animal group 1 will take 4 animals from each weight range group.

Animal group 2 will take 2 animals from each weight range group.

Animal group 3 will take 2 animals from each weight range group.

All animals are now randomly assigned to the different exposure groups and they are equally picked from the weight range groups.

  1. Then the animals are placed in cages so the animals in one cage have the best matching weight.
  2. Then the cages are randomized within the exposure groups.
  3. Then the animals are assigned animal number and animal group.

 

 

For each group the Mean value, Standard deviation and RSD is calculated. Then we calculate the maximum mean difference between the group. Good practise is to keep this number below 1%. The algoritm will help you on this. If the percetage is to high, then rerun the allocation and it will give a new result. Rerun untill you find a satisfying result. If the result is not satifying, this will mean that the weight of the animals is not suited for the allocation and you will have to remove some animals that have a very high or low weight.