Dapper: asignación de columnas dinámicas de pivote del procedimiento almacenado

asp.net asp.net-mvc-3 dapper entity-framework

Pregunta

Mi procedimiento almacenado está devolviendo las columnas dinámicas de pivote disponibles. Estoy usando SQLMapper y COlumnTypeAttribute, pero en los resultados solo puedo ver la primera columna y su valor, pero la (s) columna (s) pivote (s) dinámica (s) y sus valores están vacíos.

los datos de muestra podrían verse como

La primera columna es fija, el resto de las columnas son columnas de pivote.

TSBNumber      SystemXY   SystemBB   SystemTT
asdas          1/1/2013    
1231                      1/1/2014
12312          1/1/2013
ASAWS                     1/1/2013
awsdS                               1/1/2013

Procedimiento de almacenamiento

DECLARE @PivotColumnHeaders NVARCHAR(MAX)

SELECT @PivotColumnHeaders =  
   COALESCE(
     @PivotColumnHeaders + ',[' +  cast(SystemFullName as Nvarchar) + ']',
     '[' + cast(SystemFullName as varchar)+ ']'
   )
FROM System
WHERE (@SelectedSystemIDs IS NULL OR  System.ID IN(select  * from dbo.SplitInts_RBAR_1(@SelectedSystemIDs, ',')))     
AND ((@PlatformID IS NULL) OR  (System.PlatformID = @PlatformID) OR (@PlatformID = 12 AND System.PlatformID <= 2))  

DECLARE @PivotTableSQL NVARCHAR(MAX)
SET @PivotTableSQL = N'
   SELECT *
   FROM (
     SELECT
       TSBNumber [TSBNumber],
       SystemFullName,
       ClosedDate
     FROM ServiceEntry 
     INNER JOIN System 
       ON ServiceEntry.SystemID = System.ID
     where
      (ServiceEntry.TSBNumber IS NOT NULL)
       AND 
       (ServiceEntry.ClosedDate IS NOT NULL)
       AND
       ( 
       (''' + @SelectedTsbIDs + ''' = '''+ '0' + ''') OR
         (ServiceEntry.TSBNumber in (select * from dbo.SplitStrings_Moden(''' + @SelectedTsbIDs + ''', ''' + ',' + ''')))
        )  
   ) AS PivotData
   PIVOT (
     MAX(ClosedDate)
     FOR SystemFullName IN (
       ' + @PivotColumnHeaders + '
     ) 
   ) AS PivotTable
' 
EXECUTE (@PivotTableSQL)

ColumnAttributeTypeManager

namespace RunLog.Domain.Exports
{
    /// <summary>
    /// Uses the Name value of the ColumnAttribute specified, otherwise maps as usual.
    /// </summary>
    /// <typeparam name="T">The type of the object that this mapper applies to.</typeparam>
    public class ColumnAttributeTypeMapper<T> : FallbackTypeMapper
    {
        public static readonly string ColumnAttributeName = "ColumnAttribute";

        public ColumnAttributeTypeMapper()
            : base(new SqlMapper.ITypeMap[]
{
new CustomPropertyTypeMap(
typeof(T),
(type, columnName) =>
type.GetProperties().FirstOrDefault(prop =>
prop.GetCustomAttributes(false)
.OfType<ColumnAttribute>()
.Any(attr => attr.Name == columnName)
)
),
new DefaultTypeMap(typeof(T))
})
        {
        }
        //public ColumnAttributeTypeMapper()
        //    : base(new SqlMapper.ITypeMap[]
        //    {
        //        new CustomPropertyTypeMap(typeof (T), SelectProperty),
        //        new DefaultTypeMap(typeof (T))
        //    })
        //{
        //}

        //private static PropertyInfo SelectProperty(Type type, string columnName)
        //{
        //    return
        //        type.GetProperties(BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance).
        //            FirstOrDefault(
        //                prop =>
        //                prop.GetCustomAttributes(false)
        //                    // Search properties to find the one ColumnAttribute applied with Name property set as columnName to be Mapped 
        //                    .Any(attr => attr.GetType().Name == ColumnAttributeName
        //                                 &&
        //                                 attr.GetType().GetProperties(BindingFlags.Public |
        //                                                              BindingFlags.NonPublic |
        //                                                              BindingFlags.Instance)
        //                                     .Any(
        //                                         f =>
        //                                         f.Name == "Name" &&
        //                                         f.GetValue(attr).ToString().ToLower() == columnName.ToLower()))
        //                && // Also ensure the property is not read-only
        //                (prop.DeclaringType == type
        //                     ? prop.GetSetMethod(true)
        //                     : prop.DeclaringType.GetProperty(prop.Name,
        //                                                      BindingFlags.Public | BindingFlags.NonPublic |
        //                                                      BindingFlags.Instance).GetSetMethod(true)) != null
        //            );
        //}
    }

