2

For WCF clients, I have a IServiceProxyFactory interface to set credentials.

public interface IServiceProxyFactory<T>
{
    T GetServiceProxy();
}

public class ServiceProxy1 : IServiceProxyFactory<ServiceClient1>
{
    public ServiceClient1 GetServiceProxy()
    {
        var client = new ServiceClient1();
        // set credentials here
        return client;
    }
}

public class ServiceProxy2 : IServiceProxyFactory<ServiceClient2> { 
    // ... 
} 

From the question What is the best workaround for the WCF client `using` block issue?, and I created a helper as follows:

public static class Service<TProxy, TClient>
    where TProxy : IServiceProxyFactory<TClient>, new()
    where TClient : ICommunicationObject
{
    public static IServiceProxyFactory<TClient> proxy = new TProxy();

    public static void Use(Action<TClient> codeBlock)
    {
        TClient client = default(TClient);
        bool success = false;
        try
        {
            client = proxy.GetServiceProxy();
            codeBlock(client);
            ((ICommunicationObject)client).Close();
            success = true;
        }
        finally
        {
            if (!success)
            {
                ((ICommunicationObject)client).Abort();
            }
        }
    }
}

And I use the helper as:

Service<ServiceProxy1, ServiceClient1>.Use(svc => svc.Method()); 

Question:

  1. Is there a way where I can get rid of the TClient or TProxy(updated) type so that I can call using:

    Service<ServiceProxy1>.Use(svc => svc.Method()); 
    

    OR (updated)

    Service<ServiceClient1>.Use(svc => svc.Method()); 
    
  2. Is there a better way than to use ICommunicationObject for Close() and Abort()?

Community
  • 1
  • 1
hIpPy
  • 4,649
  • 6
  • 51
  • 65
  • Hmmm...can you change the constraint on the class to be IServiceProxy, remove TClient from the class signature, and add TClient and the constraint to the method? – JerKimball Mar 24 '13 at 02:12

2 Answers2

3
  • Code

    partial class TestClass {
        public static void TestMethod() {
            Service<ServiceProxy1>.Use(svc => svc.Method());
            Service<ServiceProxy1>.Use(svc => svc.Method());
            Service<ServiceProxy1>.Use(svc => svc.Method());
            Service<ServiceProxy2>.Use(svc => svc.Method());
        }
    }
    
    public partial interface IServiceProxyFactory<T> {
        T GetServiceProxy();
    }
    
    public partial class Service<T> where T: ServiceProxy, new() {
        public static void Use(Action<T> codeBlock) {
            using(var client=ServiceProxy.GetServiceProxy<T>().GetServiceProxy() as T)
                try {
                    codeBlock(client);
                }
                catch {
                    throw;
                }
        }
    }
    
    public abstract partial class ServiceProxy: CommunicationObject, IDisposable {
        public static T GetServiceProxy<T>() where T: ServiceProxy, new() {
            var proxy=m_List.FirstOrDefault(x => typeof(T).Equals(x.GetType())) as T;
    
            if(null==proxy) {
                proxy=new T();
                m_List.Add(proxy);
            }
    
            return proxy;
        }
    
        public abstract ServiceProxy GetServiceProxy();
        public abstract void Method();
    
        protected virtual void Dispose(bool disposing) {
            lock(ThisLock)
                if(!this.IsDisposed&&disposing) {
                    this.Close();
    
                    if(!this.IsDisposed)
                        this.Abort();
                }
        }
    
        public void Dispose() {
            this.Dispose(true);
            GC.SuppressFinalize(this);
        }
    
        ~ServiceProxy() {
            this.Dispose(false);
        }
    
        static List<ServiceProxy> m_List=new List<ServiceProxy>();
    }
    
    public partial class ServiceProxy1: ServiceProxy {
        protected override IAsyncResult OnBeginClose(
            TimeSpan timeout, AsyncCallback callback, object state
            ) {
            throw new NotImplementedException();
        }
    
        protected override IAsyncResult OnBeginOpen(
            TimeSpan timeout, AsyncCallback callback, object state
            ) {
            throw new NotImplementedException();
        }
    
        protected override void OnAbort() {
        }
    
        protected override void OnEndClose(IAsyncResult result) {
        }
    
        protected override void OnEndOpen(IAsyncResult result) {
        }
    
        protected override void OnClose(TimeSpan timeout) {
        }
    
        protected override void OnOpen(TimeSpan timeout) {
        }
    
        protected override TimeSpan DefaultCloseTimeout {
            get {
                return TimeSpan.Zero;
            }
        }
    
        protected override TimeSpan DefaultOpenTimeout {
            get {
                return TimeSpan.Zero;
            }
        }
    
        public override ServiceProxy GetServiceProxy() {
            var client=new ServiceProxy1();
            // set credentials here
            return client;
        }
    
        public override void Method() {
        }
    }
    
    public partial class ServiceProxy2: ServiceProxy1 {
        public override ServiceProxy GetServiceProxy() {
            var client=new ServiceProxy2();
            // set credentials here
            return client;
        }
    }
    

