1
\$\begingroup\$

My Problem

I have a library containing business logic which is being used from 3 different projects:

  • Website
  • Local WPF Application
  • WCF Service

The library uses Entity Framework and a connection string which is read out of config on first use and saved in a static variable. (The config file for all 3 consumer projects have the same keys)

Now I've changed the WCF Service, so that one service can serve different users, meaning different databases. Of course now the static connection string only holds one connection string so I need a concept of changing the used connection string depending on who called the service. Along with the connection string I have some more user-specific data which I stored in a static global class.

My Solution

On the WCF Service I use a custom authorization policy which changes the threads principal user to my own custom identity. On each call the service authenticates the caller, and changes the threads identity to the specific object which now contains the connection string and all the specific data previously stored in static global. If there is no thread principal set (or at least not from the type I expected), I assume the caller is not the service but the website or local application. Then I store the Identity in static variable like I did before.

My Code

/// <summary>
/// Represents the settings for the current context (user database).
/// Usually for web and app this is always the database defined in config-file and therefor stored in static
/// For WCF service the context can vary depending on the caller if the current principal is set
/// </summary>
public class ContextIdentity : IIdentity
{
    public static ContextIdentity LocalIdentity;
    public static ContextIdentity Current
    {
        get
        {
            if (Thread.CurrentPrincipal.Identity is ContextIdentity)
            {
                // Return the threads Context
                return Thread.CurrentPrincipal.Identity as ContextIdentity;
            }
            else
            {
                // Return the Local context
                if (LocalIdentity == null)
                {
                    LocalIdentity = new ContextIdentity();
                }
                return LocalIdentity;
            }
        }
    }

    public ContextIdentity()
    {
        // Load from File
        _name = "Default";
        Server = ConfigurationManager.AppSettings["DatabaseServer"];
        Database = ConfigurationManager.AppSettings["DatabaseName"];
        User = ConfigurationManager.AppSettings["DatabaseUser"];
        Password = ConfigurationManager.AppSettings["DatabasePass"];
        IntegratedSecurity = "True".Equals(ConfigurationManager.AppSettings["DatabaseIntegratedSecurity"]);
    }

    public ContextIdentity(string contextName, string server, string database, string user, string password, bool integratedSecurity)
    {
        // Load from File
        _name = contextName;
        this.Server = server;
        this.Database = database;
        this.User = user;
        this.Password = password;
        this.IntegratedSecurity = integratedSecurity;
    }


    #region Context Data Connection

    public string Server { get; set; }

    public string Database { get; set; }

    public string User { get; set; }

    public string Password { get; set; }

    public bool IntegratedSecurity { get; set; }

    // Other stuff needed...

}
\$\endgroup\$
2
\$\begingroup\$

The general approach seems reasonable to me, thought I believe it means you have to very careful around anything that can switch threads, like TPL or async-await.


public static ContextIdentity LocalIdentity;

Why is this public? From your description, it seems to me that users should never read this.

If this is so that users can set this, maybe a setter on Current would make more sense.

Also, you should use properties instead of public fields.


return Thread.CurrentPrincipal.Identity as ContextIdentity;

You already know that Indentity is ContextIdentity, so you should use a cast, not as. That way, if you make a mistake, you're going to get an exception, not null.


_name = "Default";

Why is _name a field, not a property like the others? If you don't want users changing the name, you can make the setter private.


"True".Equals(ConfigurationManager.AppSettings["DatabaseIntegratedSecurity"])

This is not Java, you can use == "True".

\$\endgroup\$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.