Puis-je exécuter plusieurs requêtes et renvoyer leurs résultats en exécutant SqlCommand
une seule fois?
Voir SqlDataReader.NextResult (un SqlDataReader est renvoyé par l'appelant SqlCommand.ExecuteReader ):
Avance le lecteur de données vers le résultat suivant [set], lors de la lecture des résultats d'instructions Transact-SQL par lots.
Exemple:
string commandText = @"SELECT Id, ContactId
FROM dbo.Subscriptions;
SELECT Id, [Name]
FROM dbo.Contacts;";
List<Subscription> subscriptions = new List<Subscription>();
List<Contact> contacts = new List<Contact>();
using (SqlConnection dbConnection = new SqlConnection(@"Data Source=server;Database=database;Integrated Security=true;"))
using (SqlCommand dbCommand = new SqlCommand(commandText, dbConnection))
{
dbCommand.CommandType = CommandType.Text;
dbConnection.Open();
SqlDataReader reader = dbCommand.ExecuteReader();
while (reader.Read())
{
subscriptions.Add(
new Subscription()
{
Id = (int)reader["Id"],
ContactId = (int)reader["ContactId"]
});
}
reader.NextResult();
while (reader.Read())
{
contacts.Add(
new Contact()
{
Id = (int)reader["Id"],
Name = (string)reader["Name"]
});
}
dbConnection.Close();
}
Autres exemples:
Créez un Stored Procedure
avec plusieurs sélections et remplissez la DataSet
.
using (SqlConnection conn = new SqlConnection(connection))
{
DataSet dataset = new DataSet();
SqlDataAdapter adapter = new SqlDataAdapter();
adapter.SelectCommand = new SqlCommand("MyProcedure", conn);
adapter.SelectCommand.CommandType = CommandType.StoredProcedure;
adapter.Fill(dataset);
return dataset;
}
L'ensemble de données renvoyé aura une DataTable
dans son tableau Tables pour chaque sélection dans la procédure stockée.
Des outils comme "dapper" rendent cela très facile, que vous utilisiez des requêtes de texte ad-hoc ou des procédures stockées; par exemple:
using(var multi = conn.QueryMultiple(sql, args))
{
var customers = multi.Read<Customer>().AsList(); // first grid
var regionName = multi.ReadFirstOrDefault<string>(); // second grid
var addresses = multi.Read<Address>().AsList(); // third grid
// todo: use those things
}
Les grilles individuelles peuvent également être lues sans / mise en mémoire tampon (en tant que IEnumerable<T>
ouvert sur le lecteur lui-même) via les paramètres facultatifs à Read[<T>]
.
C'est ce que j'utilisais pour renvoyer plusieurs jeux de résultats.
public abstract class BaseRepo
{
private string _connectionString;
protected BaseRepo(string connectionString)
{
_connectionString = connectionString;
}
private SqlConnection GetSqlConnection(int commandTimeout, CommandType commandType, ref SqlCommand sqlCmd)
{
var connection = new SqlConnection(_connectionString);
connection.Open();
sqlCmd.Connection = connection;
sqlCmd.CommandTimeout = commandTimeout;
sqlCmd.CommandType = commandType;
return connection;
}
protected int ExecuteSql(SqlCommand sqlCmd, int commandTimeout = 30, CommandType commandType = CommandType.Text)
{
using (var connection = GetSqlConnection(commandTimeout, commandType, ref sqlCmd))
{
return sqlCmd.ExecuteNonQuery();
}
}
protected IEnumerable<T> ExecuteSqlReader<T>(Func<IDataRecord, T> CreateObject, SqlCommand sqlCmd, int commandTimeout = 30, CommandType commandType = CommandType.Text)
{
using (var connection = GetSqlConnection(commandTimeout, commandType, ref sqlCmd))
{
using (var reader = sqlCmd.ExecuteReader())
return ExecuteReader(CreateObject, reader);
}
}
protected Tuple<IEnumerable<T1>, IEnumerable<T2>> ExecuteSqlReader<T1,T2>(Func<IDataRecord, T1> CreateObject1, Func<IDataRecord, T2> CreateObject2, SqlCommand sqlCmd, int commandTimeout = 30, CommandType commandType = CommandType.Text)
{
using (var connection = GetSqlConnection(commandTimeout, commandType, ref sqlCmd))
{
using (var reader = sqlCmd.ExecuteReader())
{
var result1 = ExecuteReader(CreateObject1, reader).ToList();
var result2 = ExecuteReader(CreateObject2, reader).ToList();
return Tuple.Create<IEnumerable<T1>, IEnumerable<T2>>(result1, result2);
}
}
}
protected Tuple<IEnumerable<T1>, IEnumerable<T2>, IEnumerable<T3>> ExecuteSqlReader<T1, T2, T3>(Func<IDataRecord, T1> CreateObject1, Func<IDataRecord, T2> CreateObject2, Func<IDataRecord, T3> CreateObject3, SqlCommand sqlCmd, int commandTimeout = 30, CommandType commandType = CommandType.Text)
{
using (var connection = GetSqlConnection(commandTimeout, commandType, ref sqlCmd))
{
using (var reader = sqlCmd.ExecuteReader())
{
var result1 = ExecuteReader(CreateObject1, reader).ToList();
var result2 = ExecuteReader(CreateObject2, reader).ToList();
var result3 = ExecuteReader(CreateObject3, reader).ToList();
return Tuple.Create<IEnumerable<T1>, IEnumerable<T2>, IEnumerable<T3>>(result1, result2, result3);
}
}
}
protected Tuple<IEnumerable<T1>, IEnumerable<T2>, IEnumerable<T3>, IEnumerable<T4>> ExecuteSqlReader<T1, T2, T3, T4>(Func<IDataRecord, T1> CreateObject1, Func<IDataRecord, T2> CreateObject2, Func<IDataRecord, T3> CreateObject3, Func<IDataRecord, T4> CreateObject4, SqlCommand sqlCmd, int commandTimeout = 30, CommandType commandType = CommandType.Text)
{
using (var connection = GetSqlConnection(commandTimeout, commandType, ref sqlCmd))
{
using (var reader = sqlCmd.ExecuteReader())
{
var result1 = ExecuteReader(CreateObject1, reader).ToList();
var result2 = ExecuteReader(CreateObject2, reader).ToList();
var result3 = ExecuteReader(CreateObject3, reader).ToList();
var result4 = ExecuteReader(CreateObject4, reader).ToList();
return Tuple.Create<IEnumerable<T1>, IEnumerable<T2>, IEnumerable<T3>, IEnumerable<T4>>(result1, result2, result3, result4);
}
}
}
private IEnumerable<T> ExecuteReader<T>(Func<IDataRecord, T> CreateObject, SqlDataReader reader)
{
while (reader.Read())
{
yield return CreateObject(reader);
}
reader.NextResult();
}
}
Ensuite, je viens d'hériter comme ceci:
public class ReviewRepo : BaseRepo
{
public ReviewRepo(string connectionString) : base(connectionString) { }
public ReviewPageableResult GetAllReviews(string productType, string serviceType, int pageNumber, int itemsPerPage, string sortBy, string sortDirection)
{
var parameters = new List<SqlParameter>
{
new SqlParameter("ProductRefDescription", productType),
new SqlParameter("ServiceRefDescription", serviceType),
new SqlParameter("ZipCodes", "NULL"),
new SqlParameter("PageNumber", pageNumber),
new SqlParameter("ItemsPerPage", itemsPerPage),
new SqlParameter("SortBy", sortBy),
new SqlParameter("SortDirection", sortDirection)
};
var cmd = new SqlCommand("dbo.GetReviews");
cmd.Parameters.AddRange(parameters.ToArray());
var results = ExecuteSqlReader(CreateReview, CreateReviewPageableResult, cmd, commandType: CommandType.StoredProcedure);
var reviewResult = results.Item2.Single();
reviewResult.Items = results.Item1;
return reviewResult;
}
public ReviewPageableResult GetReviewsByZip(string productType, string serviceType, string zipCodes, int pageNumber, int itemsPerPage, string sortBy, string sortDirection)
{
var parameters = new List<SqlParameter>
{
new SqlParameter("ProductRefDescription", productType),
new SqlParameter("ServiceRefDescription", serviceType),
new SqlParameter("ZipCodes", zipCodes),
new SqlParameter("PageNumber", pageNumber),
new SqlParameter("ItemsPerPage", itemsPerPage),
new SqlParameter("SortBy", sortBy),
new SqlParameter("SortDirection", sortDirection)
};
var cmd = new SqlCommand("dbo.GetReviewsByZipCodes");
cmd.Parameters.AddRange(parameters.ToArray());
var results = ExecuteSqlReader(CreateReview, CreateReviewPageableResult, cmd, commandType: CommandType.StoredProcedure);
var reviewResult = results.Item2.Single();
reviewResult.Items = results.Item1;
return reviewResult;
}
private Review CreateReview(IDataRecord record)
{
return new Review
{
PageReviewId = (int)record["PageReviewId"],
ProductRefId = (Guid)record["ProductRefId"],
ServiceTypeRefId = Convert.IsDBNull(record["ServiceTypeRefId"]) ? Guid.Empty : (Guid)record["ServiceTypeRefId"],
TerritoryId = Convert.IsDBNull(record["TerritoryId"]) ? Guid.Empty : (Guid)record["TerritoryId"],
FirstName = $"{record["FirstName"]}",
LastName = $"{record["LastName"]}",
City = $"{record["City"]}",
State = $"{record["State"]}",
Answer = $"{record["Answer"]}",
Rating =(double)record["Rating"],
SurveyDate = (DateTime)record["SurveyDate"]
};
}
private ReviewPageableResult CreateReviewPageableResult(IDataRecord record)
{
return new ReviewPageableResult
{
AverageRating = (double)record["AverageRating"],
Count1Stars = (int)record["Count1Stars"],
Count2Stars = (int)record["Count2Stars"],
Count3Stars = (int)record["Count3Stars"],
Count4Stars = (int)record["Count4Stars"],
Count5Stars = (int)record["Count5Stars"],
ItemsPerPage = (int)record["ItemsPerPage"],
PageNumber = (int)record["PageNumber"],
TotalCount = (int)record["TotalCount"],
};
}
}
J'appelle un sproc et j'obtiens plusieurs résultats avec un objet, de sorte que vous vous retrouvez avec un
List<List<Dictionary<string, object>>>
Dans le multiResultsSet, chaque jeu de résultats est alors
List<Dictionary<string, object>>
Ils peuvent être convertis en leurs types et transformés en modèles si nécessaire.
Après avoir configuré la commande sproc avec tout le nécessaire, transmettez-la à ceci:
private static List<List<Dictionary<string, object>>> ProcessReader(SqlCommand command)
{
var tables = new List<List<Dictionary<string, object>>>();
using (var reader = command.ExecuteReader())
{
do
{
var table = new List<Dictionary<string, object>>();
while (reader.Read())
table.Add(Read(reader));
tables.Add(table);
} while (reader.NextResult());
}
return tables;
}
et Read () est assez simple.
private static Dictionary<string, object> Read(IDataRecord reader)
{
var row = new Dictionary<string, object>();
for (var i = 0; i < reader.FieldCount; i++)
{
var val = reader[i];
row[reader.GetName(i)] = val == DBNull.Value ? null : val;
}
return row;
}