Split up that Business Layer

Posts   
 
    
Ian avatar
Ian
User
Posts: 511
Joined: 01-Apr-2005
# Posted on: 30-Mar-2009 04:03:23   

Hi there,

Here're a couple of methods from a single class in my Business layer:


private AccountAssignmentEntity AssignDetailToAccount(ChaletPaymentEntity paymentEntity)
{
            var adapter = new DataAccessAdapter();

            var assignmentEntity = new AccountAssignmentEntity();
            assignmentEntity.OrderDetailId = paymentEntity.OrderDetailId;

            try
            {
                adapter.StartTransaction(IsolationLevel.ReadCommitted, "trans1");

                AccountEntity accountEntity = GetUnassignedAccount(adapter, paymentEntity.Berth);

                assignmentEntity.Account = accountEntity;

                adapter.SaveEntity(assignmentEntity, true);

                adapter.Commit();

                return assignmentEntity;
            }
            catch (Exception exc)
            {
                adapter.Rollback();

                var logFile = new LogFile();
                logFile.WriteExceptionToLogFile(exc);
            }
            finally
            {
                adapter.CloseConnection();
            }

            return null;
}

public EntityCollection<VwDaysSinceDepositEntity> GetAccountWithOrderDueABalanceReminder(int[] daysRemaining)
{
            var collection = new EntityCollection<VwDaysSinceDepositEntity>();

            var adapter = new DataAccessAdapter();

            var bucket = new RelationPredicateBucket();
            bucket.PredicateExpression.Add(VwDaysSinceDepositFields.DaysSinceDeposit == daysRemaining);
            bucket.PredicateExpression.Add(VwDaysSinceDepositFields.LastEmailDate < DateTime.Today);

            adapter.FetchEntityCollection(collection, bucket);

            return collection;
}

Now I think there's a clear difference between these methods. The first one does more sophisticated work - it takes a 'paymentEntity' instance and assigns it to an un-assigned account (which it has to look up) and does all this within a transaction.

The second method is a bog standard query -_ select this where this _etc...

I've got loads of the second type and a few of the first. The trouble with this second type of method is that they're very verbose and dumb and they clutter classes up which are otherwise doing more sophisticated and higher level things. Also, this second type are typically consumed by the first type.

So the question is, shouldn't the two types of method be seperated in some way? Perhaps in different tiers?

simmotech
User
Posts: 1024
Joined: 01-Feb-2006
# Posted on: 30-Mar-2009 06:27:58   

Hi Ian

(I'm from Herfordshire too!)

For the simple fetches, why not have a couple of generic helper methods, say one for retrieving a single entity and one for a collection. You can do just about everything with the predicates, prefetch paths and ExcludeIncludeFieldsLists.

I got a bit tired of our old pre-LLCoolJ service-based write-a-new-method-and-wrappers-for-everything architecture, so I wrote an IDirectDataService with a few simple methods like this:-

EntityCollection<T> FetchCollection<T>(string processName, IPredicate filterToUse) where T: EntityBase2;
EntityCollection<T> FetchCollection<T>(string processName, CollectionFetchParameters parameters) where T: EntityBase2;

Now the client side can fetch anything it likes and I don't have to change any interfaces!

Here is an example of the server side:

public EntityCollection<T> FetchCollection<T>(string processName, CollectionFetchParameters parameters) where T: EntityBase2
{
    Guard.NotNull(processName, "processName");
    Guard.NotNull(parameters, "parameters");

    var result = new EntityCollection<T>();

    using(var scope = new TransactionScopeWrapper("DirectDataService:" + processName))
    {
        var existingTimeout = scope.DataAccessAdapter.CommandTimeOut;
        if (parameters.CommandTimeOut != null)
        {
            scope.DataAccessAdapter.CommandTimeOut = parameters.CommandTimeOut.Value;
        }

        scope.DataAccessAdapter.FetchEntityCollection(result, parameters.FilterBucket, parameters.MaxNumberOfItemsToReturn,
                                                                                                    parameters.SortExpression, parameters.PrefetchPath,
                                                                                                    parameters.ExcludeIncludeFieldsList, parameters.PageNumber, parameters.PageSize);

        if (parameters.CommandTimeOut != null)
        {
            scope.DataAccessAdapter.CommandTimeOut = existingTimeout;
        }

        scope.Complete();
    }

    return result;
}

Cheers Simon

Ian avatar
Ian
User
Posts: 511
Joined: 01-Apr-2005
# Posted on: 30-Mar-2009 17:36:40   

Hi simmotech,

(St. Albans here!)

That's a nice factorization of the fetching code, but its the - parameter specifying bulk - (i.e. the predicates, pre-fetches and field lists) that I want to hide away somewhere. confused

My hunch is that if I were using stored procedures instead of LLBLGen then that's where this code would be stored. Then my business layer would only contain the higher level stuff.

Ian.