    public class MyModel
    {
        [Column("TSBNumber")]
        public string TSBNumber { get; set; }

        [Column(Name = "FileKey")]
        public string MyProperty2 { get; set; }

        //public string MyProperty2 { get; set; } // Uses Default Mapping
        // ...
    }

    [AttributeUsage(AttributeTargets.Property, AllowMultiple = true)]
    public class ColumnAttribute : Attribute
    {
        public string Name { get; set; }
        public ColumnAttribute() { }
        public ColumnAttribute(string Name) { this.Name = Name; }
    }

    public class FallbackTypeMapper : SqlMapper.ITypeMap
    {
        private readonly IEnumerable<SqlMapper.ITypeMap> _mappers;

        public FallbackTypeMapper(IEnumerable<SqlMapper.ITypeMap> mappers)
        {
            _mappers = mappers;
        }


        public ConstructorInfo FindConstructor(string[] names, Type[] types)
        {
            foreach (var mapper in _mappers)
            {
                try
                {
                    ConstructorInfo result = mapper.FindConstructor(names, types);
                    if (result != null)
                    {
                        return result;
                    }
                }
                catch (NotImplementedException)
                {
                }
            }
            return null;
        }

        public SqlMapper.IMemberMap GetConstructorParameter(ConstructorInfo constructor, string columnName)
        {
            foreach (var mapper in _mappers)
            {
                try
                {
                    var result = mapper.GetConstructorParameter(constructor, columnName);
                    if (result != null)
                    {
                        return result;
                    }
                }
                catch (NotImplementedException)
                {
                }
            }
            return null;
        }

        public SqlMapper.IMemberMap GetMember(string columnName)
        {
            foreach (var mapper in _mappers)
            {
                try
                {
                    var result = mapper.GetMember(columnName);
                    if (result != null)
                    {
                        return result;
                    }
                }
                catch (NotImplementedException)
                {
                }
            }
            return null;
        }
    }
}

Ejecución del procedimiento almacenado

  public static string ServiceTsbExport(DateTime StartDate, DateTime EndDate, int UserRoleID,
                                                string SelectedSystemIDs,
                                                 string SelectedTsbIDs)
        {
            EFDbContext db = new EFDbContext();


            Dapper.SqlMapper.SetTypeMap(typeof(MyModel), new ColumnAttributeTypeMapper<MyModel>());
            return db.Database.SqlQuery<MyModel>("[dbo].[spExportServiceTSB] @parm1, @parm2, @parm3, @parm4, @parm5",
                        new SqlParameter("parm1", StartDate),
                        new SqlParameter("parm2", EndDate),
                        new SqlParameter("parm3", SelectedSystemIDs),
                        new SqlParameter("parm4", SelectedTsbIDs),
                        new SqlParameter("parm5", UserRoleID)
                        ).ToList().ToHTMLTable();
        }

Respuesta aceptada

Creo que estás haciendo esto muy difícil, aunque podría ser simple. Una vez hice casi exactamente lo mismo. Aquí hay una versión anónima de esto:

using Dapper;

...

using (var cnn = new SqlConnection(@"Data Source=... etc."))
{
    cnn.Open();

    var p = new DynamicParameters();
    p.Add("@params", "Id=21");

    var obs = cnn.Query(sql:"GetPivotData", param: p,
                        commandType:CommandType.StoredProcedure);

    var dt = ToDataTable(obs);
}

Este método ToDataTable es probablemente similar a su método ToHTMLTable . Aquí está:

public DataTable ToDataTable(IEnumerable<dynamic> items)
{
    if (items == null) return null;
    var data = items.ToArray();
    if (data.Length == 0) return null;

    var dt = new DataTable();
    foreach(var pair in ((IDictionary<string, object>)data[0]))
    {
        dt.Columns.Add(pair.Key, (pair.Value ?? string.Empty).GetType());
    }
    foreach (var d in data)
    {
        dt.Rows.Add(((IDictionary<string, object>)d).Values.ToArray());
    }
    return dt;
}

El núcleo de la lógica es que la dynamic devuelta por el método de extensión Query() Dapper se puede convertir a un IDictionary<string, object> .



Licencia bajo: CC-BY-SA with attribution
No afiliado con Stack Overflow
¿Es esto KB legal? Sí, aprende por qué
Licencia bajo: CC-BY-SA with attribution
No afiliado con Stack Overflow
¿Es esto KB legal? Sí, aprende por qué