One Service acting as Client and Server

One Service acting as Client and Server

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


sjones posted on Tuesday, January 06, 2009

I need to have to have 2 different Libraries that use the Csla.    Library1.Libraries and Library2.Libraries.  They represent 2 different data stores. 

Service1 is the service portion of Library1.Libraries.

Service2 is the service portion of Library2.Libraries, and also the client for Library1.Libraries

ClientApplication is the client of Library1 and Library2

The problem was that Service2 was not able to use the Csla for both client and server purposes.  The only way I have been able to come close to making this scenario work, is to make a new copy of Csla and have Library1 reference Csla1 and Library2 reference Csla2.  This is not a good solution.

Is there a way to accomplish this scenario with just one copy of Csla?

Thanks!

RockfordLhotka replied on Tuesday, January 06, 2009

I'm not sure what you are saying?

You have

Client --talks to--> Service2 --talks to--> Service1

and you use the data portal between each layer?

It is true that this isn't directly supported. The data portal is designed to sit between Client and Server and that's all (with the exception of the Silverlight data portal, which is a bit different).

However, you can enhance the data portal to support this by creating your own data portal proxy. You can create a data portal proxy to run on Service2 that is smart enough to route calls to Library2 to LocalProxy and calls to Library1 to WcfProxy (or whatever remote proxy you are using).

This isn't terribly hard, because the data portal is entirely interface-based. Many people have created their own custom proxies for scenarios like this, or for other reasons.

rsbaker0 replied on Tuesday, January 06, 2009

If I read this right, you might be saying that you need to have two different data portals in the same application.

Csla won't do this out of the box, but it can be coaxed into it without too much trouble.

I had a similar requirement and made some slight modifications to CSLA so that it allows me to substitute a data portal proxy (or, in my case, just force some types through the local data portal) on a per type basis.

Basically, I added a static Event that you can subcribe to so that CSLA will fire the event each time it wants to get the data portal proxy for a particular type. You can either just force to the local portal or substitute an entirely new proxy if desired. The other related changes were that each call to GetDataPortalProxy to has to be modified to pass in the object type.

I used class attributes and a cache in my event handler to redirect certain types, but you could use whatever mechanism you want (e.g. assembly name, etc.).

Here is the relevant code in dataportal.cs:

#region DataPortal Proxy

public class GetDataPortalProxyEventArgs : EventArgs

{

internal GetDataPortalProxyEventArgs(Type objectType)

{

ForceLocal = false;

Proxy = null;

ObjectType = objectType;

}

public DataPortalClient.IDataPortalProxy Proxy {get; set;}

public bool ForceLocal {get; set;}

public Type ObjectType { get; protected set; }

}

public delegate void GetDataPortalProxyEventHandler(object sender, GetDataPortalProxyEventArgs e);

public static event GetDataPortalProxyEventHandler OnGetDataPortalEvent;

private static DataPortalClient.IDataPortalProxy _localPortal;

private static DataPortalClient.IDataPortalProxy _portal;

private static DataPortalClient.IDataPortalProxy GetDataPortalProxy(bool forceLocal, Type objectType)

{

// CSLA MODIFICATIONS

//

// Allow interception of GetDataPortalProxy call to either substitute a data portal proxy

// or force to local portal

if (!forceLocal && OnGetDataPortalEvent != null)

{

GetDataPortalProxyEventArgs args = new GetDataPortalProxyEventArgs(objectType);

OnGetDataPortalEvent(null, args);

if (args.Proxy != null)

return args.Proxy;

else

forceLocal = args.ForceLocal;

}

if (forceLocal)

{

if (_localPortal == null)

_localPortal = new DataPortalClient.LocalProxy();

return _localPortal;

}

else

{

if (_portal == null)

{

string proxyTypeName = ApplicationContext.DataPortalProxy;

if (proxyTypeName == "Local")

_portal = new DataPortalClient.LocalProxy();

else

{

string typeName =

proxyTypeName.Substring(0, proxyTypeName.IndexOf(",")).Trim();

string assemblyName =

proxyTypeName.Substring(proxyTypeName.IndexOf(",") + 1).Trim();

_portal = (DataPortalClient.IDataPortalProxy)

Activator.CreateInstance(assemblyName, typeName).Unwrap();

}

}

return _portal;

}

}

/// <summary>

/// Releases any remote data portal proxy object, so

/// the next data portal call will create a new

/// proxy instance.

/// </summary>

[EditorBrowsable(EditorBrowsableState.Advanced)]

public static void ReleaseProxy()

{

var disp = _portal as IDisposable;

if (disp != null)

disp.Dispose();

_portal = null;

}

#endregion

sjones replied on Thursday, January 08, 2009

Thanks so much for your help!  I looked into creating a custom Proxy, as "switchable" proxy.  I ran into a problem with that solution.  Since the Proxy is cached and the IDataPortalProxy member IsServerRemote is a property ( ie, cant accept parameters ), I couldnt figure a good way to determine which library was calling the proxy and thus wether to use Local or Wcf proxy.

I then implemented rsbaker0's event based solution.  This worked perfectly and was very little work.  At first I wasnt sure where to put the eventhandler for OnGetDataPortalEvent.  I ended up putting it in my hosting application ( ie Service2 ).  Thus the Libraries would act with a local vs wcf based on what host application they were a member of.

This is the eventhandler code that I placed in Service2

WcfProxy wcfProxy = null;

LocalProxy localProxy = null;

void DataPortal_OnGetDataPortalEvent(object sender, DataPortal.GetDataPortalProxyEventArgs e)

{

if (e.ObjectType.ToString().Contains("Library2.Libraries"))

{

if (localProxy == null)

localProxy = new LocalProxy();

e.Proxy = localProxy;

}

else

{

if (wcfProxy == null)

wcfProxy = new WcfProxy();

e.Proxy = wcfProxy;

}

}

 

I also had to place an eventhandler in Client:

WcfProxy Library1Proxy = null;

WcfProxy  Library2Proxy = null;

void DataPortal_OnGetDataPortalEvent(object sender, DataPortal.GetDataPortalProxyEventArgs e)

{

if (e.ObjectType.ToString().Contains("Library2.Libraries"))

{

if (Library2Proxy == null)

Library2Proxy = new WcfProxy("Library2WcfDataPortal");

e.Proxy = Library2Proxy;

}

else

{

if ( Library1Proxy == null)

 Library1Proxy = new WcfProxy("Library1WcfDataPortal");

e.Proxy =  Library1Proxy;

}

}

From Client App.Config I added another endpoint under the Client Section:

 

<endpoint address="net.tcp://localhost:6050/Library2/" binding="netTcpBinding" bindingConfiguration="NetTcpBinding" contract="Csla.Server.Hosts.IWcfPortal" name="Library2WcfDataPortal" />

<endpoint address="net.tcp://localhost:6242/Library1/" binding="netTcpBinding" bindingConfiguration="NetTcpBinding" contract="Csla.Server.Hosts.IWcfPortal" name="Library1WcfDataPortal" />

 

Thanks so much Again for the help!

Summer

Copyright (c) Marimer LLC