繁体   English   中英

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

[英]Generic approach to dealing with multiple result sets from EF stored procedure

  • EF 6,.NET 4.51

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

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

  1. 通用返回类型
  2. 对象上下文
  3. 数据读取器
  4. 按结果集顺序返回的类类型列表

然后让helper类进行繁重的填充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);
    }

返回的结果数据集为:

在此处输入图片说明

助手类

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运算符“ <”不能应用于类型“方法组”和“系统类型”的操作数

有人可以帮忙吗? 最终,它与我们如何使用反射以及如何在aContainedDataSetReturnedTypes中传递有关。 只要可以轻松调用MultiResultsetsHelper.Process <>(),我很乐意更改周围的事物

这段代码:

aObjectContext.Translate<aContainedDataSetReturnedTypes[datasetNdx]>

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

您仍然可以调用通用方法,但必须使用Reflection

在以上所有步骤的帮助下,我提出了以下建议(仍可以改进):

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中的结果集列表相对应。

改进将是:

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

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

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