Nau*_*zet 17 c# sql reflection dapper .net-core
我正在使用.net核心和dapper,第一个没有DataTables,第二个使用它们用于TVP.
我试图将a转换List<T>为a List<SqlDataRecord>,使用此列表创建一个SqlParameter然后将其转换为DynamicParameter但遗憾的是我得到了:The member of type Microsoft.SqlServer.Server.SqlDataRecord cannot be used as a parameter value
玩了一下之后IDynamicParameters,我就开始了.
扩展方法 IEnumerable
public static DynamicWrapper toTVP<T>(this IEnumerable<T> enumerable, string tableName, string typeName)
{
List<SqlDataRecord> records = new List<SqlDataRecord>();
var properties = typeof(T).GetProperties().Where(p => Mapper.TypeToSQLMap.ContainsKey(p.PropertyType));
var definitions = properties.Select(p => Mapper.TypeToMetaData(p.Name, p.PropertyType)).ToArray();
foreach (var item in enumerable)
{
var values = properties.Select(p => p.GetValue(item, null)).ToArray();
var schema = new SqlDataRecord(definitions);
schema.SetValues(values);
records.Add(schema);
}
SqlParameter result = new SqlParameter(tableName, SqlDbType.Structured);
result.Direction = ParameterDirection.Input;
result.TypeName = typeName;
result.Value = records;
return new DynamicWrapper(result);
}
Run Code Online (Sandbox Code Playgroud)
包装器实现 IDynamicParameters
public class DynamicWrapper : IDynamicParameters
{
private readonly SqlParameter _Parameter;
public DynamicWrapper(SqlParameter param)
{
_Parameter = param;
}
public void AddParameters(IDbCommand command, Identity identity)
{
command.Parameters.Add(_Parameter);
}
}
Run Code Online (Sandbox Code Playgroud)
映射器(未经过完全测试,只有管理字符串到NVARCHAR,因为它抛出异常而没有maxLength)
public class Mapper
{
public static Dictionary<Type, SqlDbType> TypeToSQLMap = new Dictionary<Type, SqlDbType>()
{
{typeof (long),SqlDbType.BigInt},
{typeof (long?),SqlDbType.BigInt},
{typeof (byte[]),SqlDbType.Image},
{typeof (bool),SqlDbType.Bit},
{typeof (bool?),SqlDbType.Bit},
{typeof (string),SqlDbType.NVarChar},
{typeof (DateTime),SqlDbType.DateTime2},
{typeof (DateTime?),SqlDbType.DateTime2},
{typeof (decimal),SqlDbType.Money},
{typeof (decimal?),SqlDbType.Money},
{typeof (double),SqlDbType.Float},
{typeof (double?),SqlDbType.Float},
{typeof (int),SqlDbType.Int},
{typeof (int?),SqlDbType.Int},
{typeof (float),SqlDbType.Real},
{typeof (float?),SqlDbType.Real},
{typeof (Guid),SqlDbType.UniqueIdentifier},
{typeof (Guid?),SqlDbType.UniqueIdentifier},
{typeof (short),SqlDbType.SmallInt},
{typeof (short?),SqlDbType.SmallInt},
{typeof (byte),SqlDbType.TinyInt},
{typeof (byte?),SqlDbType.TinyInt},
{typeof (object),SqlDbType.Variant},
{typeof (DataTable),SqlDbType.Structured},
{typeof (DateTimeOffset),SqlDbType.DateTimeOffset}
};
public static SqlMetaData TypeToMetaData(string name, Type type)
{
SqlMetaData data = null;
if (type == typeof(string))
{
data = new SqlMetaData(name, SqlDbType.NVarChar, -1);
}
else
{
data = new SqlMetaData(name, TypeToSQLMap[type]);
}
return data;
}
}
Run Code Online (Sandbox Code Playgroud)
我的示例的SQL类型:
CREATE TYPE TestType AS TABLE (
FirstName NVARCHAR(255)
, GamerID INT
, LastName NVARCHAR(255)
, Salt UNIQUEIDENTIFIER);
GO
Run Code Online (Sandbox Code Playgroud)
使用它:
List<Gamer> gamers = new List<Gamer>();
gamers.Add(new Gamer {
Email = new string[] { "dsadsdsa@dasddas.com" },
FirstName = "Test_F0",
LastName = "Test_L0",
GamerID = 0,
Salt = Guid.NewGuid()});
gamers.Add(new Gamer {
Email = new string[] { "11111@11111.com" },
FirstName = "Test_F1",
LastName = "Test_L1",
GamerID = 1,
Salt = Guid.NewGuid()});
var structured = gamers.toTVP("GamerTable", "dbo.TestType");
using (var con = new SqlConnection(TestConnectionString))
{
con.Open();
string query = @"
SELECT *
FROM @GamerTable t
WHERE t.GamerID = 1
";
var result = con.Query(query, structured);
//var result = con.Query("dbo.DapperTest", structured, commandType: CommandType.StoredProcedure);
Run Code Online (Sandbox Code Playgroud)
正如你所看到的,模型剥离了电子邮件的字符串数组,因为我没有编写它来嵌套tvp.(TypeToSQLMap.ContainsKey部分),但可以编码,更改包装器以接受可枚举的参数和AddParameters来foreach并添加它们.更多的是关于类型名称等的问题.我正在考虑创建一些基于属性类型命名的泛型类型.现在,这已足够,如果我不这样做,请随时升级它.
我会在今天晚些时候尝试改进它.
对的,这是可能的。在 .NET Framework 中,您可以使用这些.AsTableValuedParameter extension方法,但在 .NET Core 中没有此选项(从 Dapper v 1.5 开始)要解决该问题,您必须创建一个实现ICustomQueryMapper以下内容的类:
public class TabledValuedParameter: ICustomQueryMapper
{
public void AddParameter() {...}
}
Run Code Online (Sandbox Code Playgroud)
然后你可以用它来包装你的 IEnumerable。我在这里写过关于这个主题的文章:
https://medium.com/dapper-net/sql-server-specific-features-2773d894a6ae
示例代码可在 GitHub 上获得:
https://github.com/yorek/dapper-samples/blob/master/Dapper.Samples.Advanced/SQLServerFeatures.cs
现在不再需要变通方法,因为 Dapper 2.0 TVP 是本机支持的。我刚刚更新了我的示例,现在一切正常。