2011-11-21 4 views
5

staram się to zrobić, ale ona mówi, że nie można używać FirstOrDefault,DbSet.FirstOrDefault()?

public static int GetId(this Context db, Type type, string name) 
{ 
    return db.Set(type).FirstOrDefault(x => x.Name == name).Id; 
} 

Błąd znajduje System.Data.Entity.DbSet” nie zawierają definicji „FirstOrDefault” i żadna metoda rozszerzenie „FirstOrDefault” przyjęcie pierwszego argumentu typu „System.Data.Entity.DbSet” można odnaleźć (czy brakuje using dyrektywa lub odwołania do zestawu?)

następnie próbowałem tej metody Cast ale który dał błąd Nie można utworzyć DbSe T z nierodzajowego DbSet dla obiektów typu „WindowStyle” (zresztą WindowStyle dziedziczy DomainEntity poniżej)

var set = db.Set(type).Cast<DomainEntity>(); 
return set.FirstOrDefault(x => x.Name == name).Id; 

Oto klasy

public class DomainEntity 
{ 
    public virtual int Id { get; set; } 
    public virtual string Name { get; set; } 
} 
+9

Czy masz 'używając System.Linq' na początku pliku? DbSet dziedziczy IEnumerable, ale funkcje LINQ są wszystkimi metodami Extension i nie będą dostępne bez instrukcji using. –

+0

Czy Twój drugi błąd ("Cast") jest błędem podczas kompilacji lub wykonywania? To zrobi dużą różnicę i ciężko wywnioskować z twojego pytania. –

+0

@Chris, tak, zrobiłem. Dobre pytanie. – Benjamin

Odpowiedz

8

Ta odpowiedź może nie pomóc w zależności od tego, jak „dynamiczny” sytuacja jest gdzie wywołać tę metodę, w zasadzie, jeśli znasz typ w czasie kompilacji, czy nie.Jeśli to wiesz, że możesz napisać metodę rodzajową Zamiast:

public static class MyExtensions 
{ 
    public static int? GetId<TEntity>(this Context db, string name) 
     where TEntity : DomainEntity 
    { 
     return db.Set<TEntity>() 
      .Where(x => x.Name == name) 
      .Select(x => (int?)x.Id) 
      .FirstOrDefault(); 
    } 
} 

Zmieniłem go do występu, bo nie trzeba ładować pełną podmiot jeśli chcesz tylko Id. Możesz pozwolić bazy danych wykonać pracę, aby wybrać właściwość, aby nieco poprawić wydajność. Zwracam również wartość zerową int w przypadku, gdy nie ma dopasowania do nazwy w bazie danych.

Można nazwać to w kodzie tak:

int? id = db.GetId<WindowStyle>("abc"); 

Jak widać na tym rozwiązaniu należy określić rodzaj WindowStyle w czasie kompilacji.

Zakłada się, że DomainEntity nie jest częścią Twojego modelu (nie ma DbSet<DomainEntity>), ale tylko podstawową klasą twoich obiektów. W przeciwnym razie rozwiązanie @Paul Keister byłoby łatwiejsze.

Edit

Alternatywnie można też spróbować wykonać następujące czynności:

public static class MyExtensions 
{ 
    public static int? GetId(this Context db, Type entityType, string name) 
    { 
     return ((IQueryable<DomainEntity>)db.Set(entityType)) 
      .Where(x => x.Name == name) 
      .Select(x => (int?)x.Id) 
      .FirstOrDefault(); 
    } 
} 

i nazywają to:

int? id = db.GetId("abc", someType); 

To będzie wyjątek chociaż przy starcie jeśli someType nie dziedziczy od DomainEntity. Wersja ogólna sprawdzi to podczas kompilacji. Tak więc, jeśli może preferować pierwszą wersję.

+0

Myślę, że to pomoże mi w końcu nauczyć się generyków! Dzięki. – Benjamin

+0

Gdybym mógł dać ci całą moją reputację, zrobiłbym to - tego właśnie szukam około pół roku i zawsze robiłem złe podejście. Thx, jesteś teraz naprawdę moim bogiem. – Zoka

0

Spróbuj odwrócenie go nieco zrobić

 
set.Where(x => x.Name == name).Select(o=>o.Id).FirstOrDefault(); 

