Havuzlar IQueryable'e geri dönmeli mi?


66

Örneklerini iade eden havuzlara sahip birçok proje görüyorum IQueryable. Bu, ek filtrelere izin verir ve sıralama, IQueryableüretilen farklı SQL'e çeviren diğer kodlarla gerçekleştirilebilir . Bu modelin nereden geldiğini ve iyi bir fikir olup olmadığını merak ediyorum.

En büyük endişem, bir IQueryablenumaralandırıldığı zaman, veritabanına bir süre sonra ulaşacağıma dair bir söz olduğudur. Bu, bir hatanın havuzun dışına atılacağı anlamına gelir. Bu, Entity Framework istisnasının uygulamanın farklı bir katmanına atıldığı anlamına gelebilir.

Ayrıca geçmişte Çoklu Aktif Sonuç Kümeleri (MARS) ile ilgili sorunlara da girdim (özellikle işlemleri kullanırken) ve bu yaklaşım bunun daha sık gerçekleşmesine neden olacak gibi görünüyor.

Depo kodunu terk etmeden önce veritabanının isabetli olduğundan emin olmak için her zaman LINQ ifadelerimin her birini AsEnumerableveya ToArraysonunu çağırdım .

Döndürmenin IQueryablebir veri katmanı için bir yapı taşı olarak yararlı olup olmadığını merak ediyorum . Bir depo ile daha da büyük bir bina oluşturmak için başka bir depo çağıran oldukça abartılı bir kod gördüm IQueryable.


Db, sorgu yapımı sırasında ertelenen sorgu yürütme sırasında mevcut olmasaydı, fark ne olurdu? Tüketim kodu ne olursa olsun bu durumla baş etmek zorunda kalacaktı.
Steven Evers

İlginç bir soru, ama muhtemelen cevap vermek zor olacak. Basit bir arama, sorunuzla ilgili oldukça ateşli bir tartışma gösteriyor: duckduckgo.com/?q=repository+return+iqueryable
Corbin Mart

6
Her şey, müşterilerinizin ertelenmiş uygulamadan faydalanabilecek Linq cümleleri eklemelerine izin vermek isteyip istemediğinize bağlı. whereErtelenen bir madde eklerlerse , sonuç kümesinin tamamını değil, IQueryableyalnızca bu verileri kablo üzerinden göndermeniz gerekir .
Robert Harvey,

Depo deseni kullanıyorsanız - hayır, IQueryable'ü iade etmemelisiniz. İşte güzel bir neden .
Arnis Lapsa

1
@SteveEvers Çok büyük bir fark var. Depoma bir istisna atılırsa, veri katmanına özgü bir istisna türüyle sarabilirim. Daha sonra gerçekleşirse, orjinal istisna türünü orada yakalamalıyım. İstisna kaynağına ne kadar yakın olursam, buna neyin neden olduğunu o kadar çok bilirim Bu anlamlı hata mesajları oluşturmak için önemlidir. IMHO, EF'e özel bir istisnanın havuzumu terk etmesine izin vermek, kapsülleme ihlalidir.
Travis Parks

Yanıtlar:


47

IQueryable'ü iade etmek kesinlikle havuzun tüketicilerine daha fazla esneklik sağlayacak. Doğal olarak hem fayda hem de koltuk değneği olabilen sonuçları daralma sorumluluğunu üstlenir.

İyi tarafta, istediğiniz veriyi elde etmek için tonlarca depo yöntemi (en azından bu katman üzerinde) oluşturmanız gerekmez - GetAllActiveItems, GetAllNonActiveItems, vb. Tercihinize bağlı olarak, yine, bu iyi veya kötü olabilir. Sen olacak (/ gerekir) uygulamaları uyması davranışsal sözleşmeler tanımlamak gerekir, ama bu nereye gittiğini size kalmış.

Böylece cesur alma mantığını havuzun dışına koyabilir ve kullanıcının istediği şekilde kullanmasına izin verebilirsiniz. Yani açığa IQueryable en esnek şekilde ve vb bellek filtreleme, aksine verimli sorgulama için izin verir ve olabilir yöntemleri getirilirken özel verilerin bir ton yapma ihtiyacını azaltır.

Öte yandan, şimdi kullanıcılarınıza bir av tüfeği verdiniz. Onlar niyetinde olmasanız da şeyler (.include () overusing ağır ağır sorgulama yapabilmesi ve yapıyor yapabilirsiniz bellek size verdik çünkü temelde katmanlarını ve davranışsal kontrolleri-yan adım olur, vs kendi uygulamalarda filtreleme), tam erişim.

