Sync Rule in Server and Async in Client

Sync Rule in Server and Async in Client

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


Ranjini posted on Friday, March 18, 2011

CSLA 4/ Silverlight 4

I have a "IsExists" rule to check for duplicate object with the same name. This rule was initially set up as an Async rule that triggers a command object.  I am trying to modify it , so it runs async ONLY if it is executed on client/silverlight. I am not having much success. Can someone fill  in the blanks for me ?

Here is the code -

  public class DuplicateRule : Csla.Rules.BusinessRule

        {

            public Csla.Core.IPropertyInfo IdProperty { get; set; }

 

            public DuplicateRule (Csla.Core.IPropertyInfo primaryProperty, Csla.Core.IPropertyInfo idProperty)

                : base(primaryProperty)

            {

 

                IsAsync = (Csla.ApplicationContext.LogicalExecutionLocation != ApplicationContext.LogicalExecutionLocations.Server);

                IdProperty = idProperty;

                InputProperties = new List<Csla.Core.IPropertyInfo>();

                InputProperties.Add(primaryProperty);

                InputProperties.Add(idProperty);

 

            }

            protected override void Execute (Csla.Rules.RuleContext context)

            {

                var name = (string)context.InputPropertyValues[PrimaryProperty];

                var Id = (Guid)context.InputPropertyValues[IdProperty];

                if (IsAsync)

                {

                    DomainExists command = new DomainExists(name, Id);                   

                    DataPortal<DomainExists> dpCmd = new DataPortal<DomainExists>();

                    dpCmd.ExecuteCompleted += (o, e) =>

                    {

                        if (e.Error != null)

                            context.AddErrorResult(e.Error.Message);

                        else if (e.Object.IsExists)

                            context.AddErrorResult("This domain name already exists.");

                        context.Complete();

 

                    };

                    dpCmd.BeginExecute(command);

                }

                else

                {

                    …What goes here….

                }

 

            }

        }

Thanks

Ranjini

 

JonnyBee replied on Sunday, March 20, 2011

Since Silverlight has async methods only you probably need to add a compiler directive so the sync code only runs in .NET.

You should test for ExecutionLocation (and not LogicalExecutionLocation) and the rule should look like this:

  public class DuplicateRule : Csla.Rules.BusinessRule

        {

            public Csla.Core.IPropertyInfo IdProperty { get; set; }

 

            public DuplicateRule (Csla.Core.IPropertyInfo primaryProperty, Csla.Core.IPropertyInfo idProperty)

                : base(primaryProperty)

            {

 

                IsAsync = (Csla.ApplicationContext.ExecutionLocation != ApplicationContext.ExecutionLocations.Server);

                IdProperty = idProperty;

                InputProperties = new List<Csla.Core.IPropertyInfo>();

                InputProperties.Add(primaryProperty);

                InputProperties.Add(idProperty);

 

            }

            protected override void Execute (Csla.Rules.RuleContext context)

            {

                var name = (string)context.InputPropertyValues[PrimaryProperty];

                var Id = (Guid)context.InputPropertyValues[IdProperty];

                if (IsAsync)

                {

                    DomainExists command = new DomainExists(name, Id);                   

                    DataPortal<DomainExists> dpCmd = new DataPortal<DomainExists>();

                    dpCmd.ExecuteCompleted += (o, e) =>

                    {

                        if (e.Error != null)

                            context.AddErrorResult(e.Error.Message);

                        else if (e.Object.IsExists)

                            context.AddErrorResult("This domain name already exists.");

                        context.Complete();

 

                    };

                    dpCmd.BeginExecute(command);

                }

                else

                {

                    // use sync dataportal

#if !SILVERLIGHT

                    DomainExists command = new DomainExists(name, Id);                   

                    DataPortal<DomainExists> dpCmd = new DataPortal<DomainExists>();

                    dpCmd.Execute(command);

                    if (command.IsExists)

                        context.AddErrorResult("This domain name already exists.");

#endif

                }

 

            }

        }

Ranjini replied on Sunday, March 20, 2011

Thanks for your response..

I did try that but it gives me this exception

 'Csla.DataPortal<DomainExists>' does not contain a definition for 'Execute' and no extension method 'Execute' accepting a first argument of type 'Csla.DataPortal<DomainExists>' could be found (are you missing a using directive or an assembly reference?) 