Something to mention:

  1. lock are safe to be re-entered

  2. It's impossible for me to do the exactly same declaration of a reversed inference from T to a type of any Generic<T>, such as from ServiseClient to ServiceProxy<ServiceClient>.

  3. According to 2, ServiceProxy and ServiceClient are just the same thing in the code, so there's no ServiceClient.

  4. ServiceProxy itself is abstract. For the requirement of ServiceClient need to be convert to ICommunicationObject and according to 3 plus for the convenience reason, ServiceProxy derives from CommunicationObject; and then for Is there a better way than ... stuff, it implements IDisposible

  5. For the static instances of a concrete class which inherits ServiceProxy, each would have only one instance, and stored in m_List, and calling the static generic version of GetServiceProxy<T>() just get them. This appears it's more like the flyweight pattern.

  6. According to 5, the interface IServiceProxyFactory<T> is not used at all, just put it there for look and feel happy.

  7. The instance version of GetServiceProxy() is kept the original usage, but concrete class would need to override it.

  8. In the Use method, the created client is used with a using statement. I've read the Avoiding Problems with the Using Statement, but seems I'm not aware the strategy you would like to handle the exceptions, thus I just try and rethrow.

  9. According to 1, I'm consider a thread safe way by lock to dispose atomically, the inherited IsDisposed and ThisLock are used. The Close and Abort are doing there accordingly.

  10. Oh ten! The class ServiceProxy2 and ServiceProxy1 are just for sample, and ServiceProxy2 derives from ServiceProxy1.

The code looks verbose but in fact very simple design. Just report me for the issues and I'll try to correct it. Wish helps anda GOOD LOCK!

Ken Kin
  • 4,503
  • 3
  • 38
  • 76
  • 1
    just FYI: for the 8th point: the Dispose should never throw and even more, you should be able to safely call Dispose many times on an already-disposed object. There's no such problem as "exception hiding with Using" - the problem is in invalid implementation of one's Dispose method – quetzalcoatl Mar 24 '13 at 10:02
  • @quetzalcoatl: Oh, sure. I mainly assumed that I don't know if any possible thrown, even before dispose. So leave a *try and rethrow* should be applicable way. And thank you very much! – Ken Kin Mar 24 '13 at 10:15
  • @KenKin, I ended up going with my answer below. But your use of `partial` nudged me in the right direction and so I'm going to give you the bounty. – hIpPy Mar 25 '13 at 05:27
1

I achieved it by merging ServiceProxy1 class into ServiceClient1 by using ServiceClient1 : IServiceProxyFactory<ServiceClient1> trick.

public interface IServiceProxyFactory<T>
{
    // can have a better name like SetCredentials()
    T GetServiceProxy();
}

// Got rid of ServiceProxy1 class
public partial class ServiceClient1 : IServiceProxyFactory<ServiceClient1>
{
    public ServiceClient1 GetServiceProxy()
    {
        var client = this;
        // set credentials here
        //client.ClientCredentials = "";
        return client;
    }
}

public partial class ServiceClient2 : IServiceProxyFactory<ServiceClient2> { ... } 

public static class ServiceMod<TClient>
    where TClient : class, ICommunicationObject, IServiceProxyFactory<TClient>, new()
{
    public static TReturn Use<TReturn>(Func<TClient, TReturn> codeBlock)
    {
        TClient client = default(TClient);
        bool success = false;
        try
        {
            client = new TClient().GetServiceProxy();
            TReturn result = codeBlock(client);
            client.Close();
            success = true;
            return result;
        }
        finally
        {
            if (!success)
            {
                client.Abort();
            }
        }
    }
}

And now I can do:

Service<ServiceClient1>.Use(svc => svc.Method());
hIpPy
  • 4,649
  • 6
  • 51
  • 65
  • Thanks for the bounty, and it's also good to be a self-learner. Besides, I actually found in the first place it should be `Service.Use(svc => svc.Method());` because of the signature of `Action` in original question. So I then think the only possible thing is `ServiceProxy` and `ServiceClient` are the same thing. – Ken Kin Mar 25 '13 at 11:02