2016-11-20 2 views
10

Dapper와 다중 매핑이있는 CTE를 사용하여 페이지 된 결과를 얻으려고합니다. 중복 된 열로 인해 불편을 겪고 있습니다. CTE는 예를 들어 이름 열을 갖지 못하게합니다.Dapper의 맞춤 매핑

다음 쿼리를 열 이름과 속성의 불일치가 아닌 다음 개체에 매핑하고 싶습니다.

검색어 :

WITH TempSites AS(
    SELECT 
     [S].[SiteID], 
     [S].[Name] AS [SiteName], 
     [S].[Description], 
     [L].[LocationID], 
     [L].[Name] AS [LocationName], 
     [L].[Description] AS [LocationDescription], 
     [L].[SiteID] AS [LocationSiteID], 
     [L].[ReportingID] 
    FROM (
     SELECT * FROM [dbo].[Sites] [1_S] 
     WHERE [1_S].[StatusID] = 0 
     ORDER BY [1_S].[Name] 
     OFFSET 10 * (1 - 1) ROWS 
     FETCH NEXT 10 ROWS ONLY 
    ) S 
     LEFT JOIN [dbo].[Locations] [L] ON [S].[SiteID] = [L].[SiteID] 
), 
MaxItems AS (SELECT COUNT(SiteID) AS MaxItems FROM Sites) 

SELECT * 
FROM TempSites, MaxItems 

객체 :

public class Site 
{ 
    public int SiteID { get; set; } 
    public string Name { get; set; } 
    public string Description { get; set; } 
    public List<Location> Locations { get; internal set; } 
} 

public class Location 
{ 
    public int LocationID { get; set; } 
    public string Name { get; set; } 
    public string Description { get; set; } 
    public Guid ReportingID { get; set; } 
    public int SiteID { get; set; } 
} 
나는 명명 규칙 나를 위해이 시나리오를 처리 할 수있는 존재 내 머리에 그것을 가지고 어떤 이유

하지만 언급을 찾을 수 없습니다 문서에서 그것의.

+0

나는이 질문을 닫으려고했으나 열린 현상금이 있습니다. 이것은 복제본입니다. ITypeMap 인터페이스 및 특성을 사용해야합니다. 읽기 : http://stackoverflow.com/questions/8902674/manually-map-column-names-with-class-properties –

답변

11

하나 이상의 문제가 함께 사이트의 목록을로드하기 위해, 잘 작동 그들에게 하나 하나를 포함 할 수 있습니다.

CTE 중복 열 이름을 허용하지 않습니다, 그래서 당신은 바람직 쿼리 시도처럼 일부 명명 규칙을 사용하여 별칭을 사용하여 해결할 수 있습니다

CTE는 열 이름을 중복.

나는 내 머리 속에이 시나리오를 처리 할 수있는 명명 규칙이 있지만 문서에서 언급을 찾을 수 없다는 이유가 있습니다.

당신은 아마 trueDefaultTypeMap.MatchNamesWithUnderscores 속성을 설정 염두에두고 있었지만, 재산 상태의 코드 문서로 :

USER_ID 같은

해야 열 이름은 사용자 아이디와 같은 속성/필드와 일치하도록 허용 ?

분명히 해결책이 아닙니다. 그러나이 문제는 사용자 지정 명명 규칙을 도입하여 쉽게 해결할 수 있습니다. 예를 들어 "{prefix}{propertyName}" (여기서 접두사는 기본적으로 "{className}_")이며 Dapper의 CustomPropertyTypeMap을 통해 구현합니다.

public static class CustomNameMap 
{ 
    public static void SetFor<T>(string prefix = null) 
    { 
     if (prefix == null) prefix = typeof(T).Name + "_"; 
     var typeMap = new CustomPropertyTypeMap(typeof(T), (type, name) => 
     { 
      if (name.StartsWith(prefix, StringComparison.OrdinalIgnoreCase)) 
       name = name.Substring(prefix.Length); 
      return type.GetProperty(name); 
     }); 
     SqlMapper.SetTypeMap(typeof(T), typeMap); 
    } 
} 

지금 당신이 필요로하는 모든이를 호출하는 것입니다 (1 회) :

WITH TempSites AS(
    SELECT 
     [S].[SiteID], 
     [S].[Name], 
     [S].[Description], 
     [L].[LocationID], 
     [L].[Name] AS [Location_Name], 
     [L].[Description] AS [Location_Description], 
     [L].[SiteID] AS [Location_SiteID], 
     [L].[ReportingID] 
    FROM (
     SELECT * FROM [dbo].[Sites] [1_S] 
     WHERE [1_S].[StatusID] = 0 
     ORDER BY [1_S].[Name] 
     OFFSET 10 * (1 - 1) ROWS 
     FETCH NEXT 10 ROWS ONLY 
    ) S 
     LEFT JOIN [dbo].[Locations] [L] ON [S].[SiteID] = [L].[SiteID] 
), 
MaxItems AS (SELECT COUNT(SiteID) AS MaxItems FROM Sites) 

SELECT * 
FROM TempSites, MaxItems 
:

CustomNameMap.SetFor<Location>(); 

는 쿼리에 이름 지정 규칙을 적용 여기에 수행하는 도우미 메서드입니다

당신은 그 부분을 다 끝냈습니다. 물론 원하는 경우 "Loc_"와 같이 짧은 접두어를 사용할 수 있습니다.당신은 당신이 Func<TFirst, TSecond, TReturn> map 대리자를 전달하고 분할 열로 LocationID를 지정합니다 splitOn 매개 변수를 unitilize 수있는 Query 메서드 오버로드를 사용하기 위해 필요한이 특정 경우

