Problema com a conversão de int para string no Linq em entidades

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 alguma maneira de conseguir isso? Observe que no VB.NET não há problema em usar o primeiro trecho, ele funciona muito bem, o VB é flexível, não consigo me acostumar com o rigor do C # !!!

Shimmy Weitzhandler
fonte
2
.ToString () também não funciona para LinqToEF no VB. IMHO, meio idiota.
StingyJack
5
@StingyJack, o problema está no ELINQ (entidades linq 2), porque ele traduz seu código para SQL e, quando se trata de uma solicitação interna de ToString, não sabe como converter 'ToString' para SQL. Ao contrário dos objetos linq 2, quando não há tradução e tudo é lambdas CLR, ele é executado diretamente nos objetos solicitados.
Shimmy Weitzhandler
1
Estou apenas irritado que eles permitam que esse tipo de erro seja compilado e que eu tive que procurar sempre uma descrição clara da causa em inglês (sans legal-ese e academia-ese).
StingyJack
1
Você está certo, mas eles também estão certos, não devem traduzir toda a funcionalidade CLR e CLR customizada em SQL, especialmente não na versão muito inicial do EF :) Sobre o ToString, leia a resposta de Brian: stackoverflow. com / perguntas / 1066760 /…
Shimmy Weitzhandler
Ótimo, mas e as pessoas que usam 3.5, não 4? Então o que?
Ekaterina

Respostas:

313

Com o EF v4 você pode usar SqlFunctions.StringConvert. Não há sobrecarga para int, então você precisa converter para um dobro ou um decimal. Seu código acaba assim:

var items = from c in contacts
            select new ListItem
            {
                Value = SqlFunctions.StringConvert((double)c.ContactId).Trim(),
                Text = c.Name
            };
Brian Cauthon
fonte
234
Por que diabos eles não incluem uma sobrecarga para int?
Jeremy Coenen
7
@ Nestor Isso não funciona para o SQL Compact. Descobri isso da maneira mais difícil.
Austin
24
Para evitar os espaços em branco antes do resultado, você deve usarSqlFunctions.StringConvert((double)c.ContactId).Trim()
Kim Tranjan 15/03
2
Parece não funcionar para SQLite usando System.Data.SQLite O método 'System.String StringConvert (System.Nullable`1 [System.Double])' no Typw 'System.Data.Objects.SqlClient.SqlFunctions' pode ser encontrado em um novo campo de pesquisa. für 'LINQ to Entities' übersetzt werden. (não pode ser traduzido para "LINQ to Entities")
OneWorld
5
Excelente resposta! Apenas observe que, a partir do uso do EF 6, a classe foi movida para outro espaço para nome. Portanto, antes do EF 6, você deve incluir: "System.Data.Objects.SqlClient" Se você atualizar para o EF 6 ou simplesmente estiver usando esta versão, inclua: "System.Data.Entity.SqlServer" Ao incluir o namespace incorreto com EF6, o código será compilado corretamente, mas gerará um erro de tempo de execução. Espero que esta nota ajude a evitar alguma confusão.
Leo
12

Resolvi um problema semelhante colocando a conversão do número inteiro em string fora da consulta. Isso pode ser alcançado colocando a consulta em um objeto.

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
fonte
Esta é uma maneira de resolvê-lo, mas tenha em mente que isso vai aumentar o tempo de execução, se vc tem grande quantidade de objetos, este foreach é um exagero ...
Serlok
9

Use LinqToObject: contatos. AsEnumerable ()

var items = from c in contacts.AsEnumerable()
            select new ListItem
            {
                Value = c.ContactId.ToString(),
                Text = c.Name
            };
Mohammadreza
fonte
Obrigado. Para sua informação, estou tentando resolver um problema um pouco diferente. Estou usando o LINQ para entidades / lambda e isso funciona. Eu estava tentando converter um Int para String e use "Contains" para encontrar resultados correspondentes -> ou seja, db.contacts.AsEnumerable (). Where (c => c.ContactId.ToString (). Contains ( searchitem )). ToList (); ;
ejhost
9
Se você ligar AsEnumerable, pagará um preço de alto desempenho em bancos de dados maiores, pois trará tudo para a memória. IEnumerableé mais lento em comparação com IQueryableporque o posterior é executado exclusivamente no banco de dados.
CodeArtist
5

SqlFunctions.StringConvert funcionará, mas acho complicado e, na maioria das vezes, não tenho uma necessidade real de realizar a conversão de string no lado SQL.

