【问题标题】:Generic approach to dealing with multiple result sets from EF stored procedure处理来自 EF 存储过程的多个结果集的通用方法
【发布时间】:2015-07-14 06:42:54
【问题描述】:
  • EF 6,.NET 4.51

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

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

  1. 通用返回类型
  2. 对象上下文
  3. 数据读取器
  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<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()

仍然很容易,我很乐意进行更改

【问题讨论】:

  • EXEC @procResult = [dbo].[usp_GetSideBarList] 返回什么实体类型?
  • 它返回两个数据集。第一个具有根据 Set1ReturnDto 的字段,第二个具有根据 Set2ReturnDto 的字段
  • 我会将两个 POCO 类组合在一起以创建另一个代表结果集的类,然后使用 context.Database.SqlQuery&lt;JoinedPocoClass&gt;("stored proc") 然后在其上使用 Select 以投影到单独的 POCO 类中
  • 除非你可以触发sproc 两次以给出两个不同的结果集,然后我会触发SqlQuery 强类型与两个POCO
  • @CallumLinington Ist JoinedPocoClass 实际上是我的班级 DummyReturnDto?

标签: c# entity-framework generics


【解决方案1】:

这段代码:

aObjectContext.Translate<aContainedDataSetReturnedTypes[datasetNdx]>

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

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

【讨论】:

  • OK 所以使用反射作为 MethodInfo method = typeof(ObjectContext).GetMethod("Translate");我现在得到异常 System.Reflection.AmbiguousMatchException。那么如何定位 Translate
  • 有两个重载称为Translate - 请参阅msdn - 因此您必须使用this overload 将您想要的签名传递给GetMethod
【解决方案2】:

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

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 属性)

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-09-03
    • 1970-01-01
    • 2016-10-30
    • 2016-08-06
    • 1970-01-01
    相关资源
    最近更新 更多