2014-10-04 70 views
1

我的存儲過程正在返回可用的動態透視列。我正在使用SQLMapper和COlumnTypeAttribute,但在結果中,我只能看到第一列和它的值,但動態透視列和它們的值是空的。Dapper:從存儲過程映射動態透視列

樣本數據可能看起來像

第一列是固定的,列的其餘的是樞軸列。

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

你可以使用具有動態輸出的SQLMapper嗎?我從來沒有考慮過這門課,但「映射器」對我來說聽起來很不穩定。 – 2014-10-05 13:11:21

回答

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

邏輯的心臟是由精緻小巧的Query()擴展方法返回的dynamic可強制轉換爲IDictionary<string, object>

+0

這個解決方案非常好,很簡單,我只是不確定我想用SQLMapper做什麼。 Dapper返回的動態性非常好,而且我可以使用.TOHTML方法創建數據表,這樣我就可以利用現有的Excel方法而不是將數據表寫入Excel工作簿。謝謝 :-) – user721 2014-10-06 02:59:05