Problem z konwersją int na ciąg znaków w Linq na jednostki

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

Czy w ogóle mogę to osiągnąć? Zauważ, że w VB.NET nie ma problemu z użyciem pierwszego fragmentu, który działa po prostu świetnie, VB jest elastyczny, nie jestem w stanie przyzwyczaić się do surowości C # !!!

Shimmy Weitzhandler
źródło
2
.ToString () również nie działa dla LinqToEF w VB. IMHO, głupie.
StingyJack
5
@StingyJack, problem dotyczy ELINQ (podmioty linq 2), ponieważ tłumaczy twój kod na SQL, a jeśli chodzi o wewnętrzne żądanie ToString, nie wie jak przetłumaczyć „ToString” na SQL. W przeciwieństwie do obiektów linq 2, gdy nie ma tłumaczenia, a wszystko jest lambdami CLR, jest ono wykonywane bezpośrednio na żądanych obiektach.
Shimmy Weitzhandler
1
Jestem po prostu zirytowany, że pozwalają one na skompilowanie tego rodzaju błędu i że musiałem trollować na zawsze, aby znaleźć prosty angielski opis przyczyny (bez legalnej i akademickiej).
StingyJack
1
Masz rację, ale mają również rację, nie powinni tłumaczyć wszystkich CLR i dostosowanej funkcjonalności CLR na SQL, szczególnie nie we wczesnej wersji EF :) O ToString przeczytaj odpowiedź Briana: stackoverflow. com / pytania / 1066760 /…
Shimmy Weitzhandler
Świetnie, ale co z ludźmi używającymi 3.5, no 4? Co wtedy
Ekaterina

Odpowiedzi:

313

Z EF v4 możesz korzystać SqlFunctions.StringConvert. Nie ma przeciążenia dla int, więc musisz rzutować na podwójny lub dziesiętny. Twój kod wygląda następująco:

var items = from c in contacts
            select new ListItem
            {
                Value = SqlFunctions.StringConvert((double)c.ContactId).Trim(),
                Text = c.Name
            };
