简体   繁体   中英

How can I accept a list of Func<> delegates and return a list of list when the List of func<> processes different objects

Following is my code where, I am passing a stored procedure as SQL to execute which runs multiple Select statements . Now the output of each datareader.NextResult() would be a different object (A POCO class) and they will not be dependent (hence I can't have an interface/base class to access them). The problem with this approach is that I have to do typecasting on the list returned by SelectQuery. Is there a better way to achieve following so that I can avoid typecasting?

public IEnumerable<IEnumerable<T>> SelectQuery<T>(string strSql, List<Func<IDataRecord, T>> infoExtractors)
{
  var result = new List<List<T>>();

  using (var connection = GetDBConnection())
  {
    using (var command = GetDBCommand(strSql))
    {
      command.Connection = connection;
      connection.Open();
      using (var reader = command.ExecuteReader(CommandBehavior.CloseConnection))
      {
        try
        {
          int i = 0;
          List<T> curList = new List<T>();
          result.Add(curList);
          while (reader.Read())
          {
            curList.Add(infoExtractors[i](reader));
          }

          while(reader.NextResult())
          {
            i++;
            curList = new List<T>();
            result.Add(curList);
            while (reader.Read())
            {
              curList.Add(infoExtractors[i](reader));
            }
          }
        }
        finally
        {
          reader.Close();
        }
      }
    }
  }

  return result;
}

The primary purpose of this code is to avoid letting caller closing the DB connection

Following is a sample code I wrote to simulate this and check if I can avoid typecasting

class Program
  {
    static void Main(string[] args)
    {

      List<Func<int, object>> lst = new List<Func<int, object>>();      
      lst.Add(c1.SetThis);      
      lst.Add(c2.SetThis);
      lst.Add(c3.SetThis);
      var output=  Check(lst);


      foreach(var cur in output)
      {
        foreach(var innerCur in cur)
        {
          System.Console.Write(innerCur);
        }
        System.Console.WriteLine();
      }
      System.Console.ReadLine();
    }

    static List<List<T>> Check<T>(List<Func<int, T>> inpList) 
    {
      List<List<T>> lst = new List<List<T>>();

      lst.Add(new List<T>() { inpList[0](10) });
      lst.Add(new List<T>() { inpList[1](20) });
      lst.Add(new List<T>() { inpList[2](30) });

      return lst;
    }


  }

  class c1
  {
    internal int num;

    internal static c1 SetThis(int i)
    {
      c1 c1Obj= new c1();
      c1Obj.num = i;
      return c1Obj;
    }
  }

  class c2
  {
    internal int num;
    internal static c2 SetThis(int i)
    {
      c2 c2Obj = new c2();
      c2Obj.num = i;
      return c2Obj;
    }
  }

  class c3
  {
    internal int num;
    internal static c3 SetThis(int i)
    {
      c3 c3Obj = new c3();
      c3Obj.num = i;
      return c3Obj;
    }
  }

Now, although the test sample has the same methodname, the classes c1,c2, c3 in reality will hold different data. Also The inner foreach loop for innerCur prints the type but I need a way to get the value without using typecasting? IS it possible? In worst case I will have to get rid of following inner foreach

foreach(var innerCur in cur)
{
  System.Console.Write(innerCur);
}

with

 foreach (var innerCur in cur)
 {
   if(innerCur is c1)
      System.Console.Write((innerCur as c1).num);
   else if(innerCur is c2)
      System.Console.Write((innerCur as c2).num);
   else
      System.Console.Write((innerCur as c3).num);
 }

An interesting problem. The only solution I can think of is to use reflection inside your Generic Repository class to call methods on the generic Type which has been passed in.

This gets around the problem by having the consumer of the library write a class which matches the DataBase and Tables they are querying, including the functions which transform the datarow into their object AND the List which is populated by that function.

The user passes this object into the GetData method and it becomes populated by the results.

using System;
using System.Collections.Generic;
using System.Data;
using System.Linq;
using System.Reflection;
using System.Text;
using System.Threading.Tasks;

namespace ThierCode
{
    public class MyTableQuery : YourLibrary.IMultiTableQuery
    {

        public Dictionary<string, string> MethodNameAndSql { get; set; }

        public void PopulateMyObjects(IDataRecord dr)
        {
            MyObject o = new MyObject();
            o.Name = dr["Name"].ToString();
            this.MyObjects.Add(o);
        }

        public List<MyObject> MyObjects { get; set; }
    }

    public class MyObject
    {
        public string Name { get; set; }
    }
}

namespace YourLibrary
{
    public interface IMultiTableQuery
    {
        public Dictionary<string, string> MethodNameAndSql { get; set; }
    }
    public class Repo<T> where T : IMultiTableQuery
    {
        public void GetData(T inputClass)
        {
            Type type = typeof(T);


            foreach (var kvp in inputClass.MethodNameAndSql)
            {
                string sql = kvp.Value;
                //run sql and get datareader

                //get the populate method back from the dictionary 
                MethodInfo methodInfo = type.GetMethod(kvp.Key);
                while (reader.Read())
                {
                    // execute via reflection
                    methodInfo.Invoke(inputClass, reader);
                }

            }
        }

    }
}

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM