Remoting without a hitch...almost...

Remoting without a hitch...almost...

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


NightOwl888 posted on Saturday, July 19, 2008

Although I have been working with CSLA for a few years now, I just made my first attempt at using the remoting configuration. I am still using CSLA version 1.51 that was ported over to .NET 2.0.

I had a sample project that someone else created, so setting up the project and getting it to function was a breeze.

However, since I wasn't creating my CSLA objects with remoting in mind, I discovered several issues with the way my shared Get and New methods were created - namely, they don't always call the server side code when appropriate.

I started going through my classes and cleaning them up - mostly it was just a matter of figuring out whether the code was supposed to run client side or server side. Then I ran into a snag.

I have a private method within one of my classes that instantiates a discount object and then uses it to determine the discount amount. This class requires a call to the database to determine whether the discount is still valid. Unfortunately, the private method that calculates the discount amount is called sometimes on the client and sometimes on the server.

I know this isn't great architecture, but I have code that functions and I don't want to change it so much that it will require a lot of analysis and testing. Is there a way to determine wether the current code is executing within the DataPortal so I can make a conditional GetInstance() method in this case?

rsbaker0 replied on Sunday, July 20, 2008

Does CSLA 1.5 have CommandBase objects? If so, why not just use a simple CommandBase-derived class nested in the class that implements the private method to calculate the discount.? It will handle the proper execution location regardless of whether you call it on the client or the server.

(I think I have some code that detects whether you are on the client side of the portal, but I'm using CSLA 3.0, so I'm not sure how much has changed. I'll post what I'm using onMonday)

NightOwl888 replied on Monday, July 21, 2008

I just checked, and my CSLA version does have a command base, but it is not in the original BO book. This functionality must have been added sometime after version 1.0.

I am in the process of reading the 2.0 book now, but as of this moment I don't know how to use command base. Can you provide a sample?

rsbaker0 replied on Monday, July 21, 2008

Here's the code I use to detect whether I'm on the client side of a remoting data portal. I'm not sure if it's completely kosher, but it seems to work: (In my case, I do this to screen out inappropriate calls to functions from the client.)

I'll see what I can find in terms a of a simple CommandBase.

public static void NotAllowedOnClient()

{

if (ApplicationContext.DataPortalProxy != "Local" &&

ApplicationContext.ExecutionLocation == ApplicationContext.ExecutionLocations.Client)

{

throw new NotSupportedException("Illegal client method call with remote server portal in use");

}

}

rsbaker0 replied on Monday, July 21, 2008

Here is a generic base class I  wrote that derives from CommandBase: I use it to execute arbitrary SQL statements (including fetching of scalar values). You have to derive a class from it, though, and then you just override DataPortal_Execute.

(You can leave out the database key stuff -- we have up to 3 different databases we might be connected to, so all of our infrastructure has to be aware of which one to execute any command or fetch operation against)

The general idea of CommandBase is to just remote the CommandBase-derived object itself over to the server, do some work on the server side, and then bring the object back with the result in a class member. If you're already on the server, then it uses the local data portal. You don't really have to be aware of it.

For convenience, you usually provide a static factory invoker, so in your case the actual invocation might look like:

decimal discount = DiscountCommand.GetCurrentDiscount(...)

This factory method would take whatever parameters are required and assign them to members of the DiscountCommand. The DataPortal_Execute method, which runs on the server, uses these member values to retrieve your discount, which is then put in a separate member as a result. When the DiscountCommand object comes back from the data portal, you just return the result value.

    [Serializable()]
    public class SQLCommand<T, V> : CommandBase where V : SQLCommand<T, V>
    {
        protected string _sql;
        protected T _result;
        protected string _databaseKey;

        protected SQLCommand()
            : this(String.Empty, "Base")
        {
        }
 
        protected SQLCommand(string sql, string databaseKey)
        {
            _sql = sql;
            _databaseKey = databaseKey;
        }

        public static T Execute(string sqlText)
        {
            return Execute(sqlText, "Base");
        }

        public static T Execute(string sqlText, string databaseKey)
        {
            // Must retrieve via data portal
            V result;
#if DEBUG
            Type t = typeof(V);
            string typeName = typeof(V).Name;
#endif

            V command = Activator.CreateInstance(typeof(V),true) as V;
            command._databaseKey = databaseKey;
            command._sql = sqlText;
            result = DataPortal.Execute<V>
              (command);
            return result._result;
        }

        protected override void DataPortal_Execute()
        {
            throw new System.NotImplementedException("Method DataPortal_Execute() must be implemented in derived class");
        }
    }

Copyright (c) Marimer LLC