Null
Null

Reputation: 45

C# When consuming two sets of "identical" classes from different namespaces, how do I avoid writing the same method(s) twice?

I have for example two namespaces with three classes in each namespace.These two sets of three classes are--for my purposes--identical in names and properties, but each set exists in its own namespace.

As you can guess this code is generated automatically from two instances of the same service (one for production and the other for UAT). Each instance may or may not be on the same version, however the members I care about are always the same.

For example:

namespace Domain.Uat //Auto-generated
{
    public class LoginResult { }
    public class ServiceSession { }
    public class Service
    {
        public ServiceSession Session { get; set; }
        public LoginResult Login(string username, string password);
    }
}
namespace Domain.Prod //Auto-generated
{
    public class LoginResult { }
    public class ServiceSession { }
    public class Service
    {
        public ServiceSession Session { get; set; }
        public LoginResult Login(string username, string password);
    }
}

I have a class that logs in to the service and does a bunch of other stuff. I want to be able to choose UAT or Prod from the UI layer, so I've added a bool argument called "isUat" to my--in this case--Login() method.

I want to avoid writing two nearly identical Login() methods. So I'm thinking about something like this:

namespace Domain
{
    public class DomainClass
    {
        private object MyService; 
        private object MyLoginResult; 
        private object MySession;

        public void Login(string username, string password, bool isUat)
        {
            //New up the service here:
            if (isUat)
                MyService = new Domain.Uat.Service();
            else
                MyService = new Domain.Prod.Service();

            //Do all the common stuff here
            MyLoginResult = MyService.Login(username, password); 
            MySession = MyService.Session; 
        }

        //(Lots of other methods that use MyService and MySession go here)
    }
}

Declaring type object and casting to it is dumb but I think it illustrates my general approach. I've got it working a couple different ways with partial classes and interfaces ("ILoginResult", "IServiceSession", "IService", etc.) but it feels hacky.

Is there a best-practice pattern for this problem or am I stuck writing two Login() methods?

UPDATE

The partial classes and interfaces approach I mentioned above works like this:

namespace Domain
{
    public interface ILoginResult { }
    public interface IServiceSession { }
    public interface IService
    {
        public IServiceSession Session { get; set; }
        public ILoginResult Login(string username, string password);
    }
}

Then I add some code (not auto-generated) to extend the auto-generated classes:

namespace Domain.Uat //Do the same for Domain.Prod
{
    public partial class LoginResult : Domain.ILoginResult { }
    public partial class ServiceSession : Domain.IServiceSession { }
    public partial class Service : Domain.IService { } //Doesn't work, see below
}

And then I modify my DomainClass like so:

namespace Domain
{
    public class DomainClass
    {
        private ILoginResult MyLoginResult;
        private IServiceSession MyServiceSession;
        private IService MyService;

        public void Login(string username, string password, bool isUat)
        {
            //New up the service here
            if (isUat)
                MyService = new Domain.Uat.Service();
            else
                MyService = new Domain.Prod.Service();

            //Common stuff here
            MyLoginResult = MyService.Login(username, password);
            MyServiceSession = MyService.ServiceSession;
        }

        //More members here
    }
}

This doesn't work, however, because Domain.Uat.Service doesn't actually implement Domain.IService. Domain.IService.ServiceSession gets-sets an IServiceSession, but Domain.Uat.Service doesn't actually have an IServiceSession property named ServiceSession. The same goes for LoginResult.

So, now in my partial classes I get stuck having to implement new concrete members like this:

namespace Domain.Uat //Do the same for Domain.Prod
{
    public partial class LoginResult : Domain.ILoginResult { }
    public partial class ServiceSession : Domain.IServiceSession { }
    public partial class Service : Domain.IService
    {
        public Domain.IServiceSession Domain.IService.ServiceSession
        {
            get { return Session; }
            set { Session = (Domain.Uat.ServiceSession)value; }
        }
        public Domain.ILoginResult Domain.IService.Login(string username, string password)
        {
            return Login(username, password);
        }
    }
}

I don't like this, so I tried using generics to avoid the concrete implementations, like this:

namespace Domain
{
    public interface ILoginResult { }
    public interface IServiceSession { }
    public interface IService<TLoginResult, TServiceSession>
        where TLoginResult : ILoginResult
        where TServiceSession : IServiceSession
    {
        public TServiceSession Session { get; set; }
        public TLoginResult Login(string username, string password);
    }
}

...which leads to a change in my Service class signature like this:

namespace Domain.Uat //Do the same for Domain.Prod
{
    public partial class LoginResult : Domain.ILoginResult { }
    public partial class ServiceSession : Domain.IServiceSession { }
    public partial class Service : Domain.IService<LoginResult, ServiceSession> { }
}

So I avoid the concrete implementation, but now the compiler breaks when I consume the Service in my DomainClass:

namespace Domain
{
    public class DomainClass
    {
        private IService MyService; 
        private ILoginResult MyLoginResult; 
        private IServiceSession MySession;

        public void Login(string username, string password, bool isUat)
        {
            //New up the service here:
            if (isUat)
                MyService = new Domain.Uat.Service(); //Compiler fail, can't cast
            else
                MyService = new Domain.Prod.Service(); //Compiler fail, can't cast

            //Do all the common stuff here
            MyLoginResult = MyService.Login(username, password); 
            MySession = MyService.Session; 
        }

        //(Lots of other methods that use MyService and MySession go here)
    }
}

Feels like the hole I'm digging is too deep, so I took a step back and posted on Stack Overflow!

P.S. Covariance and contravariance won't work for my IService interface because one member is a property (yes I need to be able to both get and set on Session).

Upvotes: 0

Views: 1444

Answers (2)

ThinTim
ThinTim

Reputation: 575

If you can't control the interfaces of the generated code, you could create a wrapper for each service that inherits from a common interface / abstract class.

public class WrappedUatService : IService {
   private Domain.Uat.Service _service;

   public override ILoginResult Login() { 
       ILoginResult result = new WrappedLoginResult(_service.Login());
       ...
   }
}

public class WrappedProdService : IService {
   private Domain.Prod.Service _service;

   public override ILoginResult Login() { 
       ILoginResult result = new WrappedLoginResult(_service.Login());
       ...
   }
}

It's a fair bit of extra code, but you can't treat separate classes as though they're equivalent just because they happen to have methods with the same signatures.

Upvotes: 1

josey wales
josey wales

Reputation: 271

I don't think it's hacky to use an interface for each class. Then change to be like this.

private ILoginResult MyLoginResult

Then create a constructor for domainclass and pass uat in and instantiate your properties in the constructor.

Then the login method or any other methods wouldn't need the if statement

Upvotes: 1

Related Questions