DbLinq - проблема с кэшем
-
11-09-2019 - |
Вопрос
Я использую linq to sql для MySQL (используя DbLinq) на веб-сайте ASP.NET MVC.У меня странная проблема с кэшированием.Рассмотрим следующие методы в моем классе репозитория:
public IEnumerable<Message> GetInbox(int userId)
{
using(MyDataContext repo = new MyDataContext(new MySqlConnection("[Connectionstring]")))
{
return repo.Messages.Where(m => m.MessageTo == userId);
}
}
public IEnumerable<Message> GetOutbox(int userId)
{
using (MyDataContext repo = new MyDataContext(new MySqlConnection("[Connectionstring]")))
{
return repo.Messages.Where(m => m.MessageFrom == userId);
}
}
'MyDataContext' - это сгенерированное DbLinq сопоставление с моей базой данных, которое наследуется от DataContext.Я не использую здесь datacontext повторно (приведенный выше код выглядит немного глупо, но я хотел быть абсолютно уверен, что это не какая-то проблема с повторным использованием datacontext / mysqlconnection).
Что происходит, так это то, какой бы из двух методов я ни вызывал, с каким бы идентификатором пользователя результаты остаются неизменными.Точка.Даже несмотря на то, что я вижу, что repo.Messages
имеет более 10 результатов с различной MessageFrom
и MessageTo
значения, я получаю обратно только результаты первого запроса.Так что, если я позвоню GetInbox(4374)
это дает мне сообщение A и сообщение B.Зовущий GetInbox(526)
впоследствии все еще выдает мне сообщения A и B, даже несмотря на то, что там являются сообщения C и D, которые делай имейте идентификатор пользователя 526.Я должен перезапустить приложение, чтобы увидеть какие-либо изменения.
Что здесь происходит?Я уверен, что делаю что-то настолько глупое, что мне будет стыдно, когда кто-нибудь укажет мне на это.Если я не делаю что-то очень глупое, то я нахожу эту проблему очень странной.Я читал о том, что нельзя повторно использовать DataContext, но я этого не делаю.Почему эта проблема с кэшированием?Ниже приведен мой код контроллера, но я сомневаюсь, что это имеет значение:
[Authorize]
public ActionResult Inbox(int userId)
{
Mailbox inbox = new Mailbox(userId, this.messageRepository.GetInbox(userId));
return PartialView("Inbox", inbox);
}
Хотя на SO есть похожие вопросы, я не нашел ответа именно на этот вопрос.Большое спасибо!
Обновить:изменение кода на: return repo.Messages.ToList().Where(m => m.MessageFrom == userId);
исправляет это, тогда все работает нормально.Похоже, какая-то проблема с кэшем.Однако я, конечно, не хочу исправлять это таким образом.Изменение кода таким образом, чтобы datacontext не удалялся после выполнения запроса не устраните проблему.
Решение 4
Ну, похоже, это была проблема с DbLinq.Я использовал исходный код 3-недельной давности, и в QueryCache была обнаружена явная ошибка (хотя она имеет всегда был там).Есть полная тема, которая охватывает это здесь.
Я обновил исходный код dblinq.Кэш запросов теперь отключен (подразумевает снижение производительности), и хорошо, по крайней мере, теперь он работает.Я должен посмотреть, приемлема ли производительность.Должен признаться, что я немного сбит с толку, поскольку то, что я пытаюсь сделать, является общим шаблоном linq2sql.Спасибо всем.
Другие советы
Я написал довольно похожий код, который, кажется, работает нормально.Единственное отличие заключается в том, что, как предлагает Марк, я передаю строку подключения и вызываю ToList в методе Where.Моя база данных не генерируется автоматически, а является производной от DataContext.Код приведен ниже.
class Program
{
static void Main(string[] args)
{
List<Item> first = GetItems("F891778E-9C87-4620-8AC6-737F6482CECB").ToList();
List<Item> second = GetItems("7CA18DD1-E23B-41AA-871B-8DEF6228F96C").ToList();
Console.WriteLine(first.Count);
Console.WriteLine(second.Count);
Console.Read();
}
static IEnumerable<Item> GetItems(string vendorId)
{
using (Database repo = new Database(@"connection_string_here"))
{
return repo.GetTable<Item>().Where(i => i.VendorId.ToString() == vendorId).ToList(); ;
}
}
}
Начните с написания теста.Это подскажет вам, правильно ли работает Linq2SQL.Что - то вроде:
var inboxMessages = this.messageRepository.GetInbox(userId1);
Assert.That(inboxMessages.All(m => m.MessageTo == userId1);
inboxMessages = this.messageRepository.GetInbox(userid2);
Assert.That(inboxMessages.All(m => m.MessageTo = userid2);
Если это удастся, вам действительно следует проверить, не является ли причиной проблем отложенное выполнение.Вы должны сразу же перечислить inboxMessages.
Еще одна вещь, которая может вызвать проблемы, - это тот факт, что вы начинаете перечислять, когда datacontext уже удален.Единственный способ решить эту проблему - вообще не утилизировать его (и полагаться на то, что GC очистит его, когда он выйдет за пределы области видимости), или создать пользовательский объект IDisposable, чтобы вы могли использовать его.Что - то вроде:
using(var inboxMessages = this.messageRepository.GetInbox(userId1))
{
Assert.That(inboxMessages.All(m => m.MessageTo == userId1);
}
Кэширование в LINQ-to-SQL связано с DataContext
, и в основном ограничивается кэшированием идентификационных данных - в большинстве случаев он повторно запустит запрос, даже если вы делали это раньше.Есть несколько примеров, таких как .Single(x=>x.Id == id)
(который имеет специальное обращение).
Поскольку вы явно каждый раз получаете новый контекст данных, я не думаю, что это виновато.Тем не менее, я также немного удивлен, что код работает...вы уверены, что это репрезентативно?
LINQ's Where
метод отложен - это означает, что он не выполняется до тех пор, пока вы не выполните итерацию данных (например, с помощью foreach
).Но к тому времени вы уже избавились от контекста данных!Вы что-нибудь вырезали из примера?
Также - придав ему SqlConnection
(чего вы тогда не делаете Dispose()
), возможно, вы влияете на очистку - возможно, предпочтительнее просто предоставить ей (контексту данных) строку подключения.
Я бы предпочел не использовать DbLinq для производственного кода...многие функции Linq-To-SQL не реализованы, а ознакомление с исходным кодом показывает низкий уровень зрелости...многие из методов не реализованы или помечены как "незавершенные".
...вы были предупреждены!