Click here to Skip to main content
15,890,282 members
Please Sign up or sign in to vote.
4.00/5 (1 vote)
See more:
Hello everyone:

I'm currently working with a Business Object (from the StrataFrame Framework. I have inherited from the BaseBusinessLayer (parent class for all Business Objects ... for ease of reference you can think of this as a wrapper around each table in the database ... there are equivalents to this in most class libraries). I modified this base class to be an event object (I'll write and article about that later) and one of the base functionalities of this class is that I can add child BO's to the parent business object using another custom class called a childBOLoader ... here is the code for that:
C#
namespace SouthendBOL
{
    public class childBOLoader : Dictionary<string, BaseBusinessLayer>
    {
        public void Dispose()
        {
            dispose(true);
        }

        private void dispose(bool isDisposing)
        {
            if (isDisposing)
            {
                IEnumerator oEnum = GetEnumerator();
                while (oEnum.MoveNext())
                {
                    KeyValuePair<string, BaseBusinessLayer> pair =
                               ((KeyValuePair<string, BaseBusinessLayer>)(oEnum.Current));
                    pair.Value.Dispose();
                }
            }
        }

        public void AddBO(string _name, BaseBusinessLayer _BO)
        {
            if (!ContainsKey(_name))
            {
                Add(_name, _BO);
            }
        }

    }
}

That was the introduction ... this is the Problem:

On the base business object class I have to create code that looks like this ... one for each business object to which I want to obtain a reference ... here are two examples of those methods:
C#
public DialLog getDialLogBO()
{
    _childBOs.Add("oDialLog", new DialLog());
    return (DialLog)_childBOs["oDialLog"];
}

public DialRecalls getDialRecallsBO()
{
    _childBOs.Add("oDialRecalls", new DialRecalls());
    return (DialRecalls)_childBOs["oDialRecalls"];
}



One of my key tenants of OO Design is if that if I find myself using the VB6 method of inheritance (Ctrl+C followed closely by a Ctrl+V) that I'm doing something wrong. Extending this logic, it should become clear that if I wanted to load each and every one of my BOs into my event object (this is not really the case since the only BOs I need to load are those few who actually participate in events ... but this serves to illustrate my point) I would have to create a new method for each BO. In my case this equates to 29 methods added to the BaseBusinessLayer parent class, one for each BO in the project. Now this does not add to the "weight" of the BaseBO in any way; however, I want to reduce this to ONE ... but I cannot figure out how to do that and maintain type safety.

PS .. here is an example of how these methods are used in Event Processing:

C#
 protected override void EventProcessing()
 {
    base.EventProcessing();
       
    PriceSectionFilter oPSF = getPriceSectionFilterBO();
    PriceSections oPS = getPriceSectionsBO();
}


Second Question: Am I implementing IDisposable correctly in the childBOLoader class?

If you took the time to read this far ... THANKS!
Posted
Updated 13-Mar-14 10:34am
v6

You can do it, but this is not the best idea, because it is bad for maintenance: if a name is changed, your code depending on some string value will try to create an object with an old name; and the compiler won't be able to detect this problem. There are approaches not depending on any "magical strings"; for example, you can find an instance of System.Type implementing certain interface and instantiate it.

So, let's assume that you already found some type, an instance of System.Type by comparing, say, all types in some assembly, with you given name (if you still need this bad way; if you want to use better way, my solution will work anyway). You could find all types and the full name (it's important to use full name) of each, say, using
http://msdn.microsoft.com/en-us/library/system.reflection.assembly.gettypes%28v=vs.110%29.aspx[^],
http://msdn.microsoft.com/en-us/library/system.type.fullname%28v=vs.110%29.aspx[^].

Now, having the instance of System.Type, you need to find appropriate constructor. You can even use a non-public constructor. Please see:
http://msdn.microsoft.com/en-us/library/h93ya84h(v=vs.110).aspx[^],
http://msdn.microsoft.com/en-us/library/0h6w8akb(v=vs.110).aspx[^],
http://msdn.microsoft.com/en-us/library/cs01xzbk(v=vs.110).aspx[^],
http://msdn.microsoft.com/en-us/library/e687hf0d(v=vs.110).aspx[^],
http://msdn.microsoft.com/en-us/library/h70wxday(v=vs.110).aspx[^].

You can choose the constructor you want to use by its signature (number of parameters and their types) and binding flags (you will need instance constuctor, to instantiate anything, public or not).

This step will give you the instance of the System.Reflection.ConstructorInfo:
http://msdn.microsoft.com/en-us/library/system.reflection.constructorinfo%28v=vs.110%29.aspx[^].

