views:

118

answers:

4

I am trying to create a class that uses a factory to find the right class and then calls methods on that class. I am doing something wrong though because the intellisense in Visual Studio keeps alerting me to errors and when I try to access the methods that should be within the class the factory returns they are not available.

Can anyone tell me what I'm doing wrong?

Here is the code where I am trying to get a reference to the class:

DBBase dal = DALFactory.GetDAL(typeof(T));

            myCollection = dal.GetByCriteria(myCriteria, sortExpression, startRowIndex, maximumRows, propertyNamesToBypassInstantiation);

Here is the code for the DALFactory class:

    internal static class DALFactory
    {
        // GetParser
        internal static DBBase GetDAL(System.Type BOType)
        {
            switch (BOType.Name)
            {
                case "Person":
                    return new PersonDB();
}
            // if we reach this point then we failed to find a matching type. Throw 
            // an exception.
            throw new Exception("Unknown Type");
        }
}

Here is part of the code for the base of the class returned by the factory class:

public abstract class DBBase
{
    protected static T GetSingleBO<T>(ref SqlCommand command) where T : BOBase
    {
        return GetSingleBO<T>(ref command, null);
    }


    protected static T GetSingleBO<T>(ref SqlCommand command, List<string> propertyNamesToBypassInstantiation) where T : BOBase
    {
        T BO = default(T);
        try
        {
            command.Connection.Open();
            SqlDataReader reader = command.ExecuteReader();
            if (reader.HasRows)
            {
                reader.Read();
                BOParser parser = BOParserFactory.GetParser(typeof(T));
                parser.PopulateOrdinals(reader);
                if (propertyNamesToBypassInstantiation == null)
                {
                    BO = (T)parser.PopulateBO(reader);
                }
                else
                {
                    BO = (T)parser.PopulateBO(reader, propertyNamesToBypassInstantiation);
                }
                reader.Close();
            }
            else
            {
                // Whever there's no data, we return null.
                BO = default(T);
            }
        }
        catch (Exception ex)
        {
            StringBuilder sb = new StringBuilder();
            sb.AppendFormat("Error populating data: {0}", ex.Message);
            if (ex.InnerException != null)
            {
                sb.AppendFormat("  --  Inner Exception: {0}", ex.InnerException.Message);
            }
            throw new Exception(sb.ToString(), ex);
        }
        finally
        {
            command.Connection.Close();
            command.Connection.Dispose();
        }

        // return the BO, it's either populated with data or null.
        return BO;
    }


    protected static EquatableList<T> GetBOEquatableList<T>(ref SqlCommand command) where T : BOBase
    {
        return GetBOEquatableList<T>(ref command, null);
    }

   protected static EquatableList<T> GetBOEquatableList<T>(ref SqlCommand command, List<string> propertyNamesToBypassInstantiation) where T : BOBase
    {
        EquatableList<T> BOList = new EquatableList<T>();
        try
        {
            command.Connection.Open();
            SqlDataReader reader = command.ExecuteReader();
            if (reader.HasRows)
            {
                // Get a parser for this BO type and populate
                // the ordinals.
                BOParser parser = BOParserFactory.GetParser(typeof(T));
                parser.PopulateOrdinals(reader);

                // Use the parser to build our list of BOs.
                while (reader.Read())
                {
                    T BO = default(T);
                    if (propertyNamesToBypassInstantiation == null)
                    {
                        BO = (T)parser.PopulateBO(reader);
                    }
                    else
                    {
                        BO = (T)parser.PopulateBO(reader, propertyNamesToBypassInstantiation);
                    }
                    BOList.Add(BO);
                }
                reader.Close();
            }
        }
        catch (Exception ex)
        {
            StringBuilder sb = new StringBuilder();
            sb.AppendFormat("Error populating data: {0}", ex.Message);
            if (ex.InnerException != null)
            {
                sb.AppendFormat("  --  Inner Exception: {0}", ex.InnerException.Message);
            }
            throw new Exception(sb.ToString(), ex);
        }
        finally
        {
            command.Connection.Close();
            command.Connection.Dispose();
        }

        return BOList;
    }

