LocalProxy vs WcfProxy behavioral differences

LocalProxy vs WcfProxy behavioral differences

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


lwmorris posted on Wednesday, May 20, 2009

I believe there is a problem with LocalProxy.

Expected behavior:

Using the WcfProxy, AddInstanceAuthorizationRules fires when a BusinessObject is first created and upon deserialization. This is nice because after an object is saved, I can query CanExecute, CanReadProperty, CanWriterProperty and they reflect the authorization rules defined in the business object. I expect the same behavior when using LocalProxy.

Actual behavior:

The AddInstanceAuthorizationRules fires when a BusinessObject is first created and when the object is cloned. Unfortunately, the AddInstanceAuthorizationRules appears to fire prior to the property data being populated. As a result, any logic in the AddInstanceAuthorizationRules is useless.

For example, c.CanWriteProperty(“Name”) correctly return false when using the WcfProxy, but true when using the LocalProxy.
Class1 c = new Class1();
c.Name="Larry";
c = c.Save();

//I expect this to return false.
System.Diagnostics.Debug.WriteLine(c.CanWriteProperty("Name"));

Work around:

I had to override the Save method in my base object.

public override T Save()
{
T saved = base.Save();
T clone = saved.Clone();
return clone;
}


And add a call to AddInstanceAuthorizationRules() prior to exiting DataPortal.Fetch methods.

Example code business class:

using System;
using Csla;

namespace ClassLibrary1
{
[Serializable]
public class Class1 : BusinessBase
{
private static PropertyInfo NameProperty = RegisterProperty(new PropertyInfo("Name", "Name"));
public string Name
{
get { return GetProperty(NameProperty); }
set { SetProperty(NameProperty, value); }
}

protected override void AddInstanceAuthorizationRules()
{
// Don't let anyone change the Name property after the object has been saved.
if (!this.IsNew)
AuthorizationRules.InstanceAllowWrite(NameProperty.Name, Guid.Empty.ToString());
}


[RunLocal]
protected override void DataPortal_Create()
{
}

protected override void DataPortal_Insert()
{
}

protected override void DataPortal_Fetch(object criteria)
{
LoadProperty(NameProperty, "Larry");
}
}
}


RockfordLhotka replied on Wednesday, May 20, 2009

I suspect, without digging into this, that the issue may be that the WcfProxy uses the NetDataContractSerializer, while (by default) LocalProxy (and Clone() and the rest of CSLA) uses the BinaryFormatter.

While these two serializers are nearly the same, there are some subtle differences, and it is possible that the NDCS invokes the OnDeserialized() method later in the process than the BF.

You could try switching CSLA to always use the NDCS - it is a config option named CslaSerializationFormatter that goes in appSettings. Valid options are BinaryFormatter and NetDataContractSerializer

<add key="CslaSerializationFormatter" value="NetDataContractSerializer" />

I could be totally wrong too - but this is an easy thing for you to try.

lwmorris replied on Thursday, May 21, 2009

Adding the CslaSerializationFormatter didn't make any difference.

System.Diagnostics.Debug.WriteLine(c.CanWriteProperty("Name"));

Should follow IsNew. c=c.Save() still tells me I'm allowed to write to name where as c=c.Save().Clone() tells me I'm not.

It seems that when using the WcfProxy, the OnDeserialize calls InitializeAuthorizationRules, which works. Calling Clone() ends up doing the same. LocalProxy only call InitializeAuthorizationRules when the object is created. It needs to do it again after the object's data has been populated.


Thanks for any help!

RockfordLhotka replied on Thursday, May 21, 2009

It was an idea :)

 

I don’t have time to dig into this now, or probably in the near future.

 

Like it or not, the LocalProxy and remote proxies just don’t work the same way. The remote proxies serialize your object graph twice – once on the way to the server, once on the way back. LocalProxy serializes the object graph once, so the “server-side” code operates on a clone. That is probably the issue you are facing.

 

One design goal for LocalProxy is performance. It tries, as much as possible, to cut overhead so the 1- and 2-tier scenarios really are faster than 3- and 4-tier scenarios. This design goal is higher in priority than complete functional parity with the remote proxies.

 

Fortunately the data portal proxy object is a pluggable component. You can create your own. So you could create a copy of LocalProxy in your project and do a second clone after the “server-side” update to force a serialization/deserialization process after the “server-side” code is complete.

 

I suspect most people wouldn’t want to take the perf hit for that, but if your functionality requirements need that to occur, then it may be worth it for you.

 

Rocky

lwmorris replied on Thursday, May 21, 2009

Ok. We'll simply copy the existing LocalProxy then do something like:

public DataPortalResult Update(object obj, DataPortalContext context)
{
ICloneable obj = _portal.Update(obj, context);
return new DataPortalResult(obj.Clone());
}

Thank you for help.

Copyright (c) Marimer LLC