2016-11-20 29 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などの万一の列名は、ユーザー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メソッドオーバーロードを使用する必要がある。この特定のケースで

:提供されるクラスにクエリ結果をマッピング

。しかしそれだけでは不十分です。 DapperののMulti Mapping機能を使用すると、(LINQ GroupJoinのような)SiteLocationとリストを必要としているときに(LINQ Joinのような)いくつかの単一オブジェクトへの単一の行を分割することができます。

それは一時的な匿名型に突出してから、このような所望の出力を生成するために定期的にLINQを使用するようにQuery方法を使用することによって達成することができます。

cnSqlConnectionを開かれ、sqlstringある
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(); 

上記のクエリを保持します。

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