Brian Cauthon
źródło
234
Dlaczego, u licha, nie mieliby przeciążać int?
Jeremy Coenen
7
@Nestor To nie działa dla SQL Compact. Odkryłem to na własnej skórze.
Austin,
24
Aby uniknąć białych spacji przed wynikiem, powinieneś użyćSqlFunctions.StringConvert((double)c.ContactId).Trim()
Kim Tranjan
2
Wydaje się, że nie działa dla SQLite przy użyciu System.Data.SQLite Methode „System.String StringConvert (System.Nullable`1 [System.Double])„ w Typw ”System.Data.Objects.SqlClient.SqlFunctions 'kann nicht in einen Speicherausdruck für „LINQ to Entities” übersetzt werden. (nie można przetłumaczyć na „LINQ to Entities”)
OneWorld,
5
Doskonała odpowiedź! Pamiętaj tylko, że jeśli używasz EF 6, klasa została przeniesiona do innej przestrzeni nazw. Tak więc przed EF 6 powinieneś dołączyć: „System.Data.Objects.SqlClient” Jeśli aktualizujesz do EF 6 lub po prostu korzystasz z tej wersji, dołącz: „System.Data.Entity.SqlServer” Dołączając niepoprawną przestrzeń nazw do EF6, kod dobrze się skompiluje, ale zgłosi błąd czasu wykonywania. Mam nadzieję, że ta notatka pomoże uniknąć nieporozumień.
Leo
12

Rozwiązałem podobny problem, umieszczając konwersję liczby całkowitej na ciąg znaków poza zapytaniem. Można to osiągnąć poprzez umieszczenie zapytania w obiekcie.

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
źródło
Jest to jeden ze sposobów rozwiązania tego problemu, ale należy pamiętać, że
wydłuży
9

Użyj LinqToObject: kontakty. AsEnumerable ()

var items = from c in contacts.AsEnumerable()
            select new ListItem
            {
                Value = c.ContactId.ToString(),
                Text = c.Name
            };
Mohammadreza
źródło
Dzięki. Do Twojej wiadomości próbuję rozwiązać nieco inny problem. Używam LINQ do podmiotów / lambda i to działa. Próbowałem przekonwertować Int na String i użyć „Contains”, aby znaleźć pasujące wyniki -> tj. Db.contacts.AsEnumerable (). Where (c => c.ContactId.ToString (). Contains ( searchitem )). ToList (); ;
ejhost,
9
Jeśli zadzwonisz AsEnumerable, zapłacisz wysoką wydajność w większych bazach danych, ponieważ przyniesie to wszystko do pamięci. IEnumerablejest wolniejszy w porównaniu z tym, IQueryableże później jest wykonywany wyłącznie w bazie danych.
CodeArtist
5

SqlFunctions.StringConvert będzie działać, ale uważam, że jest to uciążliwe i przez większość czasu nie mam prawdziwej potrzeby wykonywania konwersji ciągów po stronie SQL.

To, co robię, jeśli chcę wykonywać operacje na łańcuchach, to najpierw wykonać zapytanie w linq-na-byty, a następnie manipulować żądłami w linq-na-obiektach. W tym przykładzie chcę uzyskać zestaw danych zawierający pełną nazwę kontaktu i ContactLocationKey, który jest ciągiem połączenia dwóch kolumn liczb całkowitych (ContactID i 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();

Przyznaję, że kłopotliwe jest pisanie dwóch anonimowych selekcji, ale argumentowałbym, że przewyższa to wygoda wykonywania funkcji łańcuchowych (i innych) nieobsługiwanych w L2E. Należy również pamiętać, że ta metoda prawdopodobnie wiąże się z ograniczeniem wydajności.

Walter Stabosz
źródło
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
źródło
Testowałeś to i działa? przeczytaj odpowiedź wcześniej.
Shimmy Weitzhandler
Tak, już go używam. Działa dla MVC3, EF4, CTP5, SQL CE4.
Nestor
To wydaje się bardziej eleganckie niż boks do podwojenia i używania StringConvert.
CmdrTallen,
9
Ale w tym przypadku pobierzesz wszystkie dane z bazy danych, a następnie załóż, że chcesz wcześniej przefiltrować tę listę return list.ToList();!!
Wahid Bitar
4
Kiedy nie masz dostępu do SqlFunctions, nie masz wielu innych opcji niż ta. Chciałbym jednak wykorzystali to dla mojego zapytania: 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();. Wykonanie tego w ten sposób pobiera tylko identyfikator / nazwę z bazy danych (zamiast wszystkich właściwości klienta) i wykonuje sortowanie przy użyciu bardziej wydajnego indeksu w bazie danych.
Brian Cauthon
3
var selectList = db.NewsClasses.ToList<NewsClass>().Select(a => new SelectListItem({
    Text = a.ClassName,
    Value = a.ClassId.ToString()
});

Po pierwsze, przekonwertuj na obiekt, a następnie toString () będzie poprawne.

phil hong
źródło
3

Odpowiedź Briana Cauthona jest doskonała! Tylko mała aktualizacja, dla EF 6 klasa została przeniesiona do innej przestrzeni nazw. Tak więc przed EF 6 powinieneś dołączyć:

System.Data.Objects.SqlClient

Jeśli aktualizujesz do EF 6 lub po prostu używasz tej wersji, dołącz:

System.Data.Entity.SqlServer

Dołączając niepoprawną przestrzeń nazw do EF6, kod kompiluje się dobrze, ale generuje błąd czasu wykonywania. Mam nadzieję, że ta notatka pomoże uniknąć nieporozumień.

Lew
źródło
Muszę powiedzieć, że twoja odpowiedź również jest doskonała. Uaktualniłem do EF6 i wszędzie szukałem SqlFunctions. Twoja odpowiedź wskazała mi właściwy kierunek. Dodam tylko, że potrzebujesz również odwołania do EntityFramework.SqlServer (możesz mieć tylko odwołanie do EntityFramework).
Metalogic
2

Natknąłem się na ten sam problem podczas konwersji mojej aplikacji MVC 2 na MVC 3 i aby dać inne (czyste) rozwiązanie tego problemu, chcę opublikować to, co zrobiłem ...

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

GetProducers () po prostu zwraca kolekcję encji producentów. PS SqlFunctions.StringConvert nie działało dla mnie.

Barry C.
źródło
2

Jeśli Twój „kontakt” działa jak ogólna lista, mam nadzieję, że poniższy kod działa dobrze.

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

Dzięki.

Nawaz
źródło
2

Jeszcze jedno rozwiązanie:

c.ContactId + ""

Wystarczy dodać pusty ciąg, a zostanie on przekonwertowany na ciąg.

Igor Valikovsky
źródło
Zwrócony błąd: System.NotSupportedException: Nie można rzutować typu „System.Int64”, aby wpisać „System.Object”. LINQ to Entities obsługuje tylko rzutowanie typów podstawowych lub wyliczeń EDM.
QMaster,
1

Korzystanie z MySql SqlFunctions.StringConvertnie działało dla mnie. Ponieważ używam SelectListItemw swoim projekcie ponad 20 miejsc, chciałem rozwiązania, które działałoby bez zniekształcania instrukcji ponad 20 LINQ. Moim rozwiązaniem było podklasę SelectedListItem, aby zapewnić funkcję ustawiania liczb całkowitych, która odsuwa konwersję typów od LINQ. Oczywiście to rozwiązanie jest trudne do uogólnienia, ale było bardzo pomocne w moim konkretnym projekcie.

Aby użyć, utwórz następujący typ i użyj w zapytaniu LINQ zamiast SelectedListItemi użyj IntValue zamiast Value.

public class BtoSelectedListItem : SelectListItem
{
    public int IntValue
    {
        get { return string.IsNullOrEmpty(Value) ? 0 : int.Parse(Value); }
        set { Value = value.ToString(); }
    }
}
raider33
źródło
1

jeśli używasz frameworku encji i chcesz, aby jedyna int była akceptowalna, możesz użyć tego w zapytaniu linq, możesz to wypróbować

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

zadziała, ponieważ użycie (int) rzuci twoją wartość na int, więc nie potrzebujesz żadnej konwersji łańcucha na int i uzyskasz pożądany wynik.

to działało dla mnie w moim projekcie, myślę, że byłoby to dla ciebie pomocne

Saurabh Solanki
źródło
-2

Rozumiem, że musisz utworzyć klasę częściową, aby „rozszerzyć” swój model i dodać właściwość, która jest tylko do odczytu, która może wykorzystać resztę właściwości klasy.

public partial class Contact{

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

Następnie

var items = from c in contacts
select new ListItem
{
    Value = c.ContactIdString, 
    Text = c.Name
};
Mcbeev
źródło
Nie, nie można używać właściwości niestandardowych w LINQ to Entities (w .NET 3.5).
Craig Stuntz
1
Nie testowałem tego, ale też nie zadziała. ponieważ nie jest to właściwość pola tabeli. Mógłbym to zrobić najpierw za pomocą ToArray (), a następnie linqing nad obiektami, ale chcę zapytać DB. Zakładam, że nie będzie w stanie tego zrobić. Utworzyłem własny ListItem, który przyjmuje pole int. To działa dla mnie lepiej.
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
};

Stwierdziłem, że SqlFunctions.StringConvert((double)c.Age)to nie działa dla mnie albo pole jest typuNullable<Int32>

Wiele razy szukałem w ciągu ostatnich kilku dni prób i błędów, aby to znaleźć.

Mam nadzieję, że pomoże to kilku programistom.

Ken Blackford
źródło
1
Nie działa dla mnie. Zgłasza wyjątek „ ... System.String Concat(System.Object)nie można przetłumaczyć na wyrażenie sklepu ... ”.
Slauma,
1
Dla mnie też nie działa. Otrzymuję również komunikat „System.NotSupportedException: LINQ to Entities nie rozpoznaje metody„ System.String Concat (System.Object) ”i tej metody nie można przetłumaczyć na wyrażenie sklepu.”
camainc,
1
NIE DZIAŁA - OPROGRAMUJ ODPOWIEDŹ [NotSupportedException: LINQ to Entities nie rozpoznaje metody „System.String Concat (System.Object)”, a tej metody nie można przetłumaczyć na wyrażenie sklepu.]
Philipp Munin
-6

Możesz spróbować:

var items = from c in contacts
        select new ListItem
        {
            Value = Convert.ToString(c.ContactId), 
            Text = c.Name
        };
Tony Heupel
źródło
Powyższy kod nie będzie działał, ponieważ spowoduje zgłoszenie błędu „LINQ to Entities nie rozpoznaje metody„ System.String ToString (Int32) ”i tej metody nie można przetłumaczyć na wyrażenie sklepu.”
GK