As the final step, you need to invoke the constructor of your choice, optionally passing appropriate number of parameters to it:
http://msdn.microsoft.com/en-us/library/6ycw1y17(v=vs.110).aspx[^],
http://msdn.microsoft.com/en-us/library/a89hcwhh(v=vs.110).aspx[^],
http://msdn.microsoft.com/en-us/library/x7xy3xtx(v=vs.110).aspx[^],
http://msdn.microsoft.com/en-us/library/4k9x6bc0(v=vs.110).aspx[^].

This ConstructorInfo.Invoke call will always return you the instance of System.Object which you will need to type-cast to the expected type.

That's all. Enjoy. :-)

—SA
 
Share this answer
 
v5
Comments
Andreas Gieriet 13-Mar-14 16:56pm    
Hello Sergey,
Very elaborate and correct as usual! My 5!
Cheers
Andi
Sergey Alexandrovich Kryukov 13-Mar-14 16:58pm    
Thank you, Andi.
—SA
Maciej Los 13-Mar-14 17:35pm    
+5!
Sergey Alexandrovich Kryukov 13-Mar-14 17:39pm    
Thank you, Maciej.
—SA
To your second question: See MSDN: Implementing Finalize and Dispose to Clean Up Unmanaged Resources[^].

I have several comments on your code:

  • unusual naming scheme for C# (inconsitent lower/upper case wording, etc.). Have a look at Design Guidelines for Developing Class Libraries[^].
  • you use derivation where composition is probably clearer: why childBOLoader to derive from Dictionary? Do you want expose all Dictionary methods to the client of the childBOLoader class? No one would expect the childBOLoader to be a Dictionary. Better use composition instead.
  • Your class does not implement IDisposable interface (it's missing in the class header). Why do you think you need to dispose anything - you are here in a garbage collected environment.
  • You are using casts without cause - I regard casts as code smell, i.e. either avoid or comment why cannot avoid. Use the generic enumerator instead. But since your "Is-A" Dictionary instead of a "Has-A" Dictionary implementation, it looks odd anyways how you enumerate over the content of the dictionary. If it was cmposition, you would do a foreach loop (you can do that here too:
    C#
    foreach (var item in Values)
    {
       var obj = item as IDisposable; // only call Dispose on IDisposable objects
       if (obj != null) obj.Dispose(); 
    }
    or simpler if you BaseBusinessLayer class implements IDisposable:
    C#
    foreach (var item in Values) item.Dispose();

Cheers
Andi
 
Share this answer
 
v3
Comments
Maciej Los 13-Mar-14 17:35pm    
5ed!
Andreas Gieriet 14-Mar-14 2:42am    
Thanks for your 5, Maciej!
Cheers
Andi
Sergey Alexandrovich Kryukov 13-Mar-14 17:41pm    
Ah, I did not reach the second question yet. Good points, my 5.
—SA
Andreas Gieriet 14-Mar-14 2:42am    
Thanks for your 5, Sergey!
Cheers
Andi
BillWoodruff 13-Mar-14 23:10pm    
+5 Excellent !
Regarding creation of the various business objects (without seeing more of your architecture) I'd consider a single builder/getter function that takes the construction expression as a lambda and creates and inserts the object into the _childBOs Dictionary
C#
public BaseBusinessLayer getBO(Func<BaseBusinessLayer> factory)
{
    BaseBusinessLayer newBO = factory();
    string boName = newBO.GetType().Name;
    _childBOs.Add(boName);
    return newBO;
}

And then you would invoke it like:
C#
BaseBusinessLayer someBO = getBO(() => new DialRecalls());

[Edit]
If you don't like the idea of the lambda expressions, then you can restructure the inheritence hierarchy using generics so allow for a static method, implemented once in the BaseBusinessLayer class to factory the instances.
C#
using System;

namespace ConsoleApplication27
{
  class Program
  {
    static void Main(string[] args)
    {
      var dl = DialLog.Factory();
      Console.WriteLine("dl is type: " + dl.GetType().Name);
      var dr = DialRecalls.Factory();
      Console.WriteLine("dr is type: " + dr.GetType().Name);
    }
    abstract class BaseBusinessLayer<T> where T:BaseBusinessLayer<T>, new()
    {
      protected BaseBusinessLayer()
      {
      }
      static public T Factory()
      {
        return new T();
      }
    }
    class DialLog : BaseBusinessLayer<DialLog>
    {
    }
    class DialRecalls : BaseBusinessLayer<DialRecalls>
    {
    }
  }
}

And the use with the getBO method above would be:
C#
BaseBusinessLayer someBO = getBO(DialRecalls.Factory);
 
Share this answer
 
v2
Basic example of creating an object by its type name:

C#
IFilter filter = Activator.CreateInstance(Type.GetType("PriceSectionFilter")) as IFilter;


In this case all of the different filter types would need to derive from an interface or base class.

You could also do a google search for "c# factory pattern". There are plenty of ways to do what you are trying to do.

Hope that helps a little.
 
Share this answer
 
Comments
Andreas Gieriet 13-Mar-14 16:42pm    
My 5!
Cheers
Andi
Sergey Alexandrovich Kryukov 13-Mar-14 16:44pm    
It covers only some special cases. I voted 5, provided more general recipe in Solution 2, please see.
—SA
Andreas Gieriet 13-Mar-14 16:59pm    
It covers not all, as you say, but still it is the starting point.
To "why" at all to create classes by reflexion and then, by name is a valid question: high maintenance cost, not compile-time checked, it is slow, ... May be considered as design/code smell.
Cheers
Andi
Sergey Alexandrovich Kryukov 13-Mar-14 17:04pm    
Yes, actually, this is a good answer showing the way which might be it, shorter than in more general cases. It might be not sufficient, and then more general solution might be an option.
—SA
Maciej Los 13-Mar-14 17:34pm    
+5!
All 29 loadBO methods have been reduced to this:
C#
public BaseBusinessLayer getBO(Func<BaseBusinessLayer> factory)
{
    BaseBusinessLayer newBO = factory();
    string boName = newBO.GetType().Name;
   _childBOs.AddBO(boName, newBO);
    return _childBOs[boName];
}

Invoked like this:
C#
DialRecalls someBO = (DialRecalls)getBO(() => new DialRecalls());

Is this kind of what you meant ... because it works great!
 
Share this answer
 
Everyone ... thanks so much! I should have started posting questions here more often! You see, I develop alone ... so to me, all of my ideas are perfect <not>!

Andi:

As a matter of fact, each BusinessObject implements a Dispose method ... as such (at least I think it does):
C#
/// <summary>
/// Clean up any resources being used.
/// </summary>
/// <param name="disposing">true if managed resources should be disposed; otherwise, false.</param>
protected override void Dispose(bool disposing)
{
    if (disposing && (components != null))
    {
        components.Dispose();
    }
    base.Dispose(disposing);
}

Your other suggestion works nicely as follows:
C#
private void dispose(bool isDisposing)
{
    if (isDisposing)
    {
        foreach (var item in Values) item.Dispose();
    }
}

Why do I think I have to dispose of anything?

The answer to that is easy ... I'm not comfortable (ignorant of) garbage collection in general. Just can't get my head around the default behavior to know whether or not it should be implemented in one of my classes. Does this class even require and implementation of IDisposable?

Why did I use a generic Dictionary<string,>?

It seemed like a good fit and no ... the only method I really needed was Add to add a BO to the dictionary.

Unusual naming scheme for C#

Can you point out an example? The reference you gave is quite voluminous ... an example will let me focus on a problem area first ... then I can extrapolate for others.

Casts without cause ...

I assume you are meaning this line of code?
C#
return (DialRecalls)_childBOs["oDialRecalls"];

When I remove the cast I get an error "Cannot implicitly convert type 'SouthendBOL.BaseBusinessLayer' to 'SouthendBOL.PriceSectionFilter'. An explicit conversion exists (are you missing a cast?)

But since your "Is-A" Dictionary instead of a "Has-A" Dictionary implementation

Hate to admit it in public but this is a new concept to me ... care to expand on that a bit ... or provide a link for a more in depth explanation?

As for the factory method ... it appears that particular pattern likes to create an instance of all participating objects. This is not what I need. The current implementation is so simple (it takes relatively no time to add a new method) and it is so clear as to what is going on when implemented, that adding a OO pattern would add to complexity with very little benefit.

Another reason I like this implementation is that all BOs are available ... but they are only loaded when needed. Also, only critical business objects, the ones that participate in application events (like PrintInvoice ... which may involve CustomerBO, SalesTaxTableBO, InvoiceHeaderBO, InvoiceLineItemsBO ... etc) even have a method created for them.

Missing a class header ...

Well, here I go again ... can you give an example of a class header? Sorry ... newbie.

CTB

I can't thank you all enough ... this is an excellent forum!
 
Share this answer
 
v2
Comments
BillWoodruff 13-Mar-14 23:18pm    
The "norm" here is to post responses to the author of a "solution" as a comment on that specific solution, rather than as a solution in its own right. Expansions on, commentary on, your original post should be posted as additions to, or edits of, your original post.

In case you are wondering why this post was down-voted, that's probably the reason.

Welcome to CodeProject !
Andreas Gieriet 14-Mar-14 2:51am    
See my reply to this feedback in my solution.
Cheers
Andi

This content, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)



CodeProject, 20 Bay Street, 11th Floor Toronto, Ontario, Canada M5J 2N8 +1 (416) 849-8900