Bu yüzden ekibe, deneyimlerine, uygulamanın boyutuna, genel katmanlamaya ve mimariye bağlı olarak… duruma göre değişir: - \


Bunun için çalışmak istersen, sırayla DB'yi çağıran ancak katman ve davranış kontrolleri ekleyen IQueryable'ünü iade edebileceğini unutma.
psr

1
@psr Bununla ne demek istediğinizi açıklayabilir misiniz?
Travis Parks

1
@TravisParks - IQueryable'in DB tarafından uygulanması gerekmiyor, IQueryable sınıflarını kendin yazabilirsin - bu oldukça zor. Böylece, IQueryable veri tabanının etrafına bir IQueryable sarıcısı yazabilir ve IQueryable limitinizin altta yatan DB'ye tam erişimini sağlayabilirsiniz. Fakat bu yeterince yapılmasını beklemeyeceğim kadar zor.
psr

2
IQueryables'ı geri getirmeseniz bile, bir Expression<Func<Foo,bool>>yönteminize basitçe geçerek birçok yöntem (GetAllActiveItems, GetAllNonActiveItems, vb.) Yapmanıza engel olabileceğinizi unutmayın. Bu parametreler Wheredoğrudan yönteme geçirilebilir , böylece tüketicinin IQueryable <Foo> 'ya doğrudan erişime ihtiyaç duymadan kendi filtresini seçmesine olanak tanır.
Flater

1
@hanzolo: Yeniden düzenleme ile ne demek istediğine bağlı. Tasarımı değiştirmenin (örneğin yeni alanlar eklemek veya ilişkileri değiştirmek) katmanlı ve gevşek bir şekilde birleştirilmiş bir kod tabanına sahip olduğunuzda daha fazla acı verici olduğu doğrudur; ancak yeniden düzenleme, yalnızca bir katmanı değiştirmeniz gerektiğinde daha az acı vericidir . Bunların tümü, uygulamayı yeniden tasarlamayı bekleyip beklemeyeceğinize veya aynı temel mimariyi uygulamanızı basitleştirip değiştirmeyeceğinize bağlıdır. Her iki durumda da gevşek kuplajı hala savunurdum, ancak çekirdek alan kavramlarını değiştirirken yeniden yapılanmaya katkıda bulunduğunu yanlış değilsiniz.
Flater

29

IQueryable'i kamu arayüzlerine göstermek iyi bir uygulama değil. Nedeni temeldir: söylendiği gibi IQueryable gerçekleştirmesi sağlayamazsınız.

Genel arayüz, sağlayıcı ve müşteriler arasındaki bir sözleşmedir. Ve çoğu durumda tam bir uygulama beklentisi vardır. IQueryable bir hile:

  1. IQueryable uygulamak neredeyse imkansız. Ve şu anda uygun bir çözüm yok. Varlık Çerçevesinde bile birçok Desteklenmeyen Özel Durum var .
  2. Bugün Queryable sağlayıcıları altyapıya büyük bağımlılık gösteriyor. Sharepoint'in kendi kısmi uygulaması var ve benim SQL sağlayıcımın kısmi bir uygulaması var.

Havuz düzeni bize katmanlaştırma ve en önemlisi gerçekleştirmenin bağımsızlığı ile net bir temsil sağlar. Böylece gelecekteki veri kaynağında değişebiliriz.

Soru, " Veri kaynağı ikame olasılığı olmalı mı? "

Verinin yarın bir kısmı SAP servislerine, NoSQL veri tabanına veya sadece metin dosyalarına taşınabilirse, IQueryable arayüzünün uygun şekilde uygulanmasını garanti edebilir miyiz?

(Bunun neden kötü bir uygulama olduğu hakkında daha iyi noktalar)


Bu cevap, geçici dışsal bağlantıya danışmadan tek başına durmaz. En azından dış kaynak tarafından yapılan kilit noktaları özetlemeyi düşünün ve kişisel görüşünüzü yanıtınızdan uzak tutmaya çalışın ("duyduğum en kötü fikir"). Ayrıca, IQueryable ile ilgisi olmayan görünen kod pasajını kaldırmayı da düşünün.
Lars Viklund

1
Thank @LarsViklund, cevap, örnekler ve problem açıklaması ile güncellenmiştir
Artru

19

Gerçekçi olarak, ertelenmiş infaz istiyorsanız üç seçeneğiniz var:

  • Bu şekilde yap - bir tane göster IQueryable.
  • Belirli filtreler veya "sorular" için özel yöntemler ortaya koyan bir yapı uygulayın. ( GetCustomersInAlaskaWithChildren, aşağıda)
  • Kesinlikle yazılmış bir filtre / sıralama / sayfa API'sini ortaya çıkaran ve IQueryabledahili olarak derleyen bir yapı uygulayın .

