Quick question/request re WcfProxy<T> in Csla Light

Quick question/request re WcfProxy<T> in Csla Light

Old forum URL: forums.lhotka.net/forums/t/6266.aspx


PeteH posted on Tuesday, January 27, 2009

Is the GetProxy() method within the WcfProxy<T> class private by design? I have implemented a custom WcfProxy<T> class as my business objects are not all on one server, thus resulting in my Silverlight app having to access several WCF channels, dependant on the namespace the business object is in. This is all done transparently in my custom class, but I had to alter the GetProxy method to a protected virtual, so that I could override it.

Should I be going about this another way, or could this be something changed in Csla Light? I also altered the SyncrhonizedWcfProxy if anyone is interested, so that I could override that also if needed, but I think that has been depreciated now.

Thanks for any advice!

RockfordLhotka replied on Tuesday, January 27, 2009

This is probably a change that should be made. I tend to err on the side of over-protecting types so I can loosen them later, and this is similar to a change I recently made on the .NET side for similar reasons.

I'll add this to the wish list.

RockfordLhotka replied on Wednesday, January 28, 2009

I made the method virtual, because that's probably not a bad idea for other scenarios.

But for your scenario, you should be able to create a subclass of WcfProxy<T> and use your own logic to set the server URL and/or binding properties. It sounds like you'd just need to set the DataPortalUrl property in your subclass, based on which server you need to talk to.

In other words, you shouldn't have to do anything as complex as overriding GetProxy() - just set a string property.

PeteH replied on Thursday, January 29, 2009

Thanks for making the change, that's perfect. As for my scenario, whilst it is true that I could use the DataPortalUrl property in theory, I would need to read the address property from each endpoint in the ServiceReferences.ClientConfig file to do so - which is far from straightforward. I did find a way to read these values from within Silverlight, but it was using quite hacky code (loading it into a resource stream, then using LINQ to match the attribute name; the link is http://cozenedcognizance.blogspot.com/2008/10/reading-clientconfig-values-in.html) and decided that really wasn't the best way forward.

So instead, I went along with having a different name for each endpoint in the ServiceReferences.ClientConfig, along these lines:

        <client>
            <endpoint address="http://xml.host.address/WcfPortal.svc"
                binding="basicHttpBinding" bindingConfiguration="XmlHttpBinding_IWcfPortal"
                contract="Csla.WcfPortal.IWcfPortal" name="XmlEndPoint" />
            <endpoint address="http://media.host.address/WcfPortal.svc"
                binding="basicHttpBinding" bindingConfiguration="BasicHttpBinding_IWcfPortal"
                contract="Csla.WcfPortal.IWcfPortal" name="MediaEndPoint" />
            <endpoint address="http://database.host.address/WcfPortal.svc"
                binding="basicHttpBinding" bindingConfiguration="BasicHttpBinding_IWcfPortal"
                contract="Csla.WcfPortal.IWcfPortal" name="DatabaseEndPoint" />
        </client>

This makes the code in Silverlight very straightforward, only the one line as usual in Application_Startup:

            Csla.DataPortal.ProxyTypeName =
                typeof (CustomWcfProxy<>).AssemblyQualifiedName;

