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

时间:2015-07-14 06:42:54

标签: c# entity-framework generics

  • EF 6,.NET 4.51

我正在尝试构建一个通用助手类,它将帮助我翻译"将每个结果集转换为此处所述的类型安全类Handle multiple result from a stored procedure with SqlQuery

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

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

    然后让帮助程序类重新填充填充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<Set1ReturnDto>();
            Set2 = new List<Set2ReturnDto>();
        }
    
        public List<Set1ReturnDto> Set1 { get; set; }
        public List<Set2ReturnDto> 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<Type> containedDtos = new List<Type>
                                       {
                                           typeof (List<Set1ReturnDto>), 
                                           typeof (List<Set1ReturnDto>)
                                       };
    
            return MultiResultsetsHelper.Process<DummyReturnDto>(reader, objectContext, containedDtos);
        }
    

    返回的结果数据集是:

    enter image description here

    助手类

    public static class MultiResultsetsHelper
    {
        /// <summary>
        ///     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.
        /// </summary>
        /// <typeparam name="T">Type of the containing object that contains all the various result sets.</typeparam>
        /// <param name="aDbReader">Database reader that contains all the result sets returned from the database.</param>
        /// <param name="aObjectContext">Data context associated with the data reader.</param>
        /// <param name="aContainedDataSetReturnedTypes">
        ///     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
        /// </param>
        /// <returns>Retuns an object representing all the result sets returned by the data reader.</returns>
        public static T Process<T>(DbDataReader aDbReader, ObjectContext aObjectContext, List<Type> aContainedDataSetReturnedTypes) where T : new()
        {
            //What we will be returning
            T result = new T();
    
            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
                var valueForProperty = aObjectContext.Translate <aContainedDataSetReturnedTypes[datasetNdx]> (aDbReader);
    
                //Finally we update the property with the type safe information
                propertyInfo.SetValue(result, valueForProperty, null);
            }
            return result;
        }
    }
    

    但是目前我无法进行编译。

      

    错误2运营商&#39;&lt;&#39;不能应用于类型&#39;方法的操作数   组&#39;和&#39; System.Type&#39;

    有人可以帮忙吗?最终它与我们如何使用反射和传入aContainedDataSetReturnedTypes有关。只要仍然很容易调用MultiResultsetsHelper.Process&lt;&gt;()

    ,我很乐意改变现状。

2 个答案:

答案 0 :(得分:0)

此代码:

aObjectContext.Translate<aContainedDataSetReturnedTypes[datasetNdx]>

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

您仍然可以调用泛型方法,但您必须use reflection

答案 1 :(得分:0)

在上述所有方面的帮助下,我想出了以下内容(仍有待改进):

public static class MultiResultsetsHelper
    {
        /// <summary>
        ///     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.
        /// </summary>
        /// <typeparam name="T">Type of the containing object that contains all the various result sets.</typeparam>
        /// <param name="aDbReader">Database reader that contains all the result sets returned from the database.</param>
        /// <param name="aDbContext">Data context associated with the data reader.</param>
        /// <param name="aDataSetTypes">Type for each type to use when we call Translate() on the current result in the data reader.</param>
        /// <param name="aContainedDataSetReturnedTypes">
        ///     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
        /// </param>
        /// <returns>Retuns an object representing all the result sets returned by the data reader.</returns>
        public static T Process<T>(DbDataReader aDbReader, DbContext aDbContext, List<Type> aDataSetTypes, List<Type> 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;
        }

        /// <summary>
        ///     Internal helper method to get the necessary translate overload we need:
        ///     ObjectContext.Translate<T>(DbReader)
        /// </summary>
        /// <param name="aType">ObjectContext.GetType()</param>
        /// <returns>Returns the method we require, null on error.</returns>
        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<Set1ReturnDto> Dummy1 { get; set; }
    public List<Set2ReturnDto> 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<UspGetSideBarListReturnDto>(reader, myDbContext, new List<Type>{typeof(Set1ReturnDto), typeof(Set2ReturnDto)}, new List<Type>{typeof(List<Set1ReturnDto>), typeof(List<Set2ReturnDto>});

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

改进将是:

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