Dapper:从存储过程映射动态轴

时间:2014-10-04 22:17:57

标签: asp.net entity-framework asp.net-mvc-3 dapper

我的存储过程正在返回动态数据透视表列。我使用的是SQLMapper和COlumnTypeAttribute,但在结果中我只能看到第一列及其值,但动态数据透视表列及其值是空的。

示例数据可能看起来像

第一列是固定的,其余列是枢轴列。

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

存储过程

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;
        }
    }
}

执行存储过程

  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();
        }

1 个答案:

答案 0 :(得分:4)

我认为你很难做到这一点很简单。我曾经做过几乎完全相同的事情。这是它的匿名版本:

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);
}

ToDataTable方法可能与您的ToHTMLTable方法类似。这是:

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;
}

逻辑的核心是,Dapper的dynamic扩展方法返回的Query()可以转换为IDictionary<string, object>