Problème avec la conversion int en chaîne dans Linq en entités

202
var items = from c in contacts
            select new ListItem
            {
                Value = c.ContactId, //Cannot implicitly convert type 'int' (ContactId) to 'string' (Value).
                Text = c.Name
            };
var items = from c in contacts
            select new ListItem
            {
                Value = c.ContactId.ToString(), //Throws exception: ToString is not supported in linq to entities.
                Text = c.Name
            };

Existe-t-il de toute façon que je peux y parvenir? Notez que dans VB.NET, il n'y a aucun problème, utilisez le premier extrait, cela fonctionne très bien, VB est flexible, je ne peux pas m'habituer à la rigueur de C # !!!

Shimmy Weitzhandler
la source
2
.ToString () ne fonctionne pas non plus pour LinqToEF en VB. À mon humble avis, un peu stupide.
StingyJack
5
@StingyJack, le problème vient de l'ELINQ (entités linq 2), car il traduit votre code en SQL, et lorsqu'il s'agit d'une requête ToString interne, il ne sait pas comment traduire 'ToString' en SQL. Contrairement aux objets linq 2, lorsqu'il n'y a pas de traduction et que tout est des lambdas CLR, alors il est exécuté directement sur les objets demandés.
Shimmy Weitzhandler
1
Je suis simplement irrité qu'ils permettent que ce genre d'erreur soit compilé, et que j'ai dû traîner pour toujours pour trouver une description anglaise simple de la cause (sans legal-ese et academia-ese).
StingyJack
1
Vous avez raison, mais ils ont également raison, ils ne sont pas censés traduire toutes les fonctionnalités CLR et CLR personnalisées en SQL, en particulier pas dans la toute première version d'EF :) À propos de la ToString, lisez la réponse de Brian: stackoverflow. com / questions / 1066760 /…
Shimmy Weitzhandler
Génial, mais que diriez-vous des personnes utilisant 3.5, no 4? Alors quoi?
Ekaterina

Réponses:

313

Avec EF v4, vous pouvez utiliser SqlFunctions.StringConvert. Il n'y a pas de surcharge pour int, vous devez donc effectuer un cast en double ou en décimal. Votre code finit par ressembler à ceci:

var items = from c in contacts
            select new ListItem
            {
                Value = SqlFunctions.StringConvert((double)c.ContactId).Trim(),
                Text = c.Name
            };
