Method definition question for IStartEnd in abstract class

Method definition question for IStartEnd in abstract class

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


jspurlin posted on Wednesday, December 13, 2006

I have a base class that needs to implement IStartEnd.  The database stores addresses in child tables:

AddressTable - stores all addresses
OfficeAddressTable - links OfficeTable to AddressTable
PersonAddressTable links PersonTable to AddressTable

Address itself is a base class, so I thought I might uses a base class for Office Address Assignements:

[Serializable()]
    public abstract class BaseAddressAssignment<T>
        : BusinessBase<T>where T : BaseAddressAssignment<T>


I need to implement IStartEnd to something like this effect:

[Serializable()]
    public abstract class BaseAddressAssignment<T>
        : BusinessBase<T>where T : BaseAddressAssignment<T>, IStartEnd

Or this?

[Serializable()]
    public abstract class BaseAddressAssignment<T>
        : BusinessBase<T>, IStartEnd where T : BaseAddressAssignment<T>, IStartEnd

The problems is how I now define the StartEndDate validation implementation as used in the project tracker. If I use:

private static bool StartDateGTEndDate<T>(
      T target, Csla.Validation.RuleArgs e) where T : IStartEnd


The parameter type T has the same name for outer type.  This makes sense.

So here is my question: How do I define an abstract base class that will implement IStartEnd?

I also noticed that I will have to do something different when adding Business Rules:

ValidationRules.AddRule<BaseAddressAssignment>(
              StartDateGTEndDate<BaseAddressAssignment>, "Started");

In this case, there error message is:  The generic type... requires '1' type argument.

Since I do not have my definitions correctly stated though, I cannot say whether or not this is simply a side effect of an incorrect definition.

Thank you.




xal replied on Wednesday, December 13, 2006

OK, quick and (very) dirty sample:


    public interface IStartEnd
    {
        DateTime Date { get; set; }
    }

    public class BaseAddressAssignment<T>
       : Csla.BusinessBase<T>, IStartEnd where T : BaseAddressAssignment<T>

    {

        protected override void AddBusinessRules()
        {
            base.AddBusinessRules();
            ValidationRules.AddRule<IStartEnd>(
                CustomRules.StartDateGTEndDate<IStartEnd>,
                "Date");
        }


        public DateTime Date
        {
            get { return DateTime.Now; }
            set { }
        }
    }

    public class CustomRules
    {
        public static bool StartDateGTEndDate<T>(T target, Csla.Validation.RuleArgs e)
            where T : IStartEnd
        {
            return true;
        }
    }

Is that what you where trying to do? or did I get it completely wrong?

Andrés

xal replied on Wednesday, December 13, 2006

I think I get what you mean now... if you don't want to use the interface as a generic param (for whatever reason), you just specify the current type.
Now, you must be thinking, BaseAddressAssignment IS my current type, but no it's not.
As your class definition states, the type for the current class is BaseAddressAssignment<T>, so you should add it like this:

ValidationRules.AddRule<BaseAddressAssignment<T>>(
    CustomRules.StartDateGTEndDate<BaseAddressAssignment<T>>,
    "Date");

aaaah... Generics!! I know I bumped my head against the wall (more than) a few times until I finally got it... :D

Andrés

RockfordLhotka replied on Wednesday, December 13, 2006

In retrospect that interface approach for the rule method was not the best answer... It turns out that there's a much simpler answer to the problem that I just missed Sad [:(]

Shared/static methods in a class have access to the fields of instances of that class, so the rule method should look like this:

  private static bool StartDateGTEndDate<T>(T target, RuleArgs e) where T: Project
  {
    if (target._started > target._ended)
    {
      e.Description = "..."
      return true;
    }
    else
      return true;
  }

This way the interface isn't needed at all - much cleaner and simpler.

xal replied on Wednesday, December 13, 2006

Unless of course you DO plan to have many different classes that have a start/end date that aren't necessarily inheriting from project or addressbase (like in jspurlin's sample). In that case it does make perfect sense to have an interface and just one common rule that applies to all...

