Проблема с преобразованием int в строку в Linq для сущностей

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
            };

Могу ли я в любом случае добиться этого? Обратите внимание, что в VB.NET нет проблем с использованием первого фрагмента, он работает просто отлично, VB гибок, я не могу привыкнуть к строгости C # !!!

Шимми Вайцхандлер
источник
2
.ToString () также не работает для LinqToEF в VB. ИМХО, вид тупой.
StingyJack
5
@StingyJack, проблема в ELINQ (сущности linq 2), потому что он переводит ваш код в SQL, а когда дело доходит до внутреннего запроса ToString, он не знает, как преобразовать ToString в SQL. В отличие от объектов linq 2, когда нет перевода, и все является лямбдами CLR, то это выполняется непосредственно на запрошенных объектах.
Shimmy Weitzhandler
1
Я просто раздражен тем, что они допускают компиляцию такого рода ошибок, и что мне пришлось бесконечно троллить, чтобы найти простое английское описание причины (без юридических и научных целей).
StingyJack
1
Вы правы, но они также правы, они не должны переводить все функции CLR и настроенные функции CLR в SQL, особенно в самой ранней версии EF :) О ToString прочитайте ответ Брайана: stackoverflow. ru / questions / 1066760 /…
Shimmy Weitzhandler
Отлично, но как насчет людей, использующих 3.5, нет 4? Тогда что?
Екатерина

Ответы:

313

С EF v4 вы можете использовать SqlFunctions.StringConvert. Для int нет перегрузки, поэтому вам нужно привести к двойному или десятичному числу. Ваш код в конечном итоге выглядит так:

var items = from c in contacts
            select new ListItem
            {
                Value = SqlFunctions.StringConvert((double)c.ContactId).Trim(),
                Text = c.Name
            };
Брайан Коутон
источник
234
С какой стати они не включают перегрузку для int?
Джереми Коенен
7
@Nestor Это не работает для SQL Compact. Выяснили это трудным путем.
Остин
24
Чтобы избежать пробелов перед результатом, вы должны использоватьSqlFunctions.StringConvert((double)c.ContactId).Trim()
Ким Tranjan
2
Кажется, не работает для SQLite с использованием System.Data.SQLite Метод 'System.String StringConvert (System.Nullable`1 [System.Double])' в Typw 'System.Data.Objects.SqlClient.SqlFunctions' kann nicht in einen Speicherausdruck für 'LINQ to Entities' übersetzt werden. (не может быть переведено в «LINQ to Entities»)
OneWorld
5
Отличный ответ! Просто обратите внимание: начиная с EF 6, класс переместился в другое пространство имен. Итак, перед EF 6 вы должны включить: «System.Data.Objects.SqlClient». Если вы обновляетесь до EF 6 или просто используете эту версию, включите: «System.Data.Entity.SqlServer» Включая неверное пространство имен с EF6, код скомпилируется просто отлично, но выдаст ошибку времени выполнения. Я надеюсь, что эта заметка поможет избежать путаницы.
Лев
12

Я решил аналогичную проблему, поместив преобразование целого числа в строку из запроса. Это может быть достигнуто путем помещения запроса в объект.

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 });
}
Дженте Россил
источник
Это один из способов ее решения, но имейте в виду, что это увеличит время выполнения, если у вас большое количество объектов, этот foreach является излишним ...
Серлок
9

Используйте LinqToObject: контакты. AsEnumerable ()

var items = from c in contacts.AsEnumerable()
            select new ListItem
            {
                Value = c.ContactId.ToString(),
                Text = c.Name
            };
Мохаммадреза
источник
Спасибо. К вашему сведению, я пытаюсь решить немного другую проблему. Я использую LINQ для лиц / лямбда, и это работает. Я пытался преобразовать Int в строку и использовать «Содержит», чтобы найти соответствующие результаты -> т.е. db.contacts.AsEnumerable (). Где (c => c.ContactId.ToString (). Contains ( searchitem )). ToList (); ;
ejhost
9
Если вы позвоните, AsEnumerableвы заплатите высокую цену за большие базы данных, потому что это принесет все в память. IEnumerableмедленнее по сравнению с IQueryableтем, потому что последний выполняется исключительно в базе данных.
CodeArtist
5

SqlFunctions.StringConvert будет работать, но я считаю его громоздким, и большую часть времени у меня нет реальной необходимости выполнять преобразование строк на стороне SQL.

Что я делаю, если я хочу выполнять строковые манипуляции, так это сначала выполнять запрос в linq-to-entity, а затем манипулировать строками в linq-to-objects. В этом примере я хочу получить набор данных, содержащий полное имя Контакта и ContactLocationKey, который является конкатенацией строк двух столбцов Integer (ContactID и 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();

Теперь я допускаю, что писать два анонимных выбора становится громоздким, но я бы сказал, что это перевешивает удобство, с которым вы можете выполнять строковые (и другие) функции, не поддерживаемые в L2E. Также имейте в виду, что при использовании этого метода возможно снижение производительности.

Вальтер Стабош
источник
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();
            }
        }
