views:

229

answers:

3

In my web app I use several asmx (Web Services) from the same provider, they have one for this, other for that, but all require a SOAP Header with Authentication.

It is simple to add the Authentication:

public static SoCredentialsHeader AttachCredentialHeader()
{
    SoCredentialsHeader ch = new SoCredentialsHeader();
    ch.AuthenticationType = SoAuthenticationType.CRM5;
    ch.UserId = "myUsername";
    ch.Secret = apUtilities.CalculateCredentialsSecret(
        SoAuthenticationType.CRM5, apUtilities.GetDays(), "myUsername", "myPassword");
    return ch;
}

The problem is this SoCredentialsHeader come (derivation) from ONE webservice and I need to add the same code to the others, like:

public static wsContact.SoCredentialsHeader AttachContactCredentialHeader()
{
    wsContact.SoCredentialsHeader ch = new wsContact.SoCredentialsHeader();
    ch.AuthenticationType = wsContact.SoAuthenticationType.CRM5;
    ch.UserId = "myUsername";
    ch.Secret = apUtilities.CalculateCredentialsSecret(
        wsContact.SoAuthenticationType.CRM5, apUtilities.GetDays(), "myUsername", "myPassword");
    return ch;
}

public static wsDiary.SoCredentialsHeader AttachDiaryCredentialHeader()
{
    wsDiary.SoCredentialsHeader ch = new wsDiary.SoCredentialsHeader();
    ch.AuthenticationType = wsDiary.SoAuthenticationType.CRM5;
    ch.UserId = "myUsername";
    ch.Secret = apUtilities.CalculateCredentialsSecret(
        wsDiary.SoAuthenticationType.CRM5, apUtilities.GetDays(), "myUsername", "myPassword");
    return ch;
}

Is there a way to implement a design pattern in order to use only one function but that suits all webServices?

sometimes I see the T letter, is this a case for that? if yes, how can I accomplish such feature?

P.S. I could pass an enum and use a switch to check the enum name and apply the correct Header but everytime I need to add a new WebService, I need to add the enum and the code, I'm searcging for an advanced technique for this.

Thank you.

A: 

I don't know if this is something you wish to consider, as it definitely has it's down sides, but - as ultimately these ( the varoius SoCredentialsHeader classes) are all copies of the same class definition in different namespaces so with a little bit of refactoring you could simply have one class and one method.

Copy the SoCredentialsHeader class definition to a project of your own, add a reference to it and remove the class definition from all web service's proxies. Add a using statement at the top of the proxy code file and it would not tell the difference.

Basically you told it to use the same class definition (yours) for all web services.

The obvious down side is that you have to repeat this exercise whenever you update and web service reference (and that it assumes all services keep using the same definition), but we've been doing this in a similar scenario and it worked quite well for us.

Yossi Dahan
this work is what I'm trying to avoid... what you say will be the same as having an enum and them apply the correct namespace :)
balexandre
A: 

I would try using a generic method, then use reflection to set the properties:

public static T AttachDiaryCredentialHeader<T>() where T: class
{
    T ch = new T();
    Type objType = ch.GetType();
    PropertyInfo userId = objType.GetProperty("UserId");
    authType.SetValue(ch, "myUsername", null)
    //And so on for the other properties...
    return ch;
}

IMHO, This is somewhat hacky, I would keep them separate, unless like the previous post mentioned it, you're absolutely sure the definitions of these services will remain the same. One minor change to one of them will break this.

Ricardo Villamil
+3  A: 

Create a file called whatever.tt (the trick is the .tt extension) anywhere in your VS solution and paste the following code:

using System;

namespace Whatever
{
    public static class Howdy
    {
<# 
    string[] webServices = new string[] {"wsContact", "wsDiary"};
    foreach (string wsName in webServices)
    {
#>
    public static <#=wsName#>.SoCredentialsHeader AttachContactCredentialHeader()
    {
     <#=wsName#>.SoCredentialsHeader ch = new <#=wsName#>.SoCredentialsHeader();
     ch.AuthenticationType = <#=wsName#>.SoAuthenticationType.CRM5;
     ch.UserId = "myUsername";
     ch.Secret = apUtilities.CalculateCredentialsSecret(<#=wsName#>.SoAuthenticationType.CRM5,
                apUtilities.GetDays(), "myUsername", "myPassword");
     return ch;
    }
    }       
<# } #>
}

Then watch as a whatever.cs magically appears with the desired code snippets.These are called T4 templates for code generation in VS.

You'll want to turn these into partial classes or extension methods or something. The above code will not function "as is" but you get the idea.

Todd Smith
I will give this a try and I will search more about T4 templates :)
balexandre