Calling Object's DataPortal methods with different Criteria

Calling Object's DataPortal methods with different Criteria

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


ballistic posted on Thursday, January 28, 2010

I have a LoginHistory object where I need to call different fetch methods into the it's repository.

Previously, when my data access was included in my object, I had a private class for each of the criteria so that the DataPortal knew which method to call, based on the criteria type.

Now that I have a separate class handle the data access I need to define a public class for each of the criteria so that the DataPortal can again know which of the different Fetch methods to call.  While this seems to be the correct way of doing this, it does expose details which previously were kept private.

In my object I have:

        public class Item_MostRecent_byMemberId_Criteria
        {
            public int MemberId { get; private set; }
            public Item_MostRecent_byMemberId_Criteria(int MemberId)
            {
                this.MemberId = MemberId;
            }
        }

        protected void DataPortal_Fetch(DomainObjects.Members.LoginHistory.Item_MostRecent_byMemberId_Criteria criteria)
        {
            using (var dalManager = IoC.Resolve<Repository.IDalManager>())
            {
                var dal = IoC.Resolve<Repository.Members.ILoginHistoryRepository>();
                using (var dr = new SafeDataReader(dal.Fetch(criteria)))
                {
                    if ( dr.Read())
                    {
                        GetItems(dr);
                    }
                }
            }
        }

In the repository I have:
public IDataReader Fetch(DomainObjects.Members.LoginHistory.Item_MostRecent_byMemberId_Criteria credentials)

And in the service layer I have:
        public DomainObjects.Members.ILoginHistory FindMostRecentByMemberId(int MemberId)
        {
            if (MemberId.IsId())
            {
                return DataPortal.Fetch<DomainObjects.Members.LoginHistory>(new DomainObjects.Members.LoginHistory.Item_MostRecent_byMemberId_Criteria(MemberId));
            }
            return DataPortal.Create<DomainObjects.Members.LoginHistory>();   //If parameter is not valid, return new instance of object
        }

This all works fine, and the correct methods are being called, I'm just not liking the public criteria class that I have defined in the object.  Is this the "correct" way to do this? Are there any other ways to do this (using the service, object and repository)?

Thank you,

 - Andres

RockfordLhotka replied on Friday, January 29, 2010

I don't think about it that way, and I think what you are suggesting breaks layer boundaries.

If you are going to have a separate DAL, then you need to have a clearly defined interface between the business layer and data layer. Types should not span that boundary, because that couples the two layers in a bad way.

The ideal solution from a decoupling and maintanability perspective is to have three assemblies (in concept):

  1. Business assembly (contains your business objects, including criteria objects because they are part of your logical business domain)
  2. Data access assembly (contains concrete implementation of the DAL contract/interface)
  3. Data access contract assembly (contains interface definitions for DAL types, and DTO types for data interchange between the business layer and data layer)

The business assembly references the contract assembly. The data access assembly references the contract assembly. The business layer doesn't reference the data layer, and the data layer doesn't reference the business layer. They are decoupled and can now version independently.

The DAL is now pluggable (via repository or DI patterns) because it is completely interface based. The data flowing between the business and data layers always goes through the DTO types, which are the "data contract" between the two layers - again providing clean decoupling of the layers.

So your crtiera types flow through the business layer and data portal, but they shouldn't flow to the DAL, because the DAL shouldn't have access to those types.

You can see an example of what I'm talking about in the sample code from the CSLA .NET Core 3.8 video series. I think I have two different examples that follow this model if I remember right.

ballistic replied on Friday, January 29, 2010

Thinking about it again, it does look like I do not need the repository to know about the type. For some reason I thought the method in the repository had to be called "Fetch" but just have a different criteria parameters.  However, looking at the examples, it looks like I can give it any name I want and from the business object's DataPortal_Fetch (which does take a criteria parameter) I can call the repository's fetch method and pass in each parameter (which doesn't need to be a criteria type).

Also, in order to keep the Criteria class only known to the business object, I will need a non-static method that makes a call to the DataPortal's fetch method. Previously, I had the service layer call the data portal's fetch method directly, which is what was requiring me to have to know the details about the criteria class.

Again, this is the first time I design my project using interfaces, so this is new to me.

Thank you,

 - Andy

Copyright (c) Marimer LLC