DomainExists is a CommandBase object with a dataportal_execute implemented so..

 protected override void DataPortal_Execute()        {

            using (var dalFactory = DAL.DalFactory.GetManager())
            {
                var dal = dalFactory.GetProvider<DAL.IDomainDal>();
                using (var dr = new SafeDataReader(dal.Fetch((new DomainCriteria() { Name = Name}).StateBag)))
                {
                    while (dr.Read())
                    {
                        if (dr.GetGuid("Id") != Id)
                        {
                            IsExists = true;
                            break;
                        }

                    }
                }
            }

        }

 

JonnyBee replied on Sunday, March 20, 2011

Try this:

#if !SILVERLIGHT

                    DomainExists command = new DomainExists(name, Id);                                    

                    command = DataPortal.Execute<DomainExists>(command);

                    if (command.IsExists)

                        context.AddErrorResult("This domain name already exists.");

#endif

 

 

 

 

Ranjini replied on Sunday, March 20, 2011

Thanks Jonny.. That did compile. However, can you explain to me

1) The usecases for LogicalExecutionLocation vs ExecutionLocation

In my case , when I change it to ExecutionLocation, since it is returned as "Client" , it takes the async path .. What I am trying to do here is write a test method to exercise this rule on the server side and I dont want any async calls made.

2) Why DataPortal.Execute gives me a compile error?

Please help!

 

ajj3085 replied on Sunday, March 20, 2011

1.  ExecutionLocation returns Client or Server depending on whether the code is actually executing on the client or server.  If you're using the local data portal, this will ALWAYS return Client since there is no server tier.  Logical returns Client or Server based on whether you're logically on the client or server.  For for local data portal, if you're actually executing code "in the data portal" it will return server.  Otherwise it will return client.

2.  DataPortal.Execute static method is only available in .Net.  In Silverlight, only the DataPortal instance member exist.  Remember, code in Silverlight is a different runtime based on .Net 2.  The normal .Net runtime is what most people are used to.

Ranjini replied on Sunday, March 20, 2011

Thanks Andy.. I get (2), but I am still a little unclear on (1) and that may just be because I am not a CSLA pro... My solution is primarily a silverlight application, with a silverlight project, a business.client and a business.server set up. I am now in the process of testing JUST business.server and I started this conversation thread when I realised i cannot do this test unless i redid all my "IsExists" rules to handle client and server differently (async on client and sync on server).

When debugging the test project, I am noticing that when I try to run this rule(by changign the value of the primary property), it shows my executionlocation as "Client"  but my logicalexecutionlocation as "Server". I am having a hard time wrapping my head around why this might be.

Thanks again for taking the time...

JonnyBee replied on Sunday, March 20, 2011

If you use LogicalExecutionLocation your rule will be async or sync depending on whether the first instance is created in a DataPortal call or directly on the client. When using Local DataPortal your code is running in the same instance and should not try to switch between client/server.

ExecutionLocation is Server if the DataPortal has IsRemoteDataPortal == true.

LogicalExecutionLocation is Server when you are in a DataPortal call

So in your case - ExecutionLocation is Client because you are running in the client instance and logical execution location is Server because you code is executing in the "serverside" of the DataPortal.

 

Ranjini replied on Monday, March 21, 2011

In which case then I should change my rule constructor to say

   public DuplicateRule (Csla.Core.IPropertyInfo primaryProperty, Csla.Core.IPropertyInfo idProperty)

            : base(primaryProperty)

        {

            IsAsync = (Csla.ApplicationContext.ExecutionLocation == ApplicationContext.ExecutionLocations.Silverlight);

            IdProperty = idProperty;

            InputProperties = new List<Csla.Core.IPropertyInfo>();

            InputProperties.Add(primaryProperty);

            InputProperties.Add(idProperty);

        }

Would that be correct?

 

 

Thanks much for taking the time to answer my questions

Ranjini

 

 

 

JonnyBee replied on Tuesday, March 22, 2011

For your app it would work fine, however for a more general solution I'd prefer:

IsAsync = (Csla.ApplicationContext.ExecutionLocation != ApplicationContext.ExecutionLocations.Server);

Copyright (c) Marimer LLC