Если вы читаете how Query Notifications work, вы увидите, почему создание многих запросов зависимостей с помощью одного шаблона запроса является хорошей практикой. Для веб-приложения, которое подразумевается в том, что вы используете SqlCacheDependency
, а не SqlDependency
, то, что вы планируете делать, должно быть в порядке. Если вы используете Linq2SQL вы также можете попробовать LinqToCache:
var queryUsers = from u in repository.Users
where u.UserId = currentUserId
select u;
var user= queryUsers .AsCached("Users:" + currentUserId.ToString());
Для жирной клиентского приложения не будет ОК. Не из запроса на-SE, а потому, что SqlDependency
в целом является проблематичным с большим количеством клиентов, подключенных (он блокирует a worker thread для каждого домена приложения подключено):
SqlDependency был разработан для использования в ASP. NET или средний уровень услуг, где имеется относительно небольшое количество серверов, имеющих зависимостей, активных по отношению к базе данных. Он не был предназначен для использования в клиентских приложениях, где сотни или тысячи клиентов компьютеров имели бы объекты SqlDependency, настроенные для одного сервера базы данных .
Обновлено
Вот тот же тест, как @usr сделал на своем посту. Полный C# код:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Data.SqlClient;
using DependencyMassTest.Properties;
using System.Threading.Tasks;
using System.Threading;
namespace DependencyMassTest
{
class Program
{
static volatile int goal = 50000;
static volatile int running = 0;
static volatile int notified = 0;
static int workers = 50;
static SqlConnectionStringBuilder scsb;
static AutoResetEvent done = new AutoResetEvent(false);
static void Main(string[] args)
{
scsb = new SqlConnectionStringBuilder(Settings.Default.ConnString);
scsb.AsynchronousProcessing = true;
scsb.Pooling = true;
try
{
SqlDependency.Start(scsb.ConnectionString);
using (var conn = new SqlConnection(scsb.ConnectionString))
{
conn.Open();
using (SqlCommand cmd = new SqlCommand(@"
if object_id('SqlDependencyTest') is not null
drop table SqlDependencyTest
create table SqlDependencyTest (
ID int not null identity,
SomeValue nvarchar(400),
primary key(ID)
)
", conn))
{
cmd.ExecuteNonQuery();
}
}
for (int i = 0; i < workers; ++i)
{
Task.Factory.StartNew(
() =>
{
RunTask();
});
}
done.WaitOne();
Console.WriteLine("All dependencies subscribed. Waiting...");
Console.ReadKey();
}
catch (Exception e)
{
Console.Error.WriteLine(e);
}
finally
{
SqlDependency.Stop(scsb.ConnectionString);
}
}
static void RunTask()
{
Random rand = new Random();
SqlConnection conn = new SqlConnection(scsb.ConnectionString);
conn.Open();
SqlCommand cmd = new SqlCommand(
@"select SomeValue
from dbo.SqlDependencyTest
where ID = @id", conn);
cmd.Parameters.AddWithValue("@id", rand.Next(50000));
SqlDependency dep = new SqlDependency(cmd);
dep.OnChange += new OnChangeEventHandler((ob, qnArgs) =>
{
Console.WriteLine("Notified {3}: Info:{0}, Source:{1}, Type:{2}", qnArgs.Info, qnArgs.Source, qnArgs.Type, Interlocked.Increment(ref notified));
});
cmd.BeginExecuteReader(
(ar) =>
{
try
{
int crt = Interlocked.Increment(ref running);
if (crt % 1000 == 0)
{
Console.WriteLine("{0} running...", crt);
}
using (SqlDataReader rdr = cmd.EndExecuteReader(ar))
{
while (rdr.Read())
{
}
}
}
catch (Exception e)
{
Console.Error.WriteLine(e.Message);
}
finally
{
conn.Close();
int left = Interlocked.Decrement(ref goal);
if (0 == left)
{
done.Set();
}
else if (left > 0)
{
RunTask();
}
}
}, null);
}
}
}
После 50k подписки устанавливается (занимает около 5 минут), вот статистика ИО одной вставки:
set statistics time on
insert into Test..SqlDependencyTest (SomeValue) values ('Foo');
SQL Server parse and compile time:
CPU time = 0 ms, elapsed time = 0 ms.
SQL Server Execution Times:
CPU time = 16 ms, elapsed time = 16 ms.
Вставка 1000 строк занимает около 7 секунд, что включает в себя запуск нескольких сотен уведомлений. Загрузка процессора составляет около 11%. Все это на моем ThinkPad T420s.
set nocount on;
go
begin transaction
go
insert into Test..SqlDependencyTest (SomeValue) values ('Foo');
go 1000
commit
go
Я боюсь, что кэширование всех этих пользовательских данных даже для отключенных пользователей может быть слишком большим, любые альтернативы? –
SqlCacheDependency имеет политики выселения кеша. –
Это в основном тот же результат, что и мой тест. Создание вашей зависимости многопоточно, поэтому оно быстрее. Возможно, в 4 раза? 7s для 1000 вставок * ужасные *. 11% cpu = 1 ядро при 100%. – usr