Üçüncüyü tercih ediyorum (iş arkadaşlarının da ikincisini uygulamasına yardım etsem de), ancak belli ki kurulum ve bakım da var. (T4 kurtarmaya!)

Düzenleme : Bahsettiğim konuyu çevreleyen karmaşayı gidermek için, IQueryable'den çalınan aşağıdaki örneği göz önünde bulundurun

Böyle bir şeyi açığa vuracağınız senaryoda:

public class CustomerRepo : IRepo 
{ 
     private DataContext ct; 
     public Customer GetCustomerById(int id) { ... } 
     public Customer[] GetCustomersInAlaskaWithChildren() { ... } 
} 

Bahsettiğim API GetCustomersInAlaskaWithChildren, esnek bir şekilde ifade etmenize (veya başka bir ölçüt kombinasyonuna) izin verecek bir yöntemi göstermenize izin verir ve repo bunu IQueryable olarak uygular ve sonuçları size verir. Fakat asıl önemli olan, depo katmanı içerisinde yer almasıdır, bu nedenle hala ertelenmiş uygulamadan yararlanmaktadır. Sonuçları bir kez geri aldıktan sonra, üzerinde LINQ nesnelerini hala kalbinizin içeriğine ekleyebilirsiniz.

Bunun gibi bir yaklaşımın başka bir avantajı, POCO sınıfları oldukları için, bu havuzun bir web veya WCF servisinin arkasında yaşayabileceği; LINA hakkında ilk şeyi bilmeyen AJAX veya diğer arayanlara maruz kalabilir.


4
Yani, .NET'in yerleşik sorgu API'sinin yerine geçmek için bir sorgu API'sı oluşturacaksınız?
Michael Brown

4
Bana oldukça anlamsız geliyor
ozz 26:13

7
Ve benim cevap - - Bu sorunun noktasında @MikeBrown sergilemek istediğiniz olmasıdır davranışı veya avantajları arasında IQueryable maruz kalmadan IQueryablekendini . Yerleşik API ile bunu nasıl yapacaksınız?
GalacticCowboy

2
Bu çok iyi bir totoloji. Bundan neden kaçınmak istediğini açıklamamışsın. Heck WCF Veri Servisleri kablo boyunca IQueryable'ü ortaya koyuyor. Seni almaya çalışmıyorum, sadece bu hoşnutsuzluğun IQueryable insanlara sahip olduğunu anlamıyorum.
Michael Brown

2
Eğer sadece bir IQueryable kullanacaksanız, neden bir havuz kullanıyorsunuz? Havuzlar, uygulama ayrıntılarını gizlemeye yöneliktir. (Ayrıca, WCF Veri Servisleri, son 4 yılda çalıştığım çözümlerin çoğundan biraz daha yenidir. parlak yeni bir oyuncak kullanılması.)
GalacticCowboy

8

Gerçekten sadece bir meşru cevap var: bu, havuzun nasıl kullanılacağına bağlı.

Bir uçta, deponuz bir DBContext etrafındaki çok ince bir sargıdır, böylece veritabanı tabanlı bir uygulamanın etrafına test edilebilir bir kaplama enjekte edebilirsiniz. CRUD uygulamanızın buna hiç ihtiyacı olmayacağından, bunun arkasında LINQ dostu bir DB olmadan bağlantısız bir şekilde kullanılabileceğine dair gerçek bir beklenti yoktur. O zaman elbette, neden IQueryable kullanmıyorsunuz? Faydaların çoğunu [okumak: gecikmeli uygulama] alırken IEnumarable'ı tercih ederim ve kirli gibi hissetmezsiniz.

Bu uçta oturmazsanız, depo modelinin ruhundan yararlanmak ve temel bir veritabanı bağlantısına sahip olmayan uygun maddi koleksiyonları iade etmek için çok çalışacağım.


6
Dönme IEnumerablekonusunda ((IEnumerable<T>)query).LastOrDefault(), çok farklı olduğunu not etmek önemlidir query.LastOrDefault()(varsayalım queryki bir IQueryable). Bu yüzden, sadece IEnumerableyine de tüm unsurları yineleyecekseniz geri dönmek mantıklıdır .
Lou

7
 > Should Repositories return IQueryable?

HAYIR test edilmiş geliştirme / son test yapmak istiyorsanız, çünkü işinizi (= depo-tüketici) veri tabanından veya başka bir IQueryable sağlayıcıdan izole ederken test etmek için sahte bir depo oluşturmanın kolay bir yolu yoktur.


6