Which then means that everything else is encapsulated in the CustomWcfProxy<T> class, I believe that's good OOP practice?:

    public static class CustomWcfProxy
    {
        private static string _defaultXmlEndPoint = "XmlEndPoint";
        private static string _defaultMediaEndPoint = "MediaEndPoint";
        private static string _defaultDatabaseEndPoint = "DatabaseEndPoint";

        public static string DefaultXmlEndPoint
        {
            get { return _defaultXmlEndPoint; }
            set { _defaultXmlEndPoint = value; }
        }
        public static string DefaultMediaEndPoint
        {
            get { return _defaultMediaEndPoint; }
            set { _defaultMediaEndPoint = value; }
        }
        public static string DefaultDatabaseEndPoint
        {
            get { return _defaultDatabaseEndPoint; }
            set { _defaultDatabaseEndPoint = value; }
        }
    }

    public class CustomWcfProxy<T> : WcfProxy<T> where T : IMobileObject
    {
        public CustomWcfProxy()
        {
            this.XmlDataPortalEndPoint = CustomWcfProxy.DefaultXmlEndPoint;
            this.MediaDataPortalEndPoint = CustomWcfProxy.DefaultMediaEndPoint;
            this.DatabaseDataPortalEndPoint = CustomWcfProxy.DefaultDatabaseEndPoint;
        }

        public string XmlDataPortalEndPoint { get; protected set; }
        public string MediaDataPortalEndPoint { get; protected set; }
        public string DatabaseDataPortalEndPoint { get; protected set; }

        protected override WcfPortalClient GetProxy()
        {
            var typeNamespace = typeof(T).Namespace;
            switch (typeNamespace)
            {
                case "Sample.Business.Xml.BusinessClasses":
                    return GetXmlProxy();
                case "Sample.Business.Media.BusinessClasses":
                    return GetMediaProxy();
                case "Sample.Business.Database.BusinessClasses":
                    return GetDatabaseProxy();
                default:
                    return base.GetProxy();
            }
        }

        private WcfPortalClient GetXmlProxy()
        {
            if (!string.IsNullOrEmpty(base.DataPortalUrl) && base.Binding != null)
            {
                return base.GetProxy();
            }
           
            return !string.IsNullOrEmpty(this.XmlDataPortalEndPoint)
                       ? new WcfPortalClient(this.XmlDataPortalEndPoint)
                       : new WcfPortalClient();
        }

... rinse and repeat for GetMediaProxy and GetDatabaseProxy.

Maybe there's an easier/better way though, that I've overlooked?

PeteH replied on Thursday, January 29, 2009

There is one alternative, of course: if there was an DefaultEndPoint property added to WcfProxy, and a matching DataPortalEndPoint in WcfProxy<T>, then I could move the switch into the constructor for my custom proxy class and do away with the need for the override. The GetProxy() of WcfProxy<T> would need to be rewritten slightly too of course, to reference this property before defaulting to just returning a new WcfPortal.WcfPortalClient(), so quite a few changes all in all.

Another one for the wish list, maybe?

RockfordLhotka replied on Thursday, January 29, 2009

So what you are saying is that I really need an EndPoint property to specify the name of the endpoint to read from the config file. And a DefaultEndPoint property in the non-generic WcfProxy class.

 

You are totally doing the right thing – I just don’t think it should be so hard to do something that is probably relatively common.

 

If I add an EndPoint (string) property, then you’d still create a subclass, but all you’d have to do is set the EndPoint property value as appropriate, and the standard WcfProxy<T> code would do the right thing?

 

Rocky

 

PeteH replied on Thursday, January 29, 2009

I do agree with you that this might be quite common, and that the overriding of GetProxy() is a little bit overkill - especially as the code for GetXmlProxy, GetMediaProxy etc are virtually identical to the GetProxy() in WcfProxy<T>, except for a check to see if the EndPoint is set (after checking to see if the DataPortalUrl is blank, because if it's not, I go down that path by calling base.GetProxy() anyway).

The changes wouldn't be drastic to WcfProxy.cs, I believe this is all that is needed (new additions in bold):

  public static class WcfProxy
  {

.. snipped code up to this point as it would be unchanged

    private static string _defaultUrl;
    public static string DefaultUrl
    {
      get { return _defaultUrl; }
      set { _defaultUrl = value; }
    }

    private static string _defaultEndPoint;
    public static string DefaultEndPoint
    {
      get { return _defaultEndPoint; }
      set { _defaultEndPoint = value; }
    }
  }

  public class WcfProxy<T> : IDataPortalProxy<T> where T : IMobileObject
  {
    public WcfProxy()
    {
      this.DataPortalUrl = WcfProxy.DefaultUrl;
      this.Binding = WcfProxy.DefaultBinding;
      this.EndPoint = WcfProxy.DefaultEndPoint;
    }

    public System.ServiceModel.Channels.Binding Binding { get; protected set; }
    public string DataPortalUrl { get; protected set; }
    public string EndPoint { get; protected set; }

    protected virtual WcfPortal.WcfPortalClient GetProxy()
    {
      if (!string.IsNullOrEmpty(this.DataPortalUrl) && this.Binding != null)
      {
        var address = new EndpointAddress(this.DataPortalUrl);
        return new WcfPortal.WcfPortalClient(this.Binding, address);
      }
      else if (!string.IsNullOrEmpty(this.EndPoint))
        return new WcfPortal.WcfPortalClient(this.EndPoint);

      else
        return new WcfPortal.WcfPortalClient();
    }

.. snipped code after this point as it would also be unchanged.
  }

Then any CustomWcfProxy<T> class would not need to override GetProxy(), it would simply look like (in my case):

    public class CustomWcfProxy<T> : WcfProxy<T> where T : IMobileObject
    {
        public CustomWcfProxy()
        {
            this.XmlEndPoint = CustomWcfProxy.DefaultXmlEndPoint;
            this.MediaEndPoint = CustomWcfProxy.DefaultMediaEndPoint;
            this.DatabaseEndPoint = CustomWcfProxy.DefaultDatabaseEndPoint;

            var typeNamespace = typeof(T).Namespace;
            switch (typeNamespace)
            {
                case "Sample.Business.Xml.BusinessClasses":
                    base.EndPoint = this.XmlEndPoint;
                case "Sample.Business.Media.BusinessClasses":
                    base.EndPoint = this.MediaEndPoint;
                case "Sample.Business.Database.BusinessClasses":
                    base.EndPoint = this.DatabaseEndPoint;
            }
        }

        public string XmlEndPoint { get; protected set; }
        public string MediaEndPoint { get; protected set; }
        public string DatabaseEndPoint { get; protected set; }
    }


RockfordLhotka replied on Thursday, January 29, 2009

Excellent! It sounds like we’re on the same page, so I’ll try and get this in before tomorrow’s 3.6.1 release.

 

Thank you for your help on this!

 

Rocky

PeteH replied on Thursday, January 29, 2009

No problem, happy to help - it would be great if it could make it in the 3.6.1 release. I rushed the custom class example, as the switch statement needs breaks after each case to compile, but it gives the general idea anyway..

RockfordLhotka replied on Thursday, January 29, 2009

Try the latest code in svn if you would. I believe we should be good now.

PeteH replied on Friday, January 30, 2009

Just to confirm that it works perfectly with the latest code in the svn. (However there is a bug in the svn in that the CommandBase.cs is linked from Csla.NET, and this doesn't build - I had to remove the link and build it from the old CommandBase.cs in Csla Light before I could get it to build)

Is the SyncrhonizedWcfProxy obsolete now, by the way? I only ask because it uses a private WcfProxy<T> variable, and is thus unusable with custom proxy classes.

Thanks again!

RockfordLhotka replied on Friday, January 30, 2009

Sorry about that bug. I was still in the middle of adding a LoadProperty() method to the ObjectFactory base class, so the svn code as a whole as in an indeterminate state. Obviously I think I have it correct now, as I've put RC1 of 3.6.1 online for download - so hopefully I caught everything :)
 
I'm not sure what to do with SynchronizedWcfProxy. We created it to work around a bug in WCF during the SL beta. Microsoft fixed that bug, so it isn't clear that SynchronizedWcfProxy has any value at this point. We left it in there in case we needed it again, but now it feels more like dead weight for me to maintain than anything else :)
 
I should probably just eliminate it now and save myself pain into the future.
 
Rocky

----- Original Message -----
From: PeteH
To: "rocky@lhotka.net"
Subject: Re: [CSLA .NET] RE: RE: Quick question/request re WcfProxy in Csla Light
Date: Fri, 30 Jan 2009 07:15:56 -0600

Just to confirm that it works perfectly with the latest code in the svn. (However there is a bug in the svn in that the CommandBase.cs is linked from Csla.NET, and this doesn't build - I had to remove the link and build it from the old CommandBase.cs in Csla Light before I could get it to build)

Is the SyncrhonizedWcfProxy obsolete now, by the way? I only ask because it uses a private WcfProxy<T> variable, and is thus unusable with custom proxy classes.

Thanks again!





Copyright (c) Marimer LLC