Dapper : 저장 프로 시저에서 동적 피벗 열 매핑

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

문제

내 스토어드 프로 시저가 동적 피벗 열을 사용 가능한 것으로 반환 중입니다. 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();
        }

수락 된 답변

나는 이것이 단순 할 수있는 동안 이것을 매우 어렵게 만들고 있다고 생각합니다. 나는 거의 똑같은 일을 한 번 해냈다. 다음은 익명화 된 버전입니다.

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의 Query() 확장 메서드에서 반환 된 dynamicIDictionary<string, object> 로 캐스팅 할 수 있다는 것입니다.



아래 라이선스: CC-BY-SA with attribution
와 제휴하지 않음 Stack Overflow
이 KB는 합법적입니까? 예, 이유를 알아보십시오.
아래 라이선스: CC-BY-SA with attribution
와 제휴하지 않음 Stack Overflow
이 KB는 합법적입니까? 예, 이유를 알아보십시오.