Pergunta

Eu estou usando LINQ to SQL para o MySQL (usando DbLinq) em um site ASP.NET MVC. Eu tenho um problema de cache estranho. Considere os seguintes métodos na minha classe Repository:

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' é o por DbLinq mapeamento gerado para meu banco de dados, que herda de DataContext. Eu não estou reutilizando o datacontext aqui (o código acima parece um pouco bobo, mas eu queria ter certeza absoluta de que não era algum problema datacontext / MySqlConnection reutilização).

O que acontece é, um dos dois métodos que eu chamo, com tudo o que userId, os resultados permanecem os mesmos. Período. Mesmo que eu posso ver que repo.Messages tem mais de 10 resultados, com diferentes valores MessageFrom e MessageTo, eu só obter os resultados do primeiro consultados volta. Então, se eu chamar GetInbox(4374) dá-me mensagem de A e B. mensagem Chamando GetInbox(526) depois ainda me dá mensagem de A e B, embora haja são mensagens C e D que do Have um userId de 526. Eu tenho que reiniciar o aplicativo para ver as alterações.

O que está acontecendo aqui? Tenho certeza de que estou fazendo algo tão estúpido que eu vou ter vergonha quando alguém aponta-o para mim. Se eu não estou fazendo algo muito estúpido, então eu acho essa questão muito estranho. Eu li sobre não reutilizar DataContext, mas eu não sou. Por que esta questão cache? Abaixo está o meu código do controlador, mas eu duvido que é importante:

[Authorize]
public ActionResult Inbox(int userId)
{
  Mailbox inbox = new Mailbox(userId, this.messageRepository.GetInbox(userId));
  return PartialView("Inbox", inbox);
}

Embora existam perguntas semelhantes sobre SO, eu não encontrei uma resposta a esta pergunta exata. Muito obrigado!

Atualizar : mudar o código para: correções return repo.Messages.ToList().Where(m => m.MessageFrom == userId);-lo, ele funciona bem então. Parece que algum problema cache. No entanto, eu, naturalmente, não quer corrigi-lo dessa forma. Alterar o código para que o datacontext não é descartado após a consulta faz não corrigir o problema.

Foi útil?

Solução 4

Well, it seemed that it was a problem with DbLinq. I used source code from 3 weeks old and there was an apparant bug in QueryCache (though it has always been in there). There's a complete thread that covers this here.

I updated the dblinq source. Querycache is now disabled (does imply a performance hit) and well at least now it works. I'll have to see if the performance is acceptable. Must confess that I'm a bit baffled though as what I'm trying to do is a common linq2sql pattern. Thanks all.

Outras dicas

I wrote some pretty similar code that seems to work fine. The only difference is that as Marc suggests, I'm passing in the connection string and calling ToList on the Where method. My Database is not automatically generated but derives from DataContext. The code is below.

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(); ;
        }
    }
}

Start of by writing a test. This will tell you wether Linq2Sql is behaving correctly. Something like:

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);

If that succeeds, you should really check wether it's the deferred execution that's causing problems. You should enumerate inboxMessages right away.

Another thing that might be causing trouble, is the fact that you start enumerating when the datacontext is already disposed. The only way to solve this, is not to dispose it at all (and rely on the GC cleaning it up when it goes out of scope), or come up with a custom IDisposable object, so you can put a using around it. Something like:

using(var inboxMessages = this.messageRepository.GetInbox(userId1))
{
    Assert.That(inboxMessages.All(m => m.MessageTo == userId1);
}

Caching in LINQ-to-SQL is associated with the DataContext, and is mainly limited to identity caching - in most cases it will re-run a query even if you've done it before. There are a few examples, like .Single(x=>x.Id == id) (which has special handling).

Since you are clearly getting a new data-context each time, I don't think that is the culprit. However, I'm also slightly surprised that the code works... are you sure that is representative?

LINQ's Where method is deferred - meaning it isn't executed until you iterate the data (for example with foreach). But by that time you have already disposed the data-context! Have you snipped something from the example?

Also - by giving it a SqlConnection (that you don't then Dispose()), you may be impacting the cleanup - it may be preferable to just give it (the data-context) the connection string.

I'd avoid using DBLinq for production code... many of Linq-To-SQL's features aren't implemented, and walking through the source code shows a low level of maturity... many of the methods are not implemented or marked as "unterminated".

...you've been warned!

Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top