I have the following code for a compiled Linq2sql query to count rows in a table. The query throws an exception despite the same uncompiled query running smoothly:
public static Func<ServiceCustomContext, int> CompiledCount
= CompiledQuery.Compile((ServiceCustomContext db) => db.Current.Count());
public static int Count()
{
using (ServiceCustomContext db = new ServiceCustomContext(Constants.NewSqlConnection))
return CompiledCount(db);
}
ServiceCustomContext
inherits from DataContext
and has only (besides a constructor) Table
s including a table named Current
used in the example above.
And I get the following exception:
'Query was compiled for a different mapping source than the one associated with the specified DataContext.'
This is only when using, as above, a compiled query. As long as I have a simple:
return db.Current.Count();
in the Count()
method, everything is fine.
I don't understand what's wrong. I thought it might be that I need to keep a reference to the DataContext (ServiceCustomContext) although that seemed counter intuitive, but even the Microsoft examples don't do that. The only explanation I've found, is here which is basically that compiled queries as mentioned in the Microsoft examples in the link above are really wrong. I doubt that's true though.
The code would run just fine if only you used Count()
only once during the lifetime of your application. And the error means exactly what it says. If you look at the code of CompiledQuery
:
private object ExecuteQuery(DataContext context, object[] args) {
if (context == null) {
throw Error.ArgumentNull("context");
}
if (this.compiled == null) {
lock (this) {
if (this.compiled == null) {
this.compiled = context.Provider.Compile(this.query);
this.mappingSource = context.Mapping.MappingSource;
}
}
}
else {
if (context.Mapping.MappingSource != this.mappingSource)
throw Error.QueryWasCompiledForDifferentMappingSource();
}
return this.compiled.Execute(context.Provider, args).ReturnValue;
}
You can see that what it does, it actually compiles the query only when it's first invoked. It also saves the reference to your DataContext.Mapping.MappingSource
and makes sure you use the same MappingSource
on every subsequent call.
By default, every time you create a new DataContext
, a new MappingSource
is created along. This is done in constructor and there is no way to override it later as both DataContext.Mapping
and MetaModel.MappingSource
properties only have a public getter. However, there is a constructor overload of DataContext
which takes a MappingSource
as an argument which (lucky you) allows you to reuse a single mapping source throughout the lifetime of your application.
Not sure what are the exact constructors of your ServiceCustomContext
class, but the following code should give you a hint of the solution to prevent the error from happening:
public class ServiceCustomContext : DataContext
{
private static readonly MappingSource mappingSource = new AttributeMappingSource();
public ServiceCustomContext(string connection) : base(connection, mappingSource)
{
}
}
I assumed you are using attributes for declaring mapping, however you can use XmlMappingSource
in case you're using an XML file for your database mapping.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With