处理EF存储过程的多个结果集的通用方法

  • EF 6,.NET 4.51

我正在尝试构建一个通用的帮助器类,它将帮助我将每个结果集“转换”为类型安全类,如此处所述使用SqlQuery处理存储过程的多个结果

对于我的解决方案,我想将以下内容传递给我的助手类(MultiResultsetsHelper):

  1. 通用退货类型
  2. ObjectContext的
  3. DataReader的
  4. 按结果集顺序返回的类类型列表

然后让帮助类完成填充的繁重工作1.下面是目前为止的代码:

结果类

public class Set1ReturnDto { public int CruiseCount { get; set; } public string DisplayText { get; set; } public int DisplayValue { get; set; } } public class Set2ReturnDto { public string DepartingFrom { get; set; } public string Port_Code { get; set; } } public class DummyReturnDto { public DummyReturnDto() { Set1 = new List(); Set2 = new List(); } public List Set1 { get; set; } public List Set2 { get; set; } } 

低级数据库调用

  public static DummyReturnDto DonoUspGetSideBarList(DbContext aDbContext, out int aProcResult) { SqlParameter procResultParam = new SqlParameter { ParameterName = "@procResult", SqlDbType = SqlDbType.Int, Direction = ParameterDirection.Output }; DbCommand dbCommand = aDbContext.Database.Connection.CreateCommand(); dbCommand.Parameters.Add(procResultParam); dbCommand.CommandText = "EXEC @procResult = [dbo].[usp_GetSideBarList] "; dbCommand.Transaction = aDbContext.Database.CurrentTransaction.UnderlyingTransaction; DbDataReader reader = dbCommand.ExecuteReader(); aProcResult = -1; // Drop down to the wrapped `ObjectContext` to get access to the `Translate` method ObjectContext objectContext = ((IObjectContextAdapter)aDbContext).ObjectContext; List containedDtos = new List { typeof (List), typeof (List) }; return MultiResultsetsHelper.Process(reader, objectContext, containedDtos); } 

返回的结果数据集是:

在此处输入图像描述

助手class

 public static class MultiResultsetsHelper { ///  /// Given a data reader that contains multiple result sets, use the supplied object context to serialise the /// rows of data in the result set into our property. ///  /// Type of the containing object that contains all the various result sets. /// Database reader that contains all the result sets returned from the database. /// Data context associated with the data reader. ///  /// List of types in order of which the result sets are contained within the /// data reader. We will serilize sequentially each result set the data reader contains ///  /// Retuns an object representing all the result sets returned by the data reader. public static T Process(DbDataReader aDbReader, ObjectContext aObjectContext, List aContainedDataSetReturnedTypes) where T : new() { //What we will be returning T result = new T(); for (int datasetNdx = 0; datasetNdx  p.PropertyType == aContainedDataSetReturnedTypes[datasetNdx]); //Now get the object context to deserialize what is in the resultset into our type var valueForProperty = aObjectContext.Translate  (aDbReader); //Finally we update the property with the type safe information propertyInfo.SetValue(result, valueForProperty, null); } return result; } } 

但是目前我无法编译。

错误2运算符'<'无法应用于'方法组'和'System.Type'类型的操作数

有人可以帮忙吗? 最终它与我们如何使用reflection和传入aContainedDataSetReturnedTypes有关。 只要仍然很容易调用MultiResultsetsHelper.Process (),我很乐意改变现状。

这段代码:

 aObjectContext.Translate 

不起作用,因为generics类型参数总是在编译时解析。 您无法在运行时传递Type实例。

您仍然可以调用generics方法,但是您必须使用reflection 。

在以上所有的帮助下,我想出了以下内容(仍然可以改进):

 public static class MultiResultsetsHelper { ///  /// Given a data reader that contains multiple result sets, use the supplied object context to serialise the /// rows of data in the result set into our property. ///  /// Type of the containing object that contains all the various result sets. /// Database reader that contains all the result sets returned from the database. /// Data context associated with the data reader. /// Type for each type to use when we call Translate() on the current result in the data reader. ///  /// List of types in order of which the result sets are contained within the /// data reader. We will serilize sequentially each result set the data reader contains ///  /// Retuns an object representing all the result sets returned by the data reader. public static T Process(DbDataReader aDbReader, DbContext aDbContext, List aDataSetTypes, List aContainedDataSetReturnedTypes) where T : new() { //What we will be returning T result = new T(); // Drop down to the wrapped `ObjectContext` to get access to the `Translate` method ObjectContext objectContext = ((IObjectContextAdapter) aDbContext).ObjectContext; //Iterate the passed in dataset types as they are in the same order as what the reader contains for (int datasetNdx = 0; datasetNdx < aContainedDataSetReturnedTypes.Count; datasetNdx++) { //Advance the reader if we are not looking at the first dataset if (datasetNdx != 0) aDbReader.NextResult(); //Get the property we are going to be updating based on the type of the class we will be filling PropertyInfo propertyInfo = typeof (T).GetProperties().Single(p => p.PropertyType == aContainedDataSetReturnedTypes[datasetNdx]); //Now get the object context to deserialize what is in the resultset into our type MethodInfo method = GetTranslateOverload(typeof (ObjectContext)); MethodInfo generic = method.MakeGenericMethod(aDataSetTypes[datasetNdx]); //Invoke the generic method which we hvae constructed for Translate object valueForProperty = generic.Invoke(objectContext, new object[] {aDbReader}); //Finally we update the property with the type safe information propertyInfo.SetValue(result, valueForProperty); } return result; } ///  /// Internal helper method to get the necessary translate overload we need: /// ObjectContext.Translate(DbReader) ///  /// ObjectContext.GetType() /// Returns the method we require, null on error. private static MethodInfo GetTranslateOverload(Type aType) { MethodInfo myMethod = aType .GetMethods() .Where(m => m.Name == "Translate") .Select(m => new { Method = m, Params = m.GetParameters(), Args = m.GetGenericArguments() }) .Where(x => x.Params.Length == 1 && x.Args.Length == 1 && x.Params[0].ParameterType == typeof (DbDataReader) // && x.Params[0].ParameterType == x.Args[0] ) .Select(x => x.Method) .First(); return myMethod; } } 

所以假设你有:

 public class UspGetSideBarListReturnDto { public List Dummy1 { get; set; } public List Dummy2 { get; set; } } public class Set1ReturnDto { public Int32 CruiseCount { get; set; } public string DisplayText { get; set; } public Int64 DisplayValue { get; set; } } public class Set2ReturnDto { public string DepartingFrom { get; set; } public string Port_Code { get; set; } } 

你可以称之为:

 DbDataReader reader = dbCommand.ExecuteReader(); return MultiResultsHelper.Process(reader, myDbContext, new List{typeof(Set1ReturnDto), typeof(Set2ReturnDto)}, new List{typeof(List), typeof(List}); 

aDataSetTypes的顺序需要与aDbReader中的结果集列表相对应。

改进将是:

  • 仅传递数据集类型列表。 (并自动确定List属性)