    protected static int GetBOCount(ref SqlCommand command)
    {
        int count = 0;

        try
        {
            command.CommandType = CommandType.StoredProcedure;
            DbParameter idParam = command.CreateParameter();
            idParam.DbType = DbType.Int32;
            idParam.Direction = ParameterDirection.InputOutput;
            idParam.ParameterName = "@recordCount";
            idParam.Value = 0;
            command.Parameters.Add(idParam);

            command.Connection.Open();
            command.ExecuteNonQuery();
            command.Connection.Close();
            count = (int)command.Parameters["@recordCount"].Value;
        }
        catch (Exception e)
        {
            throw new Exception("Error populating data", e);
        }
        finally
        {
            command.Connection.Close();
            command.Connection.Dispose();
        }

        return count;
    }

    protected static bool DeleteBO(ref SqlCommand command)
    {
        int result = 0;
        try
        {
            command.Connection.Open();
            result = command.ExecuteNonQuery();
        }
        catch (Exception e)
        {
            throw new Exception("Error deleting data", e);
        }
        finally
        {
            command.Connection.Close();
            command.Connection.Dispose();
        }

        return result > 0;
    }


    protected static int GetFKcount(ref SqlCommand command)
    {
        int count = 0;

        try
        {
            command.CommandType = CommandType.StoredProcedure;
            DbParameter idParam = command.CreateParameter();
            idParam.DbType = DbType.Int32;
            idParam.Direction = ParameterDirection.InputOutput;
            idParam.ParameterName = "@recordCount";
            idParam.Value = 0;
            command.Parameters.Add(idParam);

            command.Connection.Open();
            command.ExecuteNonQuery();
            command.Connection.Close();
            count = (int)command.Parameters["@recordCount"].Value;
        }
        catch (Exception e)
        {
            throw new Exception("Error populating data", e);
        }
        finally
        {
            command.Connection.Close();
            command.Connection.Dispose();
        }

        return count;
    }

    public static T GetById<T>(Guid Id)
        where T : BOBase
    {
        return GetById<T>(Id, null);
    }


    abstract public static T GetById<T>(Guid Id, List<string> propertyNamesToBypassInstantiation)
        where T : BOBase;

    public static EquatableList<T> GetByCriteria<T,TCriteria>(TCriteria myCriteria)
        where T : BOBase
        where TCriteria : BaseCriteria
    {
        return GetByCriteria<T, TCriteria>(myCriteria, null);
    }


    public static EquatableList<T> GetByCriteria<T, TCriteria>(TCriteria myCriteria, List<string> propertyNamesToBypassInstantiation)
        where T : BOBase
        where TCriteria : BaseCriteria
    {
        return GetByCriteria<T, TCriteria>(myCriteria, null, -1, -1, propertyNamesToBypassInstantiation);
    }

    public static EquatableList<T> GetByCriteria<T, TCriteria>(TCriteria myCriteria, string sortExpression, int startRowIndex, int maximumRows)
        where T : BOBase
        where TCriteria : BaseCriteria
    {
        return GetByCriteria<T, TCriteria>(myCriteria, sortExpression, startRowIndex, maximumRows, null);
    }

    abstract public static EquatableList<T> GetByCriteria<T, TCriteria>(TCriteria myCriteria, string sortExpression, int startRowIndex, int maximumRows, List<string> propertyNamesToBypassInstantiation)
        where T : BOBase
        where TCriteria : BaseCriteria;

    abstract public static int GetCountForCriteria<TCriteria>(TCriteria myCriteria)
        where TCriteria : BaseCriteria;

    abstract public static bool Save<T>(T myobject)
        where T : BOBase;


    abstract public static bool Delete<T>(Guid Id)
        where T : BOBase;


    abstract public static int GetFKcount<T>(Guid Id)
        where T : BOBase;

    abstract internal static void CreateCriteriaParameters<T, TCriteria>(ref SqlCommand myCommand, TCriteria myCriteria)
        where T : BOBase
        where TCriteria : BaseCriteria;
}

And this is part of the code for the final bottom class that should be getting returned:

public partial class PersonDB : DBBase
{

    public override static Person GetById(Guid Id, List<string> propertyNamesToBypassInstantiation)
    {
        SqlCommand command = GetDbSprocCommand("sprocPersonSelectSingleItem");
        command.Parameters.Add(CreateParameter("@id", Id));
        return GetSingleBO<Person>(ref command, propertyNamesToBypassInstantiation);
    }

    public override static EquatableList<Person> GetByCriteria(PersonCriteria myCriteria, string sortExpression, int startRowIndex, int maximumRows, List<string> propertyNamesToBypassInstantiation)
    {
        SqlCommand command = GetDbSprocCommand("sprocPersonSearchList");
        CreateCriteriaParameters(ref command, myCriteria);
        CreatePagingAndSortingParameters(ref command, sortExpression, startRowIndex, maximumRows);
        return GetBOEquatableList<Person>(ref command, propertyNamesToBypassInstantiation);
    }
}
A: 

I use in my projects a factory like this:

public class SomeFactory
{
  private static SomeFactory instance = new SomeFactory();

  public static SomeFactory Instance
  {
      get
      {
          return instance;
      }
  }

  private static Dictionary<Type, Type> classes = new Dictionary<Type, Type>();

  static SomeFactory()
  {
      // add here classes that would be created by a factory
      classes.Add(typeof(IClass1), typeof(Class1));
      classes.Add(typeof(IClass2), typeof(Class2));
  }

  public T Create<T>()
  {
      return (T)System.Activator.CreateInstance(repos[typeof(T)]);
  }
}

usage

SomeFactory.Instance.Create<IClass1>()...
Sebastian Brózda
A: 

why not use attributes and create a dynamic factory

public static class MyFactory { private static Dictionary objects = new Dictionary();

    static MyFactory()
    {
        // Scan the assembly
        foreach (Type type in Assembly.GetExecutingAssembly().GetTypes())
        {
            MyAttribute[] frameAttributes =
                type.GetCustomAttributes(typeof (MyAttribute), false) as MyAttribute[];
            foreach (MyAttribute myAttribute in frameAttributes)
            {
                objects.Add(myAttribute.SomeKey, type);
            }
        }
    }
Mark
+1  A: 

Well, DBBase was defined as non-generic; that means you can't declare a generic instance of it. That's the biggest compile-time error I see.

Generally, switching on type is a Rally Bad Idea, because every new type you want this factory to be able to produce will require a new case. After a couple dozen new types, this switch statement will be too long to efficiently maintain. Try this for your factory:

public class DALFactory
{
   private Dictionary<Type, Func<DBBase>> factoryMethods;

   public void Register(Type type, Func<DBBase>> producer)
   {
      factoryMethods[type] = producer; //last-in wins
   }

   public DBBase GetObject(Type type)
   {
       if(factoryMethods.ContainsKey(type))
          return factoryMethods[type]();
       return null; //or throw exception
   }

   public Func<DBBase> GetFactoryMethod(Type type)
   {
       if(factoryMethods.ContainsKey(type))
          return factoryMethods[type];
       return null; //or throw exception
   }
}

...

var factory = new DALFactory();
factory.Register(typeof(PersonDB), ()=>new PersonDB());
//Register other named or anonymous factory methods that produce objects

When setting up your factory, you can register as many factory methods as you want without changing the factory itself.

KeithS
+1  A: 

Although all the comments about how to make a nice generic factory are nice, it wasn't my question. The problem I was having was that even though my factory was returning the reference to the class, I wasn't able to access any of the methods on the class.

Well, it turns out, that if you have static methods on a class, they aren't available when you have an "instance" of that class. Since all my methods in the derived class are static, I thought I wasn't actually getting back an instance of my class at all. Turns out I was....

Anyway, since what I really needed was a way to get a reference to the methods in the derived class, I found a solution that uses GetType, GetMethod, and MethoInfo.Invoke to call the methods instead.

Here is an example:

The method where I was originally trying to get a reference to the derived class:

public static bobase GetByCriteria<T,TCriteria>(TCriteria myCriteria)
            where T : bobase
            where TCriteria : BaseCriteria
        {
            T myObject = default(T);
            Type[] myMethodTypes = new Type[]{typeof(string),typeof(int),typeof(TCriteria)};
            System.Reflection.MethodInfo myMethod = myObject.DBclass.GetMethod("GetByCriteria", System.Reflection.BindingFlags.Public | System.Reflection.BindingFlags.Static, null, myMethodTypes, null);
            object[] myMethodParameters = new object[]{"someValueHere", 1, myObject};
            return (bobase)myMethod.Invoke(null, myMethodParameters);
        }

the bobase class:

public abstract class bobase
    {
        internal virtual Type DBclass
        {
            get
            {
                return Type.GetType(this.GetType().Name + "DB");
            }
        }
    }

I am aware that the GetMethod stuff uses reflection to get the data, so there is probably some kind of performance hit. If there is a better way to get a reference to a static method (like a static method factory) that doesn't use reflection I'd love to hear about it.

Amanda Myer