2008-11-21 4 views
4

J'ai une seule table mappée dans un datacontext. Voici la propriété et l'attribut sur la colonne d'intérêt:LinqToSql .Contains et nvarchar vs paramètres varchar -> plan de conversion d'index

[Column(Storage="_CustomerNumber", DbType="VarChar(25)")] 
public string CustomerNumber 
{ 

Cette colonne est, en fait, un varchar (25) et a un indice.

J'ai un code simple:

DataClasses1DataContext myDC = new DataClasses1DataContext(); 
myDC.Log = Console.Out; 

List<string> myList = new List<string>() { "111", "222", "333" }; 
myDC.Customers 
    .Where(c => myList.Contains(c.CustomerNumber)) 
    .ToList(); 

qui génère ce texte SQL:

SELECT [t0].[CustomerNumber], [t0].[CustomerName] 
FROM [dbo].[Customers] AS [t0] 
WHERE [t0].[CustomerNumber] IN (@p0, @p1, @p2) 
-- @p0: Input NVarChar (Size = 3; Prec = 0; Scale = 0) [111] 
-- @p1: Input NVarChar (Size = 3; Prec = 0; Scale = 0) [222] 
-- @p2: Input NVarChar (Size = 3; Prec = 0; Scale = 0) [333] 
-- Context: SqlProvider(Sql2005) Model: AttributedMetaModel Build: 3.5.21022.8 

Notez que les paramaters sont nvarchar! Lorsque cette requête atteint la base de données, elle génère un plan horrible qui implique convertir l'index de plusieurs millions de lignes sur CustomerNumber en nvarchar avant de rechercher en son sein.

Je ne suis pas autorisé à changer la table, mais je peux changer la requête et le dbml. Que puis-je faire pour extraire les données sans obtenir cette conversion d'index?

Répondre

2

est ici la façon dont je résoudre ce problème maintenant jours-. Cela convertit les paramètres au type souhaité, puis exécute la requête. Il génère le même SQL que celui généré à l'origine, avec seulement différents types de paramètres.

DbCommand myCommand = myDataContext.GetCommand(query); 

foreach (DbParameter dbParameter in myCommand.Parameters) 
{ 
    if (dbParameter.DbType == System.Data.DbType.String) 
    { 
    dbParameter.DbType = System.Data.DbType.AnsiString; 
    } 
}  

myDataContext.Connection.Open(); 

System.Data.Common.DbDataReader reader = myCommand.ExecuteReader(); 
List<RecordType> result = myDataContext.Translate<RecordType>(reader).ToList(); 

myDataContext.Connection.Close(); 
+0

Cela fonctionne, mais il y a une chasse aux sorcières: la méthode .Translate ne se charge pas correctement les entités liées à T. (En d'autres termes, si vous avez ajouté .LoadsWith sur votre contexte, et l'autre est lié à T, alors cela échouera). – dkr88

0

Voici ce que j'ai comme travail possible. Je suis intéressé à voir d'autres solutions ainsi:

List<IQueryable<Customer>> myQueries = 
    myList.Select(s => myDC.Customers.Where(c => c.CustomerNumber == s)).ToList(); 
IQueryable<Customers> myQuery = myQueries.First(); 
foreach(IQueryable<Customer> someQuery in myQueries.Skip(1)) 
{ 
    myQuery = myQuery.Concat(someQuery); 
} 
myQuery.ToList(); 

Cela génère l'instruction SQL suivante:

SELECT [t4].[CustomerNumber], [t4].[CustomerName] 
FROM (
    SELECT [t2].[CustomerNumber], [t2].[CustomerName] 
    FROM (
     SELECT [t0].[CustomerNumber], [t0].[CustomerName] 
     FROM [dbo].[Customer] AS [t0] 
     WHERE [t0].[CustomerNumber] = @p0 
     UNION ALL 
     SELECT [t1].[CustomerNumber], [t1].[CustomerName] 
     FROM [dbo].[Customer] AS [t1] 
     WHERE [t1].[CustomerNumber] = @p1 
     ) AS [t2] 
    UNION ALL 
    SELECT [t3].[CustomerNumber], [t3].[CustomerName] 
    FROM [dbo].[Customer] AS [t3] 
    WHERE [t3].[CustomerNumber] = @p2 
    ) AS [t4] 
-- @p0: Input VarChar (Size = 3; Prec = 0; Scale = 0) [111] 
-- @p1: Input VarChar (Size = 3; Prec = 0; Scale = 0) [222] 
-- @p2: Input VarChar (Size = 3; Prec = 0; Scale = 0) [333] 
-- Context: SqlProvider(Sql2005) Model: AttributedMetaModel Build: 3.5.21022.8 
0

Pour le projet sur lequel je travaille, nous devons utiliser ObjectContext au lieu de DbContext, donc je n'ai pas accès à GetCommand() ou DbParameters. Ce que je fais est de convertir la requête en chaîne, supprimer l'indicateur NVARCHAR dans la requête, puis exécuter la requête par rapport au ObjectContext. Voici une implémentation détaillée pour l'exemple donné:

List<string> myList = new List<string>() { "111", "222", "333" }; 

IQueryable<Customers> badQuery = myDC.Customers 
    .Where(c => myList.Contains(c.CustomerNumber)); 

string query = ((System.Data.Objects.ObjectQuery)badQuery).ToTraceString(); 
query = query.Replace(",N'", ",'").Replace("(N'","('"); 

List<Customers> customers = myDC.ExecuteStoreQuery<Customers>(query).ToList(); 
+0

C'est LinqToEntities. J'utilise LinqToSql. Au meilleur de ma connaissance, LinqToEntities n'a pas ce problème de traduction de requête. –

Questions connexes