Note that there are some explanatory texts on larger screens.

plurals
  1. POC# When consuming two sets of "identical" classes from different namespaces, how do I avoid writing the same method(s) twice?
    primarykey
    data
    text
    <p>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.</p> <p>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.</p> <p>For example:</p> <pre><code>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); } } </code></pre> <p>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. </p> <p>I want to avoid writing two nearly identical Login() methods. So I'm thinking about something like this:</p> <pre><code>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) } } </code></pre> <p>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. </p> <p>Is there a best-practice pattern for this problem or am I stuck writing two Login() methods?</p> <p><strong>UPDATE</strong></p> <p>The partial classes and interfaces approach I mentioned above works like this: </p> <pre><code>namespace Domain { public interface ILoginResult { } public interface IServiceSession { } public interface IService { public IServiceSession Session { get; set; } public ILoginResult Login(string username, string password); } } </code></pre> <p>Then I add some code (not auto-generated) to extend the auto-generated classes: </p> <pre><code>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 } </code></pre> <p>And then I modify my DomainClass like so: </p> <pre><code>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 } } </code></pre> <p>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. </p> <p>So, now in my partial classes I get stuck having to implement new concrete members like this: </p> <pre><code>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); } } } </code></pre> <p>I don't like this, so I tried using generics to avoid the concrete implementations, like this:</p> <pre><code>namespace Domain { public interface ILoginResult { } public interface IServiceSession { } public interface IService&lt;TLoginResult, TServiceSession&gt; where TLoginResult : ILoginResult where TServiceSession : IServiceSession { public TServiceSession Session { get; set; } public TLoginResult Login(string username, string password); } } </code></pre> <p>...which leads to a change in my Service class signature like this:</p> <pre><code>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&lt;LoginResult, ServiceSession&gt; { } } </code></pre> <p>So I avoid the concrete implementation, but now the compiler breaks when I consume the Service in my DomainClass: </p> <pre><code>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) } } </code></pre> <p>Feels like the hole I'm digging is too deep, so I took a step back and posted on Stack Overflow!</p> <p>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). </p>
    singulars
    1. This table or related slice is empty.
    1. This table or related slice is empty.
    plurals
    1. This table or related slice is empty.
    1. This table or related slice is empty.
    1. This table or related slice is empty.
    1. This table or related slice is empty.
 

Querying!

 
Guidance

SQuiL has stopped working due to an internal error.

If you are curious you may find further information in the browser console, which is accessible through the devtools (F12).

Reload