Saf mimari bakış açısından, IQueryable sızan bir soyutlamadır. Genel olarak, kullanıcıya çok fazla güç sağlar. Olduğu söyleniyor, mantıklı olduğu yerler var. OData kullanıyorsanız, IQueryable, kolayca filtrelenebilen, sıralanabilir, gruplanabilir ... vb. Bir uç nokta sağlamayı son derece kolaylaştırır. Aslında, varlıklarımın haritalandığı ve IQueryable'ün yerine DTO'ları döndürdüğü DTO'ları oluşturmayı tercih ederim. Bu şekilde verilerimi önceden filtreliyorum ve sonuçları sadece müşterinin kişiselleştirmesine izin vermek için yalnızca IQueryable yöntemini kullanıyorum.


6

Ben de böyle bir seçenekle karşılaştım.

Öyleyse, olumlu ve olumsuz yanları özetleyelim:

pozitif:

  • Esneklik. Müşteri tarafının yalnızca tek bir depo yöntemiyle özel sorgular oluşturmasına izin vermek kesinlikle esnek ve kullanışlıdır.

Negatif:

  • Denenemez . (aslında genellikle ORM'nizin altında yatan IQueryable uygulamasını test edeceksiniz. Ama bunun yerine mantığınızı test etmelisiniz)
  • Sorumluluğu bulanıklaştırır . Deponuzdaki o özel yöntemin ne yaptığını söylemek imkansız. Aslında, tüm veritabanında istediğin her şeyi geri getirebilen bir tanrı metodudur.
  • Güvensiz . Bu depo yöntemiyle neyin sorgulanabileceğine dair hiçbir kısıtlama olmadan, bazı durumlarda bu tür uygulamalar güvenlik açısından güvensiz olabilir.
  • Önbellekleme sorunları (veya genel olması için, işlem öncesi veya sonrası sorunları). Genelde, örneğin uygulamanızdaki her şeyi önbelleğe almak akıllıca değildir. Veritabanınızı önemli ölçüde yükleyebilecek bir şeyi önbelleğe almaya çalışacaksınız. Ancak, bunun nasıl yapılacağı, yalnızca bir havuz yönteminiz varsa, istemcilerin veritabanına karşı neredeyse her sorguyu yayınlamak için kullandıkları?
  • Günlük kaydı sorunları . Depo katmanına bir şey kaydetmek, bu belirli aramanın günlüğe kaydedilip kaydedilmeyeceğini kontrol etmek için önce IQueryable'ü denetlemenizi sağlar.

Bu yaklaşımı kullanmamayı tavsiye ederim. Bunun yerine birkaç Spesifikasyon oluşturmayı düşünün ve bunları kullanarak veritabanını sorgulayabilen havuz yöntemleri uygulayın. Spesifikasyon modeli , bir dizi koşulu kapsüllemek için harika bir yoldur.


5

IQueryable'ü havuzunuzda sergilemenin, geliştirme sürecinin ilk aşamalarında kabul edilebilir olduğunu düşünüyorum. Doğrudan IQueryable ile çalışabilen ve sıralama, filtreleme, gruplama vb. İşlemlerini yapabilen UI araçları vardır.

Olduğu söyleniyor, bence sadece depoya azar azar atmak ve onu bir gün ilan etmek sorumsuzca. Yaygın sorgular için yararlı işlemlere ve sorgu yardımcılarına sahip olmak, bir sorgu mantığını merkezileştirirken, depodaki tüketicilere daha küçük bir arayüz yüzeyi de sunar.

Bir projenin ilk birkaç tekrarlaması için, IQueryable ürününü depoda herkese açık olarak göstermek, daha hızlı büyümeye olanak sağlar. İlk tam sürümden önce, sorgu işlemini özel veya korumalı hale getirip vahşi sorguları bir çatı altına almanızı öneririm.


4

Yaptıklarımı (ve kendimi uyguladıklarımı) şöyle:

public interface IEntity<TKey>
{
    TKey Id { get; set; }
}

public interface IRepository<TEntity, in TKey> where TEntity : IEntity<TKey>
{
    void Create(TEntity entity);
    TEntity Get(TKey key);
    IEnumerable<TEntity> GetAll();
    IEnumerable<TEntity> GetAll(Expression<Func<TEntity, bool>> expression);
    void Update(TEntity entity);
    void Delete(TKey id);
}

Bunun yapmanıza izin verdiği şey, herhangi bir LINQy işini ifşa etmeden IQueryable.Where(a => a.SomeFilter), repo üzerinde bir sorgulama yapma esnekliğine sahip concreteRepo.GetAll(a => a.SomeFilter)olmaktır.

Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.