我试图从EF5中的存储过程中获取多个结果集。我试图减少我们平台中最昂贵的功能的数据库往返次数。
我们认为通过服务器发送事件进行更新。当视图数据更改时,每个用户都需要使用更新的数据接收其自定义视图。我们当前的方法是获取所有连接的用户,然后从每个用户的数据库中获取他们的视图。因为这非常昂贵,我们试图通过对存储过程执行一次调用来返回多个结果,从而使它更便宜一些。
我有以下代码:
var sql = "DECLARE @users UserList INSERT INTO @users(row) VALUES {0}" +
"EXEC dbo.GetTracklistViews(@users, '{1}');";
var sql_shard = "('{0}'),";
var sql_shard_last = "('{0}')";
var users = new List<Guid>();
//Logic to get all connected users
var builder = new StringBuilder();
for (var i = 0; i < users.Count; i++)
if (i < (users.Count - 1))
builder.Append(string.Format(sql_shard, users[i]));
else
builder.Append(string.Format(sql_shard_last, users[i]));
using (var ctx = new spottyData())
{
ctx.Database.Initialize(force: false);
ctx.Database.Connection.Open();
using (var cmd = ctx.Database.Connection.CreateCommand())
{
cmd.CommandText = string.Format(sql, builder.ToString(), juke.ToString());
var result = cmd.ExecuteReader();
while (result.NextResult())
{
var set = ((IObjectContextAdapter)ctx)
.ObjectContext
.Translate<tracklist>(result, "tracklist", System.Data.Objects.MergeOption.NoTracking)
.ToArray();
//Send event with 'set' data
}
}
}
在var result = cmd.ExecuteReader();
,它会抛出SqlException
:
@ users&#39;附近的语法不正确。
我们之前使用相同的语法在其他几个存储过程和函数中输入多个值。一切似乎都很好。我认为这与EXEC dbo.GetTracklistViews...
有关。
如果你们中的任何一个人认为我们在这里尝试完全疯狂的事情,还有其他任何方法可以实现这一目标吗?或者我们是否必须寻找其他方法来削减昂贵的功能?
UserList
类型的定义:
CREATE TYPE UserList
AS TABLE (
[row] INT IDENTITY(1,1) PRIMARY KEY,
[user_id] UNIQUEIDENTIFIER
);
答案 0 :(得分:1)
首先 - 您不应该在字符串中使用逗号分隔的值列表作为参数。 SQL服务器解析和拆分它的效率非常低。相反,您的存储过程应使用Table值参数。
其次 - 尽量避免使用多个结果集。它非常凌乱。 由于您的所有结果集都是相同类型的表,因此我只创建一个新类(例如将其称为UserTrackList) - 具有与Tracklist相同的属性+ UserId的1个。它甚至可以从TrackList继承
例如:
要传入的表值类型:
CREATE TYPE GuidList
AS TABLE (
[id] UNIQUEIDENTIFIER -- < assume thats what you're actually passing in?
);
存储过程定义
CREATE PROCEDURE dbo.GetTracklistViews
@users GuidList READONLY,
@juke NVARCHAR(1000) -- < I have no idea what this parameter is!
AS
BEGIN
-- SELECT a load of stuff - joined onto this table valued parameter?
END
如何在C#中调用该过程
var sql = "EXEC dbo.GetTrackListViews @users, @juke";
var prms = new List<System.Data.SqlClient.SqlParameter>();
//build table valued parameter
var tbl = new DataTable();
tbl.Columns.Add(new DataColumn("id", typeof(guid));
foreach(var user in users) //assume these are guids.
{ tbl.Rows.Add(user) }
var prm1 = new System.Data.SqlClient.SqlParameter("users", SqlDbType.Structured);
prm1.Value = tbl;
prm1.TypeName = "GuidList";
prms.Add(prm1);
// other parameter is easy:
var prm2 = new System.Data.SqlClient.SqlParameter("juke", juke.ToString());
prms.Add(prm2);
List<UserTrackList> results;
using (var ctx = new spottyData())
{
var query = ctx.Database.SqlQuery<UserTrackList>(sql, prms.ToArray());
results = query.ToList();
// NB - this works if the property names/types in the type "tracklist"
// match the column names/types in the returned resultset.
}
// example processing the results
foreach (var tracklist in results.GroupBy(x => x.UserId)
{
user = users.First(x => x == tracklist.Key);
//do something with the grouped result
}