working taghelper tag route not net form for data asp all c# sql reflection .net-core dapper

c# - taghelper - tag helpers asp net core



¿Hay una manera de pasar un TVP para dapper en.Net Core en este momento? (1)

Sí, es posible. En .NET Framework puede usar los métodos de .AsTableValuedParameter extension pero no tiene esta opción en .NET Core (a partir de Dapper v 1.5) Para resolver el problema, debe crear una clase que implemente ICustomQueryMapper :

public class TabledValuedParameter: ICustomQueryMapper { public void AddParameter() {...} }

Y luego puedes usarlo para envolver tu IEnumerable. He escrito un artículo sobre el tema aquí:

https://medium.com/dapper-net/sql-server-specific-features-2773d894a6ae

Y el código de muestra está disponible para GitHub:

https://github.com/yorek/dapper-samples/blob/master/Dapper.Samples.Advanced/SQLServerFeatures.cs

Estoy usando .net core y dapper, el primero no tiene DataTables y el segundo los usa para TVP.

Estaba tratando de convertir una List<T> en una List<SqlDataRecord> , crear un Parámetro Sql con esta lista y luego convertirlo en un DynamicParameter pero lamentablemente obtuve un: The member of type Microsoft.SqlServer.Server.SqlDataRecord cannot be used as a parameter value

ACTUALIZAR

Después de jugar un poco con IDynamicParameters , lo hice funcionar.

Método de extensión para 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); }

Envoltorio para implementar 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); } }

Asignador (no probado completamente, solo cadena administrada a NVARCHAR porque lanza una excepción sin 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; } }

Tipo de SQL para mi ejemplo:

CREATE TYPE TestType AS TABLE ( FirstName NVARCHAR(255) , GamerID INT , LastName NVARCHAR(255) , Salt UNIQUEIDENTIFIER); GO

Usándolo:

List<Gamer> gamers = new List<Gamer>(); gamers.Add(new Gamer { Email = new string[] { "[email protected]" }, FirstName = "Test_F0", LastName = "Test_L0", GamerID = 0, Salt = Guid.NewGuid()}); gamers.Add(new Gamer { Email = new string[] { "[email protected]" }, 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);

Como puede ver, el modelo eliminó la serie de cadenas para los correos electrónicos, porque no lo codifiqué para que anidara tvp. ( TypeToSQLMap.ContainsKey parte), pero podría codificarse, cambiando la envoltura para aceptar una enumerable de parámetros y AddParameters para foreach y agregarlos. Se trata más de un problema con los nombres de los tipos, etc. Estaba pensando en crear algunos tipos genéricos nombrados en función de los tipos de propiedad. Por ahora, esto es suficiente, siéntase libre de actualizarlo si no lo hago.

Trataré de mejorarlo un poco más tarde hoy.