Derived Entity Behaviors and the Manager Model

Posts   
 
    
jyoung
User
Posts: 33
Joined: 27-Feb-2006
# Posted on: 03-Apr-2007 23:52:30   

What is the best/good way to apply custom behavior to a derived entity in the manager model?

Say ProductAEntity and ProductBEntity both derive from abstract Product. ProductAEntity and ProductBEntity have different discontinued behaviors.


   public abstract class ProductEntity
    {
        // generic Product data
    }

    public class ProductAEntity : ProductEntity 
    {
        // ProductAEntity data
    }

    public class ProductBEntity : ProductEntity
    {
        // ProductBEntity data
    }

With a domain model I could just make an abstract discontinue method on Product and force ProductA and ProductB to implement it and everything would be fine. However, I am confused on how to best do this in a manager model.

The best solution I have come up with so far is to have the ProductManager supply a discontinue method and take in a Product. Then determine what type of product it is and call an appropriate object or method.


    public class ProductManager
    {
        public void DiscontinueProduct(ProductEntity product)
        {
            IDiscontinueStrategy strategy;
            
            // what is the best way to model behavior for different entities ?????

            if (product.GetType() == typeof(ProductAEntity))
            {
                strategy = new DefaultDiscontinueStrategy();
            }
            else if (product.GetType() == typeof(ProductBEntity))
            {
                strategy = new CustomDiscontinueStrategy();
            }
            else
            {
                throw new ApplicationException("Unknown Product type, cannot create discontinue strategy!");
            }

            strategy.Discontinue(product);
        }
    }


    public interface IDiscontinueStrategy
    {
        void Discontinue(ProductEntity product);
    }

    public class DefaultDiscontinueStrategy : IDiscontinueStrategy
    {
        #region IDiscontinueStrategy Members

        public void Discontinue(ProductEntity product)
        {
            throw new Exception("The method or operation is not implemented.");
        }

        #endregion
    }

    public class CustomDiscontinueStrategy : IDiscontinueStrategy
    {
        #region IDiscontinueStrategy Members

        public void Discontinue(ProductEntity product)
        {
            throw new Exception("The method or operation is not implemented.");
        }

        #endregion
    }

What I don’t like about this is the if-else statements to determine the product type, but I may just have to live with it.

How do you handle these situations?

Thanks, Joe

Otis avatar
Otis
LLBLGen Pro Team
Posts: 39749
Joined: 17-Aug-2003
# Posted on: 09-Apr-2007 16:44:45   

Manager classes act like stateless static code bases. So you call a method on a manager class and you then use the result of the method in another method or piece of code.

There's nothing wrong with adding the discontinued method to the abstract entity though. However you can also create 2 method overloads in your manager class which both accept one of the product types you have and act upon them, returning different results. So for example:

bool isDiscontinued = MyProductManager.CheckDiscontinued(_product);

and _product can be either a ProductAEntity or a ProductBEntity.

Frans Bouma | Lead developer LLBLGen Pro
jyoung
User
Posts: 33
Joined: 27-Feb-2006
# Posted on: 11-Apr-2007 17:04:26   

Hi Frans, Thanks for the reply.

I have toyed with the two methods on the manager but the problem I run into is that the code using the manager does not know the product type.

It simply has an Id so it declares a base type variable and fetches the product into it using the FetchNewEntity method on the DataAccessAdapter.

If the manager just has two methods that accept a ProductAEntity or a ProductBEntity I get a compiler error saying that it cannot convert ProductEntity into ProductAEntity.


// assuume productId has been passed in to the controller
Guid productId = new Guid("9508b6da-e6c5-453d-99c4-5f23d37d5aa8");

ProductEntity product = ProductManager.FetchProduct(productId);

ProductManager.DiscontinueProduct(product);


public sealed class ProductManager
{
        public static void DiscontinueProduct(ProductAEntity product)
        {
            // Do ProductA specific discontinue here
        }

        public static void DiscontinueProduct(ProductBEntity product)
        {
            // Do ProductB specific discontinue here
        }

        public static ProductEntity FetchProduct(Guid productId)
        {
            ProductEntity product;

            IRelationPredicateBucket predicateBucket = new RelationPredicateBucket();
            predicateBucket.PredicateExpression.Add(ProductFields.ProductId == productId);

            using (DataAccessAdapter adapter = new DataAccessAdapter())
            {
                // if we use adapter.FetchEntity it is not polymorphic so we will just get a ProductEntity
                // adaper.FetchNewEntity is polymorphic so we can declare the base type and get back the concrete type

                product = adapter.FetchNewEntity(new ProductEntityFactory(), predicateBucket) as ProductEntity;
            }

            return product;
        }
}

In order to make the code compile I would have to manually cast the product to ProductA or ProductB and then send it off to the Discontinue method. Now I can put that check in the Manager and just accept the ProductEntity. This is what we do now, accept a base type determine the concrete type and pass it on to the appropriate method or object.

Now we may be doing something completely wrong here, I don't know, that is why I am trying to find out how other folks are doing this.

jyoung
User
Posts: 33
Joined: 27-Feb-2006
# Posted on: 14-Apr-2007 14:34:04   

Having no luck trying to figure out the best way to handle this in a Manager Model, I have started exploring how to use LLBLGen in a Domain Model.