Нестор
источник
Вы проверяли это, и это работает? прочитайте этот ответ раньше.
Шимми Вайцхандлер
Да, я уже использую это. Работает для MVC3, EF4, CTP5, SQL CE4.
Нестор
Это кажется более элегантным, чем бокс для удвоения и использования StringConvert.
CmdrTallen
9
Но в этом случае вы получите все данные из базы данных, а затем предположите, что вы хотите сделать некоторую фильтрацию в этом списке раньше return list.ToList();!!
Вахид Битар
4
Когда вы не можете получить доступ к SqlFunctions, у вас нет много других опций, кроме этой. Тем не менее, я бы использовал это для моего запроса: 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();. Делая это таким образом, только получает идентификатор / имя из базы данных (вместо всех свойств клиента) и выполняет сортировку, используя более эффективный индекс в базе данных.
Брайан Коутон,
3
var selectList = db.NewsClasses.ToList<NewsClass>().Select(a => new SelectListItem({
    Text = a.ClassName,
    Value = a.ClassId.ToString()
});

Сначала преобразуйте в объект, затем toString () будет корректным.

Фил Хонг
источник
3

Ответ Брайана Котона превосходен! Небольшое обновление для EF 6, класс переместился в другое пространство имен. Итак, перед EF 6 вы должны включить:

System.Data.Objects.SqlClient

Если вы обновляетесь до EF 6 или просто используете эту версию, включите:

System.Data.Entity.SqlServer

Включив некорректное пространство имен в EF6, код будет прекрасно компилироваться, но выдает ошибку времени выполнения. Я надеюсь, что эта заметка поможет избежать путаницы.

Лео
источник
Я должен сказать, что ваш ответ также превосходен. Я обновился до EF6 и всюду искал SqlFunctions. Ваш ответ указал мне в правильном направлении. Я просто добавлю, что вам также нужна ссылка на EntityFramework.SqlServer (у вас может быть только ссылка на EntityFramework).
Metalogic
2

Я столкнулся с этой же проблемой, когда я конвертировал свое приложение MVC 2 в MVC 3, и просто чтобы дать другое (чистое) решение этой проблемы, я хочу опубликовать то, что я сделал ...

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

GetProducers () просто возвращает коллекцию сущностей Producers. PS SqlFunctions.StringConvert не работает для меня.

BarryC
источник
2

Если ваш «контакт» действует как общий список, я надеюсь, что следующий код работает хорошо.

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

Спасибо.

Наваз
источник
2

Еще одно решение:

c.ContactId + ""

Просто добавьте пустую строку, и она будет преобразована в строку.

Игорь Валиковский
источник
Возвращенная ошибка: System.NotSupportedException: невозможно преобразовать тип «System.Int64» к типу «System.Object». LINQ to Entities поддерживает только приведение типов примитивов и перечислений EDM.
QMaster
1

Используя MySql, у SqlFunctions.StringConvertменя не получилось. Поскольку я использую SelectListItemболее 20 мест в своем проекте, я хотел, чтобы решение работало без искажения более 20 операторов LINQ. Мое решение было подклассаSelectedListItem в том, чтобы создать для обеспечения целочисленного сеттера, который перемещает преобразование типов из LINQ. Очевидно, что это решение сложно обобщить, но оно оказалось весьма полезным для моего конкретного проекта.

Чтобы использовать, создайте следующий тип и используйте его в своем запросе LINQ вместо SelectedListItemи используйте IntValue вместо Value.

public class BtoSelectedListItem : SelectListItem
{
    public int IntValue
    {
        get { return string.IsNullOrEmpty(Value) ? 0 : int.Parse(Value); }
        set { Value = value.ToString(); }
    }
}
raider33
источник
1

если вы используете Entity Framework и хотите сделать единственное int приемлемым, вы можете использовать это в запросе linq, вы можете попробовать это

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

это будет работать, потому что использование (int) приведёт ваше значение к int, поэтому вам не нужно преобразовывать строку в int, и вы получите желаемый результат.

это сработало для меня в моем проекте, я думаю, что это будет полезно для вас

Саураб Соланки
источник
-2

Насколько я понимаю, вам нужно создать частичный класс, чтобы «расширить» вашу модель и добавить свойство, предназначенное только для чтения, которое может использовать остальные свойства класса.

public partial class Contact{

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

затем

var items = from c in contacts
select new ListItem
{
    Value = c.ContactIdString, 
    Text = c.Name
};
Mcbeev
источник
Нет, вы не можете использовать пользовательские свойства в LINQ to Entities (в .NET 3.5).
Крейг Штунц
1
Я не проверял, но это тоже не сработает. так как это не свойство поля таблицы. Я мог бы сначала сделать это с помощью ToArray (), а затем привязать объекты, но я хочу запросить базу данных. Я полагаю, не сможет сделать это. Я создал свой собственный ListItem, который принимает поле int. Это работает лучше для меня.
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
};

Я обнаружил, что SqlFunctions.StringConvert((double)c.Age)у меня не работает либо поле имеет типNullable<Int32>

Мне понадобилось много поисков за последние несколько дней проб и ошибок, чтобы найти это.

Надеюсь, это поможет нескольким программистам.

Кен Блэкфорд
источник
1
Не работает для меня. Выдает исключение " ... System.String Concat(System.Object)не может быть переведено в выражение магазина ... ".
Слаума
1
У меня тоже не работает. Я также получаю «System.NotSupportedException: LINQ to Entities не распознает метод метода« System.String Concat (System.Object) », и этот метод нельзя преобразовать в выражение хранилища».
Camainc
1
НЕ РАБОТАЕТ - УДАЛИТЕ ЭТО ОТВЕТ [NotSupportedException: LINQ to Entities не распознает метод метода System.String Concat (System.Object), и этот метод не может быть преобразован в выражение хранилища.]
Филипп Мунин
-6

Ты можешь попробовать:

var items = from c in contacts
        select new ListItem
        {
            Value = Convert.ToString(c.ContactId), 
            Text = c.Name
        };
Тони Хойпель
источник
Приведенный выше код не будет работать, так как он выдаст сообщение об ошибке «LINQ to Entities не распознает метод метода System.String ToString (Int32)», и этот метод нельзя преобразовать в выражение хранилища ».
GK