Problema con la conversión de int a string en Linq a 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
            };

¿Hay alguna forma de lograr esto? Tenga en cuenta que en VB.NET no hay ningún problema para usar el primer fragmento, funciona muy bien, VB es flexible, ¡no puedo acostumbrarme a la rigidez de C #!

Shimmy Weitzhandler
fuente
2
.ToString () tampoco funciona para LinqToEF en VB. En mi humilde opinión, un poco estúpido.
StingyJack
55
@StingyJack, el problema está en ELINQ (entidades linq 2), porque traduce su código a SQL y, cuando se trata de una solicitud interna de ToString, no sabe cómo traducir 'ToString' a SQL. A diferencia de los objetos linq 2, cuando no hay traducción y todo es CLR lambdas, se realiza directamente en los objetos solicitados.
Shimmy Weitzhandler
1
Me irrita que permitan que se compile ese tipo de error, y que tuve que troll para siempre para encontrar una descripción sencilla de la causa en inglés (sans legal-ese y academia-ese).
StingyJack
1
Tienes razón, pero también tienen razón, no se supone que traduzcan todas las funciones CLR y CLR personalizadas a SQL, especialmente no en la versión muy temprana de EF :) Acerca de ToString, lee la respuesta de Brian: stackoverflow. com / preguntas / 1066760 / ...
Shimmy Weitzhandler
Genial, pero ¿qué hay de las personas que usan 3.5, no 4? ¿Y que?
Ekaterina

Respuestas:

313

Con EF v4 puedes usar SqlFunctions.StringConvert. No hay sobrecarga para int, por lo que debe convertir a un doble o un decimal. Su código termina luciendo así:

var items = from c in contacts
            select new ListItem
            {
                Value = SqlFunctions.StringConvert((double)c.ContactId).Trim(),
                Text = c.Name
            };
Brian Cauthon
fuente
234
¿Por qué demonios no incluirían una sobrecarga para int?
Jeremy Coenen
77
@Nestor Esto no funciona para SQL Compact. Encuéntralo por la vía difícil.
Austin
24
Para evitar los espacios en blanco antes del resultado, debe usarSqlFunctions.StringConvert((double)c.ContactId).Trim()
Kim Tranjan
2
Parece que no funciona para SQLite usando System.Data.SQLite El Methode 'System.String StringConvert (System.Nullable`1 [System.Double])' en Typw 'System.Data.Objects.SqlClient.SqlFunctions' kann nicht en einen Speicherausdruck für 'LINQ to Entities' übersetzt werden. (no se puede traducir a "LINQ to Entities")
OneWorld
55
Excelente respuesta! Solo tenga en cuenta que, al comenzar a usar EF 6, la clase se movió a otro espacio de nombres. Por lo tanto, antes de EF 6, debe incluir: "System.Data.Objects.SqlClient" Si actualiza a EF 6, o simplemente está utilizando esta versión, incluya: "System.Data.Entity.SqlServer" Incluyendo el espacio de nombres incorrecto con EF6, el código se compilará bien pero arrojará un error de tiempo de ejecución. Espero que esta nota ayude a evitar cierta confusión.
Leo
12

Resolví un problema similar colocando la conversión del entero a la cadena fuera de la consulta. Esto se puede lograr poniendo la consulta en un 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
fuente
Esta es una forma de resolverlo, pero tenga en cuenta que esto aumentará el tiempo de ejecución, si tiene una gran cantidad de objetos, este foreach es excesivo ...
Serlok
9

Utilice LinqToObject: contactos. AsEnumerable ()

var items = from c in contacts.AsEnumerable()
            select new ListItem
            {
                Value = c.ContactId.ToString(),
                Text = c.Name
            };
Mohammadreza
fuente
Gracias. Para su información, estoy tratando de resolver un problema ligeramente diferente. Estoy usando LINQ para entidades / lambda y esto funciona. Estaba tratando de convertir un Int a String y usar "Contiene" para encontrar resultados coincidentes -> es decir, db.contacts.AsEnumerable (). Where (c => c.ContactId.ToString (). Contins ( searchitem )). ToList (); ;
ejhost
9
Si llama AsEnumerable, pagará un precio de alto rendimiento en bases de datos más grandes porque traerá todo a la memoria. IEnumerablees más lento en comparación con IQueryableporque el último se ejecuta exclusivamente en la base de datos.
CodeArtist
5

SqlFunctions.StringConvert funcionará, pero me resulta engorroso, y la mayoría de las veces, no tengo una necesidad real de realizar la conversión de cadenas en el lado de SQL.

Lo que hago si quiero hacer manipulaciones de cadenas es realizar primero la consulta en linq-to-persons, luego manipular las picaduras en linq-to-objects. En este ejemplo, quiero obtener un conjunto de datos que contengan el nombre completo de un contacto y ContactLocationKey, que es la concatenación de cadenas de dos columnas enteras (ContactID y 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();

Ahora, reconozco que se vuelve engorroso tener que escribir dos selecciones anónimas, pero argumentaría que esto se ve superado por la conveniencia de que puede realizar funciones de cadena (y otras) no admitidas en L2E. También tenga en cuenta que probablemente haya una penalización de rendimiento al usar este método.

Walter Stabosz
fuente
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();
            }
        }