Brian Cauthon
la source
234
Pourquoi diable n'incluraient-ils pas une surcharge pour int?
Jeremy Coenen du
7
@Nestor Cela ne fonctionne pas pour SQL Compact. Trouvé cela de manière difficile.
Austin
24
Pour éviter les espaces blancs avant le résultat, vous devez utiliserSqlFunctions.StringConvert((double)c.ContactId).Trim()
Kim Tranjan
2
Semble ne pas fonctionner pour SQLite à l'aide de System.Data.SQLite La méthode 'System.String StringConvert (System.Nullable`1 [System.Double])' dans Typw 'System.Data.Objects.SqlClient.SqlFunctions' kann nicht in einen Speicherausdruck für 'LINQ to Entities' übersetzt werden. (ne peut pas être traduit en "LINQ to Entities")
OneWorld
5
Excellente réponse! Veuillez noter que, à partir du moment où vous utilisez EF 6, la classe a été déplacée vers un autre espace de noms. Donc, avant EF 6, vous devez inclure: "System.Data.Objects.SqlClient" Si vous effectuez une mise à jour vers EF 6, ou utilisez simplement cette version, incluez: "System.Data.Entity.SqlServer" En incluant l'espace de noms incorrect avec EF6, le code se compilera très bien mais générera une erreur d'exécution. J'espère que cette note aide à éviter une certaine confusion.
Leo
12

J'ai résolu un problème similaire en plaçant la conversion de l'entier en chaîne hors de la requête. Cela peut être réalisé en plaçant la requête dans un objet.

var items = from c in contacts
            select new 
            {
                Value = c.ContactId,
                Text = c.Name
            };
var itemList = new SelectList();
foreach (var item in items)
{
    itemList.Add(new SelectListItem{ Value = item.ContactId, Text = item.Name });
}
Jente Rosseel
la source
C'est une façon de le résoudre, mais gardez à l'esprit que cela augmentera le temps d'exécution, si vous avez une grande quantité d'objets, cette foreach est
exagérée
9

Utilisez LinqToObject: contacts. AsEnumerable ()

var items = from c in contacts.AsEnumerable()
            select new ListItem
            {
                Value = c.ContactId.ToString(),
                Text = c.Name
            };
Mohammadreza
la source
Merci. Pour info, j'essaie de résoudre un problème légèrement différent. J'utilise LINQ pour les entités / lambda et cela fonctionne. J'essayais de convertir un Int en chaîne et d'utiliser "Contient" pour trouver les résultats correspondants -> c'est-à-dire db.contacts.AsEnumerable (). Où (c => c.ContactId.ToString (). Contient ( searchitem )). ToList (); ;
ejhost
9
Si vous appelez, AsEnumerablevous paierez un prix élevé sur les bases de données plus volumineuses, car cela mettra tout en mémoire. IEnumerableest plus lent par rapport à IQueryableparce que le dernier est exécuté exclusivement dans la base de données.
CodeArtist
5

SqlFunctions.StringConvert fonctionnera, mais je trouve cela lourd, et la plupart du temps, je n'ai pas vraiment besoin d'effectuer la conversion de chaîne du côté SQL.

Ce que je fais si je veux faire des manipulations de chaînes, c'est d'abord exécuter la requête dans linq-to-entity, puis manipuler les piqûres dans linq-to-objects. Dans cet exemple, je veux obtenir un ensemble de données contenant le nom complet d'un contact et ContactLocationKey, qui est la concatination de chaîne de deux colonnes entières (ContactID et LocationID).

// perform the linq-to-entities query, query execution is triggered by ToArray()
var data =
   (from c in Context.Contacts
   select new {
       c.ContactID,
       c.FullName,
       c.LocationID
   }).ToArray();

// at this point, the database has been called and we are working in
// linq-to-objects where ToString() is supported
// Key2 is an extra example that wouldn't work in linq-to-entities
var data2 =
   (from c in data
    select new {
       c.FullName,
       ContactLocationKey = c.ContactID.ToString() + "." + c.LocationID.ToString(),
       Key2 = string.Join(".", c.ContactID.ToString(), c.LocationID.ToString())
    }).ToArray();

Maintenant, je reconnais que cela devient pénible d'avoir à écrire deux sélections anonymes, mais je dirais que cela est contrebalancé par la commodité dont vous pouvez effectuer des fonctions de chaîne (et autres) non prises en charge dans L2E. Gardez également à l'esprit qu'il y a probablement une baisse des performances en utilisant cette méthode.

Walter Stabosz
la source
4
public static IEnumerable<SelectListItem> GetCustomerList()
        {
            using (SiteDataContext db = new SiteDataContext())
            {
                var list = from l in db.Customers.AsEnumerable()
                           orderby l.CompanyName
                           select new SelectListItem { Value = l.CustomerID.ToString(), Text = l.CompanyName };

                return list.ToList();
            }
        }
Nestor
la source
L'avez-vous testé et cela fonctionne? lisez cette réponse avant.
Shimmy Weitzhandler
Oui, je l'utilise déjà. Il fonctionne pour MVC3, EF4, CTP5, SQL CE4.
Nestor
Cela semble plus élégant que la boxe pour doubler et utiliser le StringConvert.
CmdrTallen
9
Mais dans ce cas, vous récupérerez toutes les données de la base de données, puis supposez que vous souhaitez effectuer un filtrage sur cette liste avant return list.ToList();!!
Wahid Bitar
4
Lorsque vous ne pouvez pas accéder à SqlFunctions, vous n'avez pas beaucoup d'autres options que cela. Cependant, je l' aurais utilisé pour ma requête: return (from l in db.Customers orderby l.CompanyName select new {Id=l.CustomerID, Name=l.CompanyName}).AsEnumerable().Select(c=> new SelectListItem{Value=c.Id.ToString(), Text = c.Name}).ToList();. Le faire de cette façon obtient uniquement l'id / nom de la base de données (au lieu de toutes les propriétés du client) et effectue le tri en utilisant l'index le plus efficace sur la base de données.
Brian Cauthon
3
var selectList = db.NewsClasses.ToList<NewsClass>().Select(a => new SelectListItem({
    Text = a.ClassName,
    Value = a.ClassId.ToString()
});

Tout d'abord, convertissez en objet, puis toString () sera correct.

phil hong
la source
3

La réponse de Brian Cauthon est excellente! Juste une petite mise à jour, pour EF 6, la classe a été déplacée vers un autre espace de noms. Donc, avant EF 6, vous devez inclure:

System.Data.Objects.SqlClient

Si vous mettez à jour vers EF 6, ou utilisez simplement cette version, incluez:

System.Data.Entity.SqlServer

En incluant l'espace de noms incorrect avec EF6, le code se compilera très bien mais générera une erreur d'exécution. J'espère que cette note aide à éviter une certaine confusion.

Leo
la source
Je dois dire que votre réponse est également excellente. J'ai mis à niveau vers EF6 et j'ai cherché partout des fonctions Sql. Votre réponse m'a orienté dans la bonne direction. J'ajouterai simplement que vous avez également besoin d'une référence à EntityFramework.SqlServer (vous ne pouvez avoir qu'une référence à EntityFramework).
Metalogic
2

J'ai rencontré ce même problème lorsque je convertissais mon application MVC 2 en MVC 3 et juste pour donner une autre solution (propre) à ce problème, je veux publier ce que j'ai fait ...

IEnumerable<SelectListItem> producers = new SelectList(Services.GetProducers(),
    "ID", "Name", model.ProducerID);

GetProducers () renvoie simplement une collection d'entités de producteurs. PS Le SqlFunctions.StringConvert n'a pas fonctionné pour moi.

BarryC
la source
2

Si votre "contact" fait office de liste générique, j'espère que le code suivant fonctionne bien.

var items = contact.Distinct().OrderBy(c => c.Name)
                              .Select( c => new ListItem
                              {
                                Value = c.ContactId.ToString(),
                                Text = c.Name
                              });

Merci.

Nawaz
la source
2

Encore une solution:

c.ContactId + ""

Ajoutez simplement une chaîne vide et elle sera convertie en chaîne.

Igor Valikovsky
la source
Erreur renvoyée: System.NotSupportedException: impossible de caster le type 'System.Int64' en type 'System.Object'. LINQ to Entities prend uniquement en charge la conversion de types primitifs ou d'énumération EDM.
QMaster
1

Avec MySql, cela SqlFunctions.StringConvertn'a pas fonctionné pour moi. Depuis que j'utilise SelectListItemdans plus de 20 endroits dans mon projet, je voulais une solution qui fonctionne sans dénaturer les 20+ instructions LINQ. Ma solution était de sous-classer SelectedListItemafin de fournir un setter entier, qui éloigne la conversion de type de LINQ. Évidemment, cette solution est difficile à généraliser, mais était très utile pour mon projet spécifique.

Pour l'utiliser, créez le type suivant et utilisez-le dans votre requête LINQ à la place de SelectedListItemet utilisez IntValue à la place de Value.

public class BtoSelectedListItem : SelectListItem
{
    public int IntValue
    {
        get { return string.IsNullOrEmpty(Value) ? 0 : int.Parse(Value); }
        set { Value = value.ToString(); }
    }
}
raider33
la source
1

si vous utilisez le framework d'entité et que vous souhaitez rendre le seul int acceptable, vous pouvez l'utiliser dans la requête linq, vous pouvez essayer ceci

var items = from c in contacts
        select new ListItem
        {
            Value = (int)ContractId 
            Text = c.Name
        };

cela fonctionnera parce que l'utilisation de (int) convertira votre valeur en int, vous n'avez donc pas besoin de conversion pour la chaîne en int et vous obtenez le résultat souhaité.

cela a fonctionné pour moi dans mon projet, je pense qu'il serait utile pour vous

Saurabh Solanki
la source
-2

Ma compréhension est que vous devez créer une classe partielle pour "étendre" votre modèle et ajouter une propriété en lecture seule qui peut utiliser le reste des propriétés de la classe.

public partial class Contact{

   public string ContactIdString
   {
      get{ 
            return this.ContactId.ToString();
      }
   } 
}

ensuite

var items = from c in contacts
select new ListItem
{
    Value = c.ContactIdString, 
    Text = c.Name
};
Mcbeev
la source
Non, vous ne pouvez pas utiliser de propriétés personnalisées dans LINQ to Entities (dans .NET 3.5).
Craig Stuntz
1
Je ne l'ai pas testé, mais ça ne marchera pas non plus. car ce n'est pas une propriété de champ de table. Je pourrais d'abord le faire avec ToArray () puis linqing sur des objets mais je veux interroger la base de données. Je suppose que je ne pourrai pas le faire. J'ai créé mon propre ListItem qui prend un champ int. Cela fonctionne mieux pour moi.
Shimmy Weitzhandler
-2
var items = from c in contacts
select new ListItem
{
    Value = String.Concat(c.ContactId), //This Works in Linq to Entity!
    Text = c.Name
};

J'ai trouvé que ça SqlFunctions.StringConvert((double)c.Age)ne fonctionnait pas pour moi non plus le champ est de typeNullable<Int32>

Cela m'a pris beaucoup de recherches au cours des derniers jours d'essais et d'erreurs pour trouver cela.

J'espère que cela aide quelques codeurs.

Ken Blackford
la source
1
Ça ne marche pas pour moi. Il lève l'exception " ... System.String Concat(System.Object)ne peut pas être traduit en une expression de magasin ... ".
Slauma
1
Ça ne marche pas non plus pour moi. J'obtiens également "System.NotSupportedException: LINQ to Entities ne reconnaît pas la méthode" System.String Concat (System.Object) "et cette méthode ne peut pas être traduite en une expression de magasin."
camainc le
1
NE FONCTIONNE PAS - SUPPRIMER CETTE RÉPONSE [NotSupportedException: LINQ to Entities ne reconnaît pas la méthode 'System.String Concat (System.Object)', et cette méthode ne peut pas être traduite en une expression de magasin.]
Philipp Munin
-6

Peux-tu essayer:

var items = from c in contacts
        select new ListItem
        {
            Value = Convert.ToString(c.ContactId), 
            Text = c.Name
        };
Tony Heupel
la source
Le code ci-dessus ne fonctionnera pas car il générera une erreur indiquant "LINQ to Entities ne reconnaît pas la méthode" System.String ToString (Int32) "et cette méthode ne peut pas être traduite en une expression de magasin."
GK