I remember once creating a collection + child object that where something along the lines of:



Public Class DateRangeCollection(Of T As DateRangeCollection(Of T,C), C As {BusinessBase(Of C), IDateRange})
Inherits BusinessListBase(Of T, C)

End Class

The collection, along with the object that implemented such interface would mark child items that where invalid because their date ranges collided with others. It also did grouping of items, like for instance, a project can have the same resource twice, but not on an overlapping date range, and of course, different resources could be in the project at the same time.

This collection + interface handled all of those cases (and a couple more) very very well. It took a few tries to get there, because performance became an issue when you had 200+ items (I had 20.000 in one case). It also had to handle adding, removing and editing of the child items, so it was an interesting challenge.

Andrés

jspurlin replied on Thursday, December 14, 2006

What I am trying to do is implement the IStartEnd interface in an abstract class where generic type T is used to inherit BusinessBase.  So this method definition works for other abstract classes that need to inherit from BusinessBase:

[Serializable()]
    public abstract class BasePerson<T>
        : BusinessBase<T> where T : BasePerson<T>


And this I think is what to do to implement the IStartEnd interface

[Serializable()]
    public abstract class BaseAddressAssignment<T>
        : BusinessBase<T>
, IStartEnd where T : BaseAddressAssignment<T>

Adding Business rules by your suggestion works:

ValidationRules.AddRule<IStartEnd>(
              StartDateGTEndDate<
IStartEnd>, "Started");
            ValidationRules.AddRule<
IStartEnd>(
              StartDateGTEndDate<
IStartEnd>, "Ended");

The problem is with the method StartDateGTEndDate:

private static bool StartDateGTEndDate<T>(T target, RuleArgs e) where T : BaseAddressAssignment<T>

T is already used for the outer type BaseAddressAssignment. But this actually may not be the best way to call it. I was thinking this particular business rule / validation rule / method (IStartEnd) could be "automatically" used, but "hidden" from the AddressAssignment classes (there are only 2) that will inherit from it.

I have not yet got that method signature to compile, but again, as XAL pointed out as generics where once new to him, they are new to me. Since StartDate and EndDate are fields to the linking tables OfficeAddress and PersonAddress I am struggling with correct object composition, too.

Here is the quandry:  A single address may have more than one StartDate and EndDate.  For example, we are coding for a hospices where things really have to check out for billing in order for Medicare to reimburse. So if an office moves, we need to make sure during billing the correct address is used or reimbursements are not made and the hospice staff does not get paid.  It sounds complicated:  correct object composition is not so difficult to understand.  It becomes "not so easy" to do when "social logic" is added!

ajj3085 replied on Thursday, December 14, 2006

Maybe I haven't been following closely enough, but if an address can have a start and end date (and multiple ones, maybe you need a collection of addresses, and each one has its own distinct start and end date.  Would that help clean things up for you?

Andy

xal replied on Thursday, December 14, 2006

If you just want your rule method to apply to anything deriving from BaseAddressAssignment, and you're going to implement it in that base class, all you need to do is this:

private static bool StartDateGTEndDate(BaseAddressAssignment<T> target, RuleArgs e) { return true; }

and add the rule like:
ValidationRules.AddRule<BaseAddressAssignment<T>>(
                StartDateGTEndDate, "StartDate");


========================================================================

If you want the method outside the base class and want it to apply to any IStartEnd, then you just do:

private static bool StartDateGTEndDate(IStartEnd target, RuleArgs e) {...}

And add the rule like:
ValidationRules.AddRule<IStartEnd>(
                StartDateGTEndDate, "StartDate");



Andrés

jspurlin replied on Thursday, December 14, 2006

Thank you, I will put it to use and let you know in a little bit.

ajj385, it is an interesting design problem (which may sound confusing, but it is really about scenarios where Medicare can reject a claim).

So the object composition is pretty key here. What I may need to do is add a collection of AddressAssignments to the OfficeAddress class.

Xal, you have been great help.  Thank you.
jspurlin


Copyright (c) Marimer LLC