Debugged. As I postulated in the 'Re: new release' thread this morning,
the problem is that the cached SelectQuery also stores the parameters to
the SELECT, so subsequent requests on the same expression get the same
results, even though the parameters are different.
The problem is getting the "same expression," which took me longer to
figure out than I'd have hoped. See the attached patch, which turns
ReadTest.A5_SelectSingleOrDefault() from a passing test into a failing
test. It passes under .NET's Linq to SQL, and fails under DbLinq's SQL
Server provider.
The breakage? Turning this:
id = "BLAUS";
cust = db.Customers.SingleOrDefault(c => c.CustomerID == id);
into this:
cust = GetCustomerById(db, "BLAUS");
// ...
private static Customer GetCustomerById(Northwind db, string id)
{
return db.Customers.SingleOrDefault(c => c.CustomerID == id);
}
Which to most people would look identical, but isn't, for reasons I
can't actually fathom.
Regardless, I've figured out how to easily reproduce (yay), and what the
actual problem is (input parameters stored in the cached object -- yay).
The solution? I haven't figured that bit out yet. I'm open to
suggestions.
However, it does show one further benefit of the CompiledQuery.Compile()
approach: it can also cache the result of the expression tree creation,
which QueryCache can't avoid (as it uses the expression tree as the key
to its cache).
- Jon
On Mon, 2009-05-11 at 13:25 -0400, Jonathan Pryor wrote:
> No, it's running under xsp2 on Mono/Linux. I don't think the web
> server is the problem, though. (At least, I can't think of any way in
> which the web server would be the problem.)
>
> I'll obviously need to debug this further. :-/
>
> - Jon
>
> On Mon, 2009-05-11 at 16:09 +0200, Giacomo Tesio wrote:
>
> > I've tried some test, and it seemed to be working.
> >
> > At least, it's not what I was thinking.
> >
> > Have you finally found a failing test for this use case?
> > It seem related to web environment. Was it running on IIS?
> >
> >
> > Giacomo
> >
> > On Mon, May 11, 2009 at 12:26 AM, Giacomo Tesio <[email protected]>
> > wrote:
> >
> > As far as I remember, some week ago I've "fixed" a bug about
> > the querycache: in the expression comparer I compare
> > expression.ToString().
> >
> > If the expression cached contains the parameter value too,
> > this "fix" would be a bug.
> >
> > But, the fact itself that the query cache contains
> > parameter's values would make the query cache far less
> > useful than what it should be.
> >
> >
> >
> > Giacomo
> >
> >
> >
> >
> > On Fri, May 8, 2009 at 12:47 PM, Pascal Craponne
> > <[email protected]> wrote:
> >
> > Nope, but I can testify that this cache worked, when
> > I wrote it :)
> >
> >
> >
> > On Fri, May 8, 2009 at 00:57, Jonathan Pryor
> > <[email protected]> wrote:
> >
> > I think QueryCache is broken. I'm quite
> > confused about the whole thing, actually...
> >
> > What I'm trying to do is "port" NerdDinner
> > to Mono/Linux. In an ideal world, nothing
> > would need to change, but this isn't an
> > ideal world (as I'm finding plenty of issues
> > in Mono's System.Data.Linq i.e. DbLinq to
> > keep me less than happy, e.g. the .Count()
> > issue I asked about a few days ago).
> >
> > So what I'm seeing happen is this: the url
> > e.g. http://localhost:8080/Dinners/Details/1
> > contains detail information about the dinner
> > with ID 1. Simple enough. The problem is
> > after I hit this URL, if I go to any other
> > dinner detail page (e.g.
> > http://localhost:8080/Dinners/Details/2), I
> > get the information for the first page I
> > visited.
> >
> > This is, of course, incredibly annoying.
> >
> > What makes me think QueryCache is the
> > problem is that if I change
> > QueryCache.GetFromSelectCache() to always
> > return null (i.e. kill the cache),
> > everything Just Works.
> >
> > So I'm quite sure that QueryCache is the
> > problem. What I'm not sure about is why I
> > can't reproduce this in the unit tests
> > (which is really annoying).
> >
> > What I do know is that when I view the log
> > output, the generated queries have the wrong
> > values. For example, my annotated log
> > output:
> >
> > # Went to:
> > http://localhost:8080/Dinners/Details/1
> > # Getting Dinner: 1
> > SELECT TOP (2) [DinnerID], [Title],
> > [EventDate], [Description], [HostedBy], [ContactPhone], [Address],
> > [Country], [Latitude], [Longitude]
> > FROM [dbo].[Dinners]
> > WHERE [DinnerID] = @id
> > -- @id: Input Int32 (Size = 4; Prec = 0;
> > Scale = 0) [1]
> > -- Context: SqlServer Model:
> > AttributedMetaModel Build: 3.5.0.0
> > # Got Dinner: 1
> >
> > # Now, within DinnerRepository.GetDinner(),
> > I sanity check with:
> > # id = 3;
> > # var a = db.Dinners.Single(d =>
> > d.DinnerID == id);
> > # id = 4;
> > # var b = db.Dinners.Single(d =>
> > d.DinnerID == id);
> > # Console.Error.WriteLine("#
> > a='{0}'; b='{1}'", a.DinnerID, b.DinnerID);
> > # Sanity check output...
> > SELECT TOP (2) [DinnerID], [Title],
> > [EventDate], [Description], [HostedBy], [ContactPhone], [Address],
> > [Country], [Latitude], [Longitude]
> > FROM [dbo].[Dinners]
> > WHERE [DinnerID] = @id
> > -- @id: Input Int32 (Size = 4; Prec = 0;
> > Scale = 0) [3]
> > -- Context: SqlServer Model:
> > AttributedMetaModel Build: 3.5.0.0
> > SELECT TOP (2) [DinnerID], [Title],
> > [EventDate], [Description], [HostedBy], [ContactPhone], [Address],
> > [Country], [Latitude], [Longitude]
> > FROM [dbo].[Dinners]
> > WHERE [DinnerID] = @id
> > -- @id: Input Int32 (Size = 4; Prec = 0;
> > Scale = 0) [4]
> > -- Context: SqlServer Model:
> > AttributedMetaModel Build: 3.5.0.0
> > # a='3'; b='4'
> > # OK, sanity check is sane.
> >
> > # At this point, things are sane.
> >
> > # Went to:
> > http://localhost:8080/Dinners/Details/2
> > # Getting Dinner: 2
> > SELECT TOP (2) [DinnerID], [Title],
> > [EventDate], [Description], [HostedBy], [ContactPhone], [Address],
> > [Country], [Latitude], [Longitude]
> > FROM [dbo].[Dinners]
> > WHERE [DinnerID] = @id
> > -- @id: Input Int32 (Size = 4; Prec = 0;
> > Scale = 0) [4]
> > -- Context: SqlServer Model:
> > AttributedMetaModel Build: 3.5.0.0
> > # Got Dinner: 4
> > # Sanity check...
> > SELECT TOP (2) [DinnerID], [Title],
> > [EventDate], [Description], [HostedBy], [ContactPhone], [Address],
> > [Country], [Latitude], [Longitude]
> > FROM [dbo].[Dinners]
> > WHERE [DinnerID] = @id
> > -- @id: Input Int32 (Size = 4; Prec = 0;
> > Scale = 0) [4]
> > -- Context: SqlServer Model:
> > AttributedMetaModel Build: 3.5.0.0
> > SELECT TOP (2) [DinnerID], [Title],
> > [EventDate], [Description], [HostedBy], [ContactPhone], [Address],
> > [Country], [Latitude], [Longitude]
> > FROM [dbo].[Dinners]
> > WHERE [DinnerID] = @id
> > -- @id: Input Int32 (Size = 4; Prec = 0;
> > Scale = 0) [4]
> > -- Context: SqlServer Model:
> > AttributedMetaModel Build: 3.5.0.0
> > # a='4'; b='4'
> >
> > # Uh, wtf? Notice that even though we're
> > going to 2,
> > # we see [4] in the output. Furthermore,
> > my
> > # NerdDinner.GetDinner() sanity check is
> > also returning
> > # the *same* Dinner, even though it
> > shouldn't.
> >
> > # Went to:
> > http://localhost:8080/Dinners/Details/3
> > # Getting Dinner: 3
> > SELECT TOP (2) [DinnerID], [Title],
> > [EventDate], [Description], [HostedBy], [ContactPhone], [Address],
> > [Country], [Latitude], [Longitude]
> > FROM [dbo].[Dinners]
> > WHERE [DinnerID] = @id
> > -- @id: Input Int32 (Size = 4; Prec = 0;
> > Scale = 0) [4]
> > -- Context: SqlServer Model:
> > AttributedMetaModel Build: 3.5.0.0
> > # Got Dinner: 4
> > SELECT TOP (2) [DinnerID], [Title],
> > [EventDate], [Description], [HostedBy], [ContactPhone], [Address],
> > [Country], [Latitude], [Longitude]
> > FROM [dbo].[Dinners]
> > WHERE [DinnerID] = @id
> > -- @id: Input Int32 (Size = 4; Prec = 0;
> > Scale = 0) [4]
> > -- Context: SqlServer Model:
> > AttributedMetaModel Build: 3.5.0.0
> > SELECT TOP (2) [DinnerID], [Title],
> > [EventDate], [Description], [HostedBy], [ContactPhone], [Address],
> > [Country], [Latitude], [Longitude]
> > FROM [dbo].[Dinners]
> > WHERE [DinnerID] = @id
> > -- @id: Input Int32 (Size = 4; Prec = 0;
> > Scale = 0) [4]
> > -- Context: SqlServer Model:
> > AttributedMetaModel Build: 3.5.0.0
> > # a='4'; b='4'
> >
> > # and things are still hosed.
> >
> > I'm largely clueless here, not being nearly
> > familiar enough with the code. All I'm sure
> > of is that killing the query cache makes
> > things work.
> >
> > My suspicion is that SelectQuery is actually
> > at fault, as SelectQuery contains the input
> > parameters (SelectQuery.InputParameters),
> > which are used ~directly in the resulting
> > SQL query (see SelectQuery.GetCommand(),
> > which adds each input Parameter to
> > Command.Parameters, and uses
> > parameter.GetValue() to obtain the actual
> > value. So it appears that SelectQuery is not
> > only caching the query itself, but the
> > parameters used with the query as well,
> > which is absolutely bad.
> >
> > But if this were truly the case, then this
> > would be visible in the unit tests (e.g.
> > ReadTest.A5_SelectSingleOrDefault()), and
> > I'm not seeing it in the unit tests.
> >
> > Thoughts? (Or better, actual fixes?)
> >
> > - Jon
> >
> >
> >
> >
> >
> >
> >
> >
> >
> >
> >
> >
> >
> >
> >
> >
> >
> >
>
>
> >
--~--~---------~--~----~------------~-------~--~----~
You received this message because you are subscribed to the Google Groups
"DbLinq" group.
To post to this group, send email to [email protected]
To unsubscribe from this group, send email to
[email protected]
For more options, visit this group at
http://groups.google.com/group/dblinq?hl=en
-~----------~----~----~----~------~----~------~--~---
Index: src/DbLinq/Test/Providers/ReadTest.cs
===================================================================
--- src/DbLinq/Test/Providers/ReadTest.cs (revision 1073)
+++ src/DbLinq/Test/Providers/ReadTest.cs (working copy)
@@ -117,6 +117,7 @@
var cust = db.Customers.SingleOrDefault(c => c.CompanyName == "Around the Horn");
Assert.IsNotNull(cust, "Expected one customer 'Around the Horn'.");
+#if false
var id = "ALFKI";
cust = db.Customers.SingleOrDefault(c => c.CustomerID == id);
Assert.AreEqual("ALFKI", cust.CustomerID);
@@ -132,9 +133,29 @@
Assert.AreEqual("ALFKI", cust.CustomerID);
id = "BLAUS";
cust = db.Customers.SingleOrDefault(c => c.CustomerID == id);
+#endif
+ cust = GetCustomerById(db, "ALFKI");
+ Assert.AreEqual("ALFKI", cust.CustomerID);
+
+ cust = GetCustomerById(db, "BLAUS");
+ Assert.AreEqual("BLAUS", cust.CustomerID);
+
+ cust = GetCustomerById(db, "DNE");
+ Assert.IsNull(cust);
+
+ cust = GetCustomerById(db, "ALFKI");
+ Assert.AreEqual("ALFKI", cust.CustomerID);
+
+ cust = GetCustomerById(db, "BLAUS");
+ Assert.AreEqual("BLAUS", cust.CustomerID);
}
+ private static Customer GetCustomerById(Northwind db, string id)
+ {
+ return db.Customers.SingleOrDefault(c => c.CustomerID == id);
+ }
+
[Test]
public void A6_ConnectionOpenTest()
{