Ant*_*ift 10 .net c# sql-server dapper
我正在尝试使用带有Dapper和多映射的CTE来获取分页结果.我对重复的列感到不便; 例如,CTE阻止我使用Name列.
我想将以下查询映射到以下对象,而不是列名称和属性之间的不匹配.
查询:
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
Run Code Online (Sandbox Code Playgroud)
对象:
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; }
}
Run Code Online (Sandbox Code Playgroud)
由于某种原因,我在脑海中有一个命名约定,它将为我处理这个场景,但我在文档中找不到它.
Iva*_*oev 20
有一个以上的问题,让我们一一介绍.
CTE重复列名称:
CTE不允许重复的列名,因此您必须使用别名来解析它们,最好使用一些命名约定,例如在查询尝试中.
由于某种原因,我在脑海中有一个命名约定,它将为我处理这个场景,但我在文档中找不到它.
您可能想过将DefaultTypeMap.MatchNamesWithUnderscores属性设置为true,但作为属性的代码文档说明:
是否应允许像User_Id这样的列名匹配UserId等属性/字段?
显然这不是解决方案.但是,通过引入自定义命名约定(例如"{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);
}
}
Run Code Online (Sandbox Code Playgroud)
现在您只需要调用它(一次):
CustomNameMap.SetFor<Location>();
Run Code Online (Sandbox Code Playgroud)
将命名约定应用于您的查询:
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
Run Code Online (Sandbox Code Playgroud)
你完成了那部分.当然,如果你愿意,你可以使用更短的前缀,如"Loc_".
将查询结果映射到提供的类:
在这种特殊情况下,您需要使用Query方法重载,该方法允许您传递Func<TFirst, TSecond, TReturn> map委托并将splitOn参数单元化以指定LocationID为拆分列.然而,这还不够.Dapper的多映射功能允许您在需要with 列表(如LINQ )时将单行拆分为多个单个对象(如LINQ ). JoinSiteLocation GroupJoin
它可以通过使用该Query方法投影到临时匿名类型,然后使用常规LINQ生成所需的输出,如下所示:
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();
Run Code Online (Sandbox Code Playgroud)
其中cn被打开SqlConnection并且sql是string保持上述查询.
您可以使用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; }
}
Run Code Online (Sandbox Code Playgroud)
可以使用以下3种方法之一完成映射
方法1
手动将模型的自定义TypeMapper设置为:
Dapper.SqlMapper.SetTypeMap(typeof(Site), new ColumnAttributeTypeMapper<Site>());
Dapper.SqlMapper.SetTypeMap(typeof(Location), new ColumnAttributeTypeMapper<Location>());
Run Code Online (Sandbox Code Playgroud)
方法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>());
// ...
}
}
}
Run Code Online (Sandbox Code Playgroud)
方法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);
}
}
Run Code Online (Sandbox Code Playgroud)
| 归档时间: |
|
| 查看次数: |
18071 次 |
| 最近记录: |