2012-01-23 9 views
38

Używam struktury entity do łączenia się z bazą danych. Mam jeden mały problem:Odzyskiwanie obiektu z elementu entityframework bez pola ONE

Mam jedną tabelę, która ma jedną kolumnę varbinary (MAX) (z filestream).

Używam polecenia SQL do zarządzania częścią "Dane", ale EF do reszty (metadane pliku).

Mam jeden kod, który musi uzyskać wszystkie pliki id, nazwa pliku, guid, data modyfikacji, ... pliku. Nie wymaga to w ogóle pola "Dane".

Czy istnieje sposób na odzyskanie listy, ale bez wypełnienia tej kolumny?

Coś

context.Files.Where(f=>f.xyz).Exclude(f=>f.Data).ToList(); 

??

Wiem, że mogę tworzyć anonimowe obiekty, ale muszę przekazać wynik do metody, więc żadnych anonimowych metod. I nie chcę umieszczać tego na liście typu anonimowego, a następnie utworzyć listę mojego nieanonimicznego typu (plik).

Celem jest, aby tego uniknąć:

using(RsSolutionsEntities context = new RsSolutionsEntities()) 
{ 
    var file = context.Files 
     .Where(f => f.Id == idFile) 
     .Select(f => new { 
      f.Id, f.MimeType, f.Size, f.FileName, f.DataType, 
      f.DateModification, f.FileId 
     }).FirstOrDefault(); 

    return new File() { 
     DataType = file.DataType, DateModification = file.DateModification, 
     FileId = file.FileId, FileName = file.FileName, Id = file.Id, 
     MimeType = file.MimeType, Size = file.Size 
    }; 
} 

(używam tutaj typ anonimowy, bo inaczej dostaniesz NotSupportedException: jednostki lub typu złożonego „ProjectName.File” nie może być wykonana w sposób . LINQ podmiotom zapytania)