Twoja osoba zwróci jednostkę zerową, a następnie spróbuje pobrać z niej identyfikator.

+0

Myślę, że próbowałem "Gdzie", ale miał ten sam problem co "FirstOrDefault". Dzięki. – Benjamin

+0

To rozwiązuje problem (niewłaściwe użycie 'FirstOrDefault'), ale nie ma to związku z pytaniem. –

6

Pierwsza konstrukcja nie będzie działać, ponieważ pracujesz z nietypowym DbSet, więc nie można zastosować metody rozszerzenia FirstOrDefault, która działa tylko z wersją ogólną. Wygląda na to, że już to rozumiesz, ponieważ już próbujesz uzyskać nietypowy DbSet. Błąd, który otrzymujesz za pomocą metody Cast(), jest spowodowany przez próbę przesłania DbSet do DbSet. Nie można na to pozwolić, ponieważ gdyby było, możliwe byłoby dodanie niezgodnych elementów do DbSet (obiekty typu innego niż WindowsStyle). Innym sposobem na powiedzenie tego jest to, że kowariancja nie jest obsługiwana dla DbSets, ponieważ DbSets zezwala na dodawanie.

Myślę, że będziesz musiał znaleźć inny sposób robienia tego, co chcesz zrobić. Mieszanie LINQ i dziedziczenie w ten sposób oczywiście problematyczne. Ponieważ masz zdefiniowany typ podstawowy i pracujesz tylko z atrybutami, które są dostępne w typie podstawowym, dlaczego nie po prostu zapytać o typ podstawowy?

public static int GetId(this Context db, string name) 
    { 
     return db.DomainEntities.FirstOrDefault(x => x.Name == name).Id; 
    } 

Jesteś pewnie martwi kolizji nazw pomiędzy różnymi typami, ale prawdopodobnie można zacząć patrzeć na to i pochodnych związków typu, aby upewnić się, że szukasz w odpowiednim typem. Jednym ze sposobów radzenia sobie z tym jest dodanie flagi typu do definicji DomainEntity.

+1

i co się dzieje tutaj, jeśli twój obiekt jest pusty i próbujesz podać referencję:) –

+0

Paul, Dzięki. Ale nie mam 'DbSet ' na 'DbContext'. Czy to dlatego mam problem? Używałem 'DomainEntity' do organizowania innych klas przez dziedziczenie. Myślę, że prawdziwy problem polega na tym, że nie rozumiem generyków. – Benjamin

+0

Próbowałem używać 'MakeGenericType', ale powiedział:' DbSet 'nie jest typem ogólnym. – Benjamin

2

Oto pomysł, który wydaje się działać.

public static int GetId(this Context db, Type type, string name) 
{ 
    var set = db.Set(type); 
    foreach (dynamic entry in set) 
     if (entry.Name == name) 
      return entry.Id; 
} 
+1

Powinieneś być w stanie zamienić 'dynamic' na' DomainEntity' tutaj, co da ci prawie to samo co moja odpowiedź. –

+3

Załadowanie pełnej tabeli do pamięci (prawdopodobnie 1 milion wierszy) w celu uzyskania pojedynczego identyfikatora wygląda bardzo nieefektywnie. Rozpoczęcie pętli spowoduje wykonanie zapytania 'set' bez filtra. Trzymaj się z daleka od tego rozwiązania. – Slauma

+0

@Slauma, poza tym zwraca mecz po iteracji całego stołu :) wpis został zmieniony. – Shimmy

3

Oto problem. Klasa DbSethas its own implementation of Cast<T>(), która TYLKO dopuszcza typy baz danych (takie jak Cast<WindowStyle>()), więc ta metoda nie zezwala na Cast<DomainEntity>() i rzuca wyjątek.

Zamiast tego należy użyć metody rozszerzenia IQueryable.Cast<T>(), która po prostu przesyła dane do typu podstawowego. Oto przykład:

var set = ((IQueryable)db.Set(type)).Cast<DomainEntity>(); 
return set.First(x => x.Name == name).Id; 
23

Może brakuje

using System.Linq; 
+0

To rozwiązało mnie jak czar! ;) – Sammy

+0

Zapomniałem upewnić się, że System.Linq był w użyciu! To prawie zawsze tam jest domyślnie i nigdy nie myślałem, żeby to sprawdzić. –