: 제공된 클래스에 쿼리 결과를 매핑

. 그러나 그것은 충분하지 않습니다. 말끔의 Multi Mapping 기능을 사용하면 Site 목록 Location와 (LINQ GroupJoin 같은)이 필요 동안 (LINQ Join 같이) 여러 하나의 객체에 단일 행을 분할 할 수 있습니다.

그것은이 같은 원하는 출력 생성 할 임시 익명 형으로 돌출하고 정규 LINQ를 사용할 Query 방법을 사용하여 달성 될 수있다 : cnSqlConnectionsql 열릴

var sites = cn.Query(sql, (Site site, Location loc) => new { site, loc }, splitOn: "LocationID") 
    .GroupBy(e => e.site.SiteID) 
    .Select(g => 
    { 
     var site = g.First().site; 
     site.Locations = g.Select(e => e.loc).Where(loc => loc != null).ToList(); 
     return site; 
    }) 
    .ToList(); 

string이다 위의 쿼리를 들고.

1

아래 코드는 관련 위치

var conString="your database connection string here"; 
using (var conn = new SqlConnection(conString)) 
{ 
    conn.Open(); 
    string qry = "SELECT S.SiteId, S.Name, S.Description, L.LocationId, L.Name,L.Description, 
        L.ReportingId 
        from Site S INNER JOIN 
        Location L ON S.SiteId=L.SiteId"; 
    var sites = conn.Query<Site, Location, Site> 
        (qry, (site, loc) => { site.Locations = loc; return site; }); 
    var siteCount = sites.Count(); 
    foreach (Site site in sites) 
    { 
     //do something 
    } 
    conn.Close(); 
} 
+0

답장을 보내 주셔서 감사합니다. 그러나 쿼리의 변경 사항으로 인해 페이지 매김의 필요성이 완전히 무시되었습니다. 또한 사용할 수있는 최대 사이트 수를 반환해야한다는 요구 사항이 있습니다. –

5

ColumnAttributeTypeMapper을 사용하여 다른 속성으로 열 이름을 매핑 할 수 있습니다.

자세한 내용은 Gist에 대한 첫 번째 의견을 참조하십시오.

당신은

public class Site 
{ 
    public int SiteID { get; set; } 
    [Column("SiteName")] 
    public string Name { get; set; } 
    public string Description { get; set; } 
    public List<Location> Locations { get; internal set; } 
} 

public class Location 
{ 
    public int LocationID { get; set; } 
    [Column("LocationName")] 
    public string Name { get; set; } 
    [Column("LocationDescription")] 
    public string Description { get; set; } 
    public Guid ReportingID { get; set; } 
    [Column("LocationSiteID")] 
    public int SiteID { get; set; } 
} 

매핑과 같은 매핑을 할 수

1

수동으로 한 번 모델에 대한 사용자 지정 TypeMapper을 설정

방법은 다음 3 가지 방법 중 하나를 사용하여 수행 할 수 있습니다 :

Dapper.SqlMapper.SetTypeMap(typeof(Site), new ColumnAttributeTypeMapper<Site>()); 
Dapper.SqlMapper.SetTypeMap(typeof(Location), new ColumnAttributeTypeMapper<Location>()); 

방법 2

.NET Framework> = v4.0의 클래스 라이브러리의 경우 PreApplicationStartMethod를 사용하여 사용자 지정 형식 매핑을위한 클래스를 등록 할 수 있습니다.

using System.Web; 
using Dapper; 

[assembly: PreApplicationStartMethod(typeof(YourNamespace.Initiator), "RegisterModels")] 

namespace YourNamespace 
{ 
    public class Initiator 
    { 
     private static void RegisterModels() 
     { 
      SqlMapper.SetTypeMap(typeof(Site), new ColumnAttributeTypeMapper<Site>()); 
      SqlMapper.SetTypeMap(typeof(Location), new ColumnAttributeTypeMapper<Location>()); 
      // ... 
     } 
    } 
} 

방법 3

아니면 클래스를 찾을 수는 ColumnAttribute 반사를 통해 적용 및 유형 매핑을 설정합니다. 이 작업은 약간 느려질 수 있지만 어셈블리의 모든 매핑이 자동으로 수행됩니다. 조립품이로드되면 RegisterTypeMaps()으로 전화하십시오.

public static void RegisterTypeMaps() 
    { 
     var mappedTypes = Assembly.GetAssembly(typeof (Initiator)).GetTypes().Where(
      f => 
      f.GetProperties().Any(
       p => 
       p.GetCustomAttributes(false).Any(
        a => a.GetType().Name == ColumnAttributeTypeMapper<dynamic>.ColumnAttributeName))); 

     var mapper = typeof(ColumnAttributeTypeMapper<>); 
     foreach (var mappedType in mappedTypes) 
     { 
      var genericType = mapper.MakeGenericType(new[] { mappedType }); 
      SqlMapper.SetTypeMap(mappedType, Activator.CreateInstance(genericType) as SqlMapper.ITypeMap); 
     } 
    } 
+0

방법 1을 시도하고 있는데 _error가 발생합니다. CS0535 : 'FallbackTypeMapper'가 'SqlMapper.ITypeMap.FindExplicitConstructor()'인터페이스 멤버를 구현하지 않습니다. 어떤 제안? – Joe