(np kod rzucać poprzedni wyjątek:

File file2 = context.Files.Where(f => f.Id == idFile) 
    .Select(f => new File() {Id = f.Id, DataType = f.DataType}).FirstOrDefault(); 

i „Plik” jest typem uzyskać z context.Files.ToList(). Jest to dobra klasa:

using File = MyProjectNamespace.Common.Data.DataModel.File; 

plik jest znaną klasą moim EF datacontext:

public ObjectSet<File> Files 
{ 
    get { return _files ?? (_files = CreateObjectSet<File>("Files")); } 
} 
private ObjectSet<File> _files; 
+3

Czy możesz usunąć tę kolumnę z obiektu EF? – Gabe

+1

Chciałbym móc, ale to jest kolumna "NON NULL", a EF nie podoba się, gdy mam kolumnę inną niż null, które nie są w modelu – J4N

+0

Jedynym powodem, dla którego EF miałby problem z wykluczonymi kolumnami innymi niż zero jest podczas 'INSERT' do bazy danych. Możesz obejść to, używając procedur, wyzwalaczy i innych metod. Dla "SELECT" absolutnie ** można ** wykluczyć kolumny tabeli. – Yuck

Odpowiedz

14

Czy istnieje sposób na odzyskanie listy, ale bez wypełnienia tej kolumny?

Nie bez projekcji, której chcesz uniknąć. Jeśli kolumna jest odwzorowana, jest to naturalna część twojej istoty. Podmiot bez tej kolumny nie jest kompletny - jest to inny zestaw danych = rzutowanie.

Używam tutaj typ anonimowy, bo inaczej dostaniesz NotSupportedException: Podmiot lub kompleks typu „ProjectName.File” nie może być wykonana w LINQ podmiotom zapytania.

Jako wyjątek nie można projektować na zmapowany obiekt. Wspomniałem o powodzie powyżej - projekcja tworzy inny zbiór danych, a EF nie lubi "jednostek częściowych".

Błąd 16 Błąd 3023: Problem z fragmentami mapowania zaczynając od linii 2717: Kolumna Files.Data w tabeli pliki muszą być odwzorowane: To nie ma wartości domyślnej i nie jest pustych.

Nie wystarczy usunąć własność od projektanta. Musisz otworzyć EDMX jako XML i usunąć kolumnę z SSDL, która sprawi, że twój model będzie bardzo delikatny (każda aktualizacja z bazy danych spowoduje powrót do twojej kolumny). Jeśli nie chcesz mapować kolumny, powinieneś użyć widoku bazy danych bez kolumny i zmapować widok zamiast tabeli, ale nie będziesz mógł wstawić danych.

Aby obejść wszystkie problemy, należy użyć table splitting i oddzielić problematyczną kolumnę binarną od innej jednostki z relacją 1: 1 do głównej jednostki File.

+8

To jest powód do porzucenia EF. Absolutnie szalony, to jest chleb i masło, jak to się dzieje, z wyjątkiem jednej dużej kolumny, która PERFEKCYJNIE pasuje w normalnych rozwiązaniach bazodanowych jako pojedyncza kolumna maks., Podczas gdy cała reszta może być użyta w wyliczaniu tych obiektów i tak dalej.Rozwiązania w obecnym stanie znacznie zwiększają złożoność - czy to dodawanie wszelkiego rodzaju dodatkowych typów do wykonywania rzutów, jak i kod do konwersji dokładnie tego samego typu na ten sam iz powrotem; lub, tworząc tabelę 1: 1 i wszystko, co nie jest potrzebne. –

+0

Dlaczego chcemy unikać prognoz? Czy to ze względu na wydajność? – Gilles

+0

@Gilles: Przepraszamy, jest zbyt ostry. Nie chciałem sugerować, że projekcja jest zła. Projekcja jest świetna, jeśli znasz konsekwencje. Konsekwencją jest to, że projekcja nie jest jednostką - EF nie zapewni żadnej z jej zaawansowanych funkcji, gdy zwrócisz projekcję. Chętne ładowanie może nie działać (w zależności od zapytania), leniwy ładowanie nie zadziała, śledzenie zmian nie zadziała, itp. –

8

zrobiłbym coś takiego:

var result = from thing in dbContext.Things 
      select new Thing { 
       PropertyA = thing.PropertyA, 
       Another = thing.Another 
       // and so on, skipping the VarBinary(MAX) property 
      }; 

Gdzie jest twoja jednostka Thing że EF wie, jak się zmaterializować. Wynikowa instrukcja SQL nie powinna zawierać dużej kolumny w zestawie wyników, ponieważ nie jest potrzebna w zapytaniu.

EDIT: Z Twoich edycji, pojawi się błąd NotSupportedException: Podmiot lub kompleks typu „ProjectName.File” nie może być wykonana w LINQ podmiotom zapytania. ponieważ nie zmapowano tej klasy jako encji. Użytkownik nie może dołączać obiektów w kwerendach LINQ do jednostek, których EF nie zna i oczekiwać, że wygeneruje odpowiednie instrukcje SQL.

Można odwzorować inny typ, który wyklucza kolumnę VarBinary(MAX) w swojej definicji lub użyć powyższego kodu.

+1

Już próbowałem, ale EF mówi mi, że nie mogę umieścić typu złożonego w selekcji. – J4N

+0

Czy możesz przesłać kod, który wypróbowałeś i błąd, który otrzymałeś w powyższym pytaniu? Wygląda na to, że próbujesz użyć obiektu typu, którego EF nie zna i nie może generować instrukcji SQL. – Yuck

+0

Edytowałem mój poprzedni kod – J4N

5

można to zrobić:

var files = dbContext.Database.SqlQuery<File>("select FileId, DataType, MimeType from Files"); 

lub to:

var files = objectContext.ExecuteStoreQuery<File>("select FileId, DataType, MimeType from Files"); 

zależności od wersji EF

+0

jeśli masz kontekst dbcontext zamiast objectcontext, możesz uzyskać dostęp do kontekstu obiektu. (dbContext jako IObjectContextAdapter) .ObjectContext; Dostępna jest również metoda dbcontext dbContext.Database.SqlQuery (ciąg sql); –

+0

To zadziałało dla mnie. Należy jednak zauważyć, że dodałem kolumnę pustą dla pustego pola danych. "SELECT Id, Name, Path, CreateDate, LastUpdated, null AS Data From [File]"; – Rhyous

0

Chciałbym podzielić się moje próby obejścia tego problemu w przypadku ktoś inny znajduje się w tej samej sytuacji.

Zacząłem od tego, co sugerował Jeremy Danyow, co jest dla mnie mniej bolesną opcją.

// You need to include all fields in the query, just make null the ones you don't want. 
var results = context.Database.SqlQuery<myEntity>("SELECT Field1, Field2, Field3, HugeField4 = NULL, Field5 FROM TableName"); 

W moim przypadku musiałem obiekt IQueryable<> wynik więc dodałem AsQueryable() na końcu. To oczywiście pozwala mi dodawać połączenia do .Where, .Take i innych poleceń, które wszyscy znamy i działały dobrze. Ale jest zastrzeżenie:

Normalny kod (w zasadzie context.myEntity.AsQueryable()) zwrócił System.Data.Entity.DbSet<Data.DataModel.myEntity>, podczas gdy ta metoda zwróciła System.Linq.EnumerableQuery<Data.DataModel.myEntity>.

Najwyraźniej oznacza to, że moje niestandardowe zapytanie zostanie wykonane "tak jak jest", gdy tylko zajdzie taka potrzeba, a filtrowanie dodane później zostanie wykonane później, a nie w bazie danych.

Dlatego próbowałem naśladować obiekt Entity Framework za pomocą dokładnego zapytania, które EF tworzy, nawet z tymi aliasami [Extent1], ale to nie zadziałało.Analizując obiekt wynikowy, jego zapytanie skończyło jak

FROM [dbo].[TableName] AS [Extent1].Where(c => ...

zamiast oczekiwać

FROM [dbo].[TableName] AS [Extent1] WHERE ([Extent1]...

W każdym razie, to działa, i tak długo, jak tabela nie jest ogromny, ta metoda być wystarczająco szybki. W przeciwnym razie nie masz możliwości, aby ręcznie dodać warunki, łącząc łańcuchy, takie jak klasyczny dynamiczny SQL. To bardzo prosty przykład w przypadku, gdy nie wiem, co mówię:

string query = "SELECT Field1, Field2, Field3, HugeField4 = NULL, Field5 FROM TableName"; 
if (parameterId.HasValue) 
    query += " WHERE Field1 = " + parameterId.Value.ToString(); 
var results = context.Database.SqlQuery<myEntity>(query); 

W przypadku metoda czasami potrzebuje tego pola można dodać parametr bool a następnie zrobić coś takiego:

IQueryable<myEntity> results; 
if (excludeBigData) 
    results = context.Database.SqlQuery<myEntity>("SELECT Field1, Field2, Field3, HugeField4 = NULL, Field5 FROM TableName").AsQueryable(); 
else 
    results = context.myEntity.AsQueryable(); 

Jeśli komuś udaje się, aby rozszerzenia Linq działały poprawnie, tak jakby był to oryginalny obiekt EF, proszę skomentuj, abym mógł zaktualizować odpowiedź.

1

miałem ten wymóg, ponieważ mam Document podmiot, który ma pole Content z zawartością pliku, czyli jakieś 100 MB, a ja mam funkcję wyszukiwania, które chciałem zwrócić resztę kolumn.

wybrałem używać projekcji:

IQueryable<Document> results = dbContext.Documents.Include(o => o.UploadedBy).Select(o => new { 
    Content = (string)null, 
    ContentType = o.ContentType, 
    DocumentTypeId = o.DocumentTypeId, 
    FileName = o.FileName, 
    Id = o.Id, 
    // etc. even with related entities here like: 
    UploadedBy = o.UploadedBy 
}); 

Wtedy mój kontroler WebAPI przechodzi ten results obiektu do wspólnej funkcji paginacja, które stosuje się .Skip, .Take i .ToList.

Oznacza to, że po uruchomieniu zapytania nie ma dostępu do kolumny Content, więc dane 100 MB nie są dotykane, a zapytanie jest tak szybkie, jak by się chciało/oczekiwało.

Następnie oddaję go z powrotem do mojej klasy DTO, która w tym przypadku jest dokładnie taka sama jak klasa encji, więc może to nie być krok, który musisz wdrożyć, ale jest zgodny z moim typowym wzorcem kodowania WebApi tak:

var dtos = paginated.Select(o => new DocumentDTO 
{ 
    Content = o.Content, 
    ContentType = o.ContentType, 
    DocumentTypeId = o.DocumentTypeId, 
    FileName = o.FileName, 
    Id = o.Id, 
    UploadedBy = o.UploadedBy == null ? null : ModelFactory.Create(o.UploadedBy) 
}); 

Potem powrót do listy DTO:

return Ok(dtos); 

więc używa projekcji, co może nie pasować do wymagań ich twórców, ale jeśli używasz klas dto, jesteś konwersja w każdym razie. Można równie dobrze wykonać następujące czynności, aby powrócić ich jako swoich rzeczywistych podmiotów:

var dtos = paginated.Select(o => new Document 
{ 
    Content = o.Content, 
    ContentType = o.ContentType, 
    DocumentTypeId = o.DocumentTypeId, 
    //... 

zaledwie kilku dodatkowych kroków, ale to działa dobrze dla mnie.

0

Próbowałem to:

Z wykresu edmx (EF 6), kliknąłem kolumnę chciałem ukryć przed EF i ich właściwości można ustawić ich getter i setter prywatny. W ten sposób dla mnie to działa.

Zwracam niektóre dane, które zawierają odniesienie do użytkownika, więc chciałem ukryć pole Hasło, mimo że jest zaszyfrowane i zasolone, po prostu nie chciałem tego na moim jsonie i nie chciałem zrobić:

Select(col => new {}) 

ponieważ jest to trudny do stworzenia i utrzymania, zwłaszcza w przypadku dużych stołów z wieloma relacjami.

Wadą tej metody jest to, że jeśli kiedykolwiek zregenerujesz swój model, będziesz musiał ponownie zmodyfikować ich pozyskiwanie i ustawianie.

0

Używam tutaj typ anonimowy, bo inaczej dostaniesz NotSupportedException: Podmiot lub kompleks typu „ProjectName.File” nie może być wykonana w LINQ podmiotom zapytania.

var file = context.Files 
     .Where(f => f.Id == idFile) 
     .FirstOrDefault() // You need to exeucte the query if you want to reuse the type 
     .Select(f => new { 
      f.Id, f.MimeType, f.Size, f.FileName, f.DataType, 
      f.DateModification, f.FileId 
     }).FirstOrDefault(); 

A także nie jest to złe praktyki do de-normalizacji tabeli do dalszego, tj jeden z metadanych i jeden z ładunkiem uniknięcia projekcję. Projekcja działałaby, jedynym problemem jest konieczność edycji za każdym razem, gdy nowa kolumna zostanie dodana do tabeli.

Powiązane problemy