O que faço se quiser manipular seqüências de caracteres é executar primeiro a consulta no linq-to-entity e depois manipular as picadas no linq-to-objects. Neste exemplo, desejo obter um conjunto de dados contendo o nome completo de um contato e ContactLocationKey, que é a concatinação de seqüência de caracteres de duas colunas Inteiras (ContactID e 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();

Agora, admito que é complicado ter que escrever duas seleções anônimas, mas eu argumentaria que isso é superado pela conveniência de que você pode executar funções de string (e outras) não suportadas no L2E. Lembre-se também de que provavelmente existe uma penalidade de desempenho usando esse método.

Walter Stabosz
fonte
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
fonte
Você testou e funciona? leia esta resposta antes.
Shimmy Weitzhandler
Sim, eu já estou usando. Funciona para MVC3, EF4, CTP5, SQL CE4.
Nestor
Isso parece mais elegante do que o boxe para dobrar e usar o StringConvert.
CmdrTallen
9
Mas, neste caso, você buscará todos os dados do banco de dados e, em seguida, suponha que deseje fazer alguma filtragem nessa lista antes return list.ToList();!
Wahid Bitar 26/09
4
Quando você não pode acessar SqlFunctions, você não tem muitas outras opções além desta. No entanto, eu teria usado isso para a minha consulta: 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();. Fazer dessa maneira obtém apenas o ID / nome do banco de dados (em vez de todas as propriedades do cliente) e faz a classificação usando o índice mais eficiente no banco de dados.
Brian Cauthon
3
var selectList = db.NewsClasses.ToList<NewsClass>().Select(a => new SelectListItem({
    Text = a.ClassName,
    Value = a.ClassId.ToString()
});

Em primeiro lugar, converta em objeto e, em seguida, toString () estará correto.

phil hong
fonte
3

A resposta de Brian Cauthon é excelente! Apenas uma pequena atualização, para o EF 6, a classe foi movida para outro espaço para nome. Portanto, antes do EF 6, você deve incluir:

System.Data.Objects.SqlClient

Se você atualizar para o EF 6 ou simplesmente estiver usando esta versão, inclua:

System.Data.Entity.SqlServer

Ao incluir o namespace incorreto no EF6, o código será compilado corretamente, mas gerará um erro de tempo de execução. Espero que esta nota ajude a evitar alguma confusão.

Leo
fonte
Devo dizer que sua resposta também é excelente. Atualizei para o EF6 e procurei em todo o lado SqlFunctions. Sua resposta me apontou na direção certa. Acrescentarei apenas que você precisa de uma referência ao EntityFramework.SqlServer (você pode ter apenas uma referência ao EntityFramework).
Metalogic 17/04
2

Encontrei esse mesmo problema ao converter meu aplicativo MVC 2 para MVC 3 e, para fornecer outra solução (limpa) para esse problema, quero postar o que fiz ...

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

GetProducers () simplesmente retorna uma coleção de entidades de Producers. PS O SqlFunctions.StringConvert não funcionou para mim.

BarryC
fonte
2

Se o seu "contato" estiver atuando como lista genérica, espero que o código a seguir funcione bem.

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

Obrigado.

Nawaz
fonte
2

Mais uma solução:

c.ContactId + ""

Basta adicionar uma string vazia e ela será convertida em string.

Igor Valikovsky
fonte
Erro retornado: System.NotSupportedException: Não foi possível converter o tipo 'System.Int64' para o tipo 'System.Object'. O LINQ to Entities suporta apenas tipos de enumeração ou primitivo EDM de conversão.
Qmaster
1

Usando o MySql, o SqlFunctions.StringConvertnão funcionou para mim. Como eu uso SelectListItemem mais de 20 lugares no meu projeto, eu queria uma solução que funcionasse sem contorcer as mais de 20 instruções LINQ. Minha solução foi a subclasse SelectedListItempara fornecer um setter inteiro, que afasta a conversão de tipo do LINQ. Obviamente, esta solução é difícil de generalizar, mas foi bastante útil para o meu projeto específico.

Para usar, crie o seguinte tipo e use na sua consulta LINQ no lugar de SelectedListIteme use IntValue no lugar de Value.

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

Se você usa a estrutura de entidade e deseja tornar aceitável o único int, pode usá-lo na consulta linq.

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

funcionará porque usar (int) converterá seu valor para int, assim você não precisa de nenhuma conversão para string em int e obtém o resultado desejado.

isso funcionou para mim no meu projeto, acho que seria útil para você

Saurabh Solanki
fonte
-2

Meu entendimento é que você precisa criar uma classe parcial para "estender" seu modelo e adicionar uma propriedade somente de leitura que possa utilizar o restante das propriedades da classe.

public partial class Contact{

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

Então

var items = from c in contacts
select new ListItem
{
    Value = c.ContactIdString, 
    Text = c.Name
};
Mcbeev
fonte
Não, você não pode usar propriedades personalizadas no LINQ to Entities (no .NET 3.5).
Craig Stuntz
1
Eu não testei, mas também não vai funcionar. já que não é uma propriedade de campo da tabela. Eu poderia primeiro fazer isso com ToArray () e depois fazer a linq sobre objetos, mas quero consultar o banco de dados. Eu suponho que não será capaz de fazê-lo. Eu criei meu próprio ListItem que usa um campo int. Isso funciona melhor para mim.
217 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
};

eu achei aquilo SqlFunctions.StringConvert((double)c.Age) não funcionou para mim ou o campo é do tipoNullable<Int32>

Levei muito tempo pesquisando nos últimos dias de tentativa e erro para encontrar isso.

Espero que isso ajude alguns codificadores por aí.

Ken Blackford
fonte
1
Nao funciona para mim. Ele lança a exceção " ... System.String Concat(System.Object)não pode ser traduzido em uma expressão de loja ... ".
Slauma
1
Também não funciona para mim. Também recebo "System.NotSupportedException: LINQ to Entities não reconhece o método 'System.String Concat (System.Object)' e esse método não pode ser convertido em uma expressão de armazenamento."
camainc
1
NÃO FUNCIONA - DEVOTE ESTA RESPOSTA [NotSupportedException: LINQ to Entities não reconhece o método 'System.String Concat (System.Object)', e esse método não pode ser traduzido em uma expressão de armazenamento.]
Philipp Munin,
-6

Você pode tentar:

var items = from c in contacts
        select new ListItem
        {
            Value = Convert.ToString(c.ContactId), 
            Text = c.Name
        };
Tony Heupel
fonte
O código acima não funcionará, pois ocorrerá um erro dizendo "O LINQ to Entities não reconhece o método 'System.String ToString (Int32)', e esse método não pode ser convertido em uma expressão de armazenamento".
GK