Néstor
fuente
¿Lo probaste y funciona? lee esta respuesta antes.
Shimmy Weitzhandler
Sí, ya lo estoy usando. Funciona para MVC3, EF4, CTP5, SQL CE4.
Nestor
Esto parece más elegante que el boxeo para duplicar y usar el StringConvert.
CmdrTallen
9
¡Pero en este caso obtendrá todos los datos de la base de datos y luego suponga que quiere hacer algún filtrado en esta lista antes return list.ToList();!
Wahid Bitar
44
Cuando no puede acceder a SqlFunctions, no tiene muchas otras opciones además de esta. Sin embargo, habría utilizado esto para mi 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();. Hacerlo de esta manera solo obtiene el id / nombre de la base de datos (en lugar de todas las propiedades del cliente) y realiza la clasificación utilizando el índice más eficiente en la base de datos.
Brian Cauthon
3
var selectList = db.NewsClasses.ToList<NewsClass>().Select(a => new SelectListItem({
    Text = a.ClassName,
    Value = a.ClassId.ToString()
});

En primer lugar, convertir a objeto, luego toString () será correcto.

phil hong
fuente
3

¡La respuesta de Brian Cauthon es excelente! Solo una pequeña actualización, para EF 6, la clase se movió a otro espacio de nombres. Entonces, antes de EF 6, debe incluir:

System.Data.Objects.SqlClient

Si actualiza a EF 6, o simplemente está usando esta versión, incluya:

System.Data.Entity.SqlServer

Al incluir el espacio de nombres incorrecto con EF6, el código se compilará bien pero arrojará un error de tiempo de ejecución. Espero que esta nota ayude a evitar cierta confusión.

León
fuente
Debo decir que su respuesta también es excelente. Actualicé a EF6 y he estado buscando SqlFunctions en todas partes. Su respuesta me señaló en la dirección correcta. Solo agregaré que necesita una referencia a EntityFramework.SqlServer también (es posible que solo tenga una referencia a EntityFramework).
Metalogic
2

Me encontré con este mismo problema cuando estaba convirtiendo mi aplicación MVC 2 a MVC 3 y solo para dar otra solución (limpia) a este problema, quiero publicar lo que hice ...

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

GetProducers () simplemente devuelve una colección de entidades de Productores. PD: El SqlFunctions.StringConvert no funcionó para mí.

BarryC
fuente
2

Si su "contacto" está actuando como una lista genérica, espero que el siguiente código funcione bien.

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

Gracias.

Nawaz
fuente
2

Una solución mas:

c.ContactId + ""

Simplemente agregue una cadena vacía y se convertirá en cadena.

Igor Valikovsky
fuente
Error devuelto: System.NotSupportedException: no se puede convertir el tipo 'System.Int64' para escribir 'System.Object'. LINQ to Entities solo admite la conversión de tipos EDM primitivos o de enumeración.
QMaster
1

Usando MySql, SqlFunctions.StringConvertno funcionó para mí. Como utilizo SelectListItemen más de 20 lugares en mi proyecto, quería una solución que funcionara sin distorsionar las más de 20 declaraciones LINQ. Mi solución fue subclasificar SelectedListItempara proporcionar un configurador de enteros, que aleja la conversión de tipos de LINQ. Obviamente, esta solución es difícil de generalizar, pero fue bastante útil para mi proyecto específico.

Para usar, cree el siguiente tipo y utilícelo en su consulta LINQ en lugar de SelectedListItemIntValue en lugar de Value.

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

si usa el marco de la entidad y desea hacer que el único int sea aceptable, puede usar esto en la consulta linq, puede probar esto

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

funcionará porque el uso de (int) arrojará su valor a int, por lo que no necesita ninguna conversión de cadena a int y obtendrá el resultado que desea.

esto funcionó para mí en mi proyecto, creo que sería útil para usted

Saurabh Solanki
fuente
-2

Entiendo que debe crear una clase parcial para "extender" su modelo y agregar una propiedad que sea de solo lectura que pueda utilizar el resto de las propiedades de la clase.

public partial class Contact{

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

Luego

var items = from c in contacts
select new ListItem
{
    Value = c.ContactIdString, 
    Text = c.Name
};
Mcbeev
fuente
No, no puede usar propiedades personalizadas en LINQ to Entities (en .NET 3.5).
Craig Stuntz
1
No lo probé, pero tampoco funcionará. ya que no es una propiedad de campo de tabla. Primero podría hacerlo con ToArray () y luego enlazar los objetos, pero quiero consultar la base de datos. Supongo que no podrá hacerlo. Creé mi propio ListItem que toma un campo int. Eso funciona mejor para mi.
Shimmy Weitzhandler el
-2
var items = from c in contacts
select new ListItem
{
    Value = String.Concat(c.ContactId), //This Works in Linq to Entity!
    Text = c.Name
};

Descubrí que SqlFunctions.StringConvert((double)c.Age)no funcionaba para mí, el campo es de tipoNullable<Int32>

Me llevó mucha búsqueda en los últimos días de prueba y error para encontrar esto.

Espero que esto ayude a algunos codificadores.

Ken Blackford
fuente
1
No funciona para mi Lanza la excepción " ... System.String Concat(System.Object)no se puede traducir a una expresión de tienda ... ".
Slauma
1
Tampoco funciona para mí. También obtengo "System.NotSupportedException: LINQ to Entities no reconoce el método 'System.String Concat (System.Object)', y este método no se puede traducir a una expresión de tienda".
camainc
1
NO FUNCIONA: DEVOTA ESTA RESPUESTA [NotSupportedException: LINQ to Entities no reconoce el método 'System.String Concat (System.Object)', y este método no se puede traducir a una expresión de tienda.]
Philipp Munin
-6

Puedes intentar:

var items = from c in contacts
        select new ListItem
        {
            Value = Convert.ToString(c.ContactId), 
            Text = c.Name
        };
Tony Heupel
fuente
El código anterior no funcionará, ya que arrojará un error que dice "LINQ to Entities no reconoce el método 'System.String ToString (Int32)', y este método no se puede traducir a una expresión de tienda".
GK