2009-02-08 29 views
9

Ürün listesi (giyim) içeren bir veritabanı tablom var. Ürünler kategorilere aittir ve farklı mağazalardan gelmektedir.sorgusu filtrelemek için hangi desen kullanır? C#

Numune kategoriler üstler dipleri, ayakkabı

Numune mağazaları: gap.com, macys.com, target.com

Müşterilerim aşağıdaki şekillerde ürünlere filtre isteyebilir:

  • tüm ürünlerin (filtresiz) mağaza tarafından kategoriye göre
  • göre kategori ve mağaza

Şu anda, "Ürünler" sınıfımda, kullanıcının istediği filtrenin türüne bağlı olarak ürünleri döndüren BİR yöntem var. Hangi ürünlerin iade edilmesi gerektiğini belirlemek için FilterBy enum kullanıyorum. o "mağaza" içerir dize çünkü ben boş geçen parametresine sahip

Products.GetProducts(FilterBy.Category, "tops", ""); 

: Kullanıcı "üstleri" kategorisinde tüm ürünleri görmek istiyorsa

Örneğin, ben bu işlevi çağırmak filtresine göre ancak bu durumda mağaza yok.

Product.GetProducts(FilterBy.CategoryAndStore, "tops", "macys.com"); 

Sorum şu, bunu yapmak için daha iyi bir yolu nedir: Kullanıcı kategorisinde VE mağazaya göre filtreleme istiyorsa Ancak, bu yolu yöntemi derim? Az önce strateji tasarım modelini öğrendim. Bunu daha iyi (uzatmak ve bakımı daha kolay) bir şekilde yapmak için kullanabilir miyim? Bu insanlar defalarca strateji desen mutlaka iyi örme vermez

+0

[Örnek ile Filtre Tasarımı Desen] (http://www.singhajit.com/filter-design-pattern/) –

cevap

13

Eric Evan'un "Domain Drive Design" 'a göre, belirtim desenine ihtiyacınız var. Böyle bir şey

public interface ISpecification<T> 
{ 
    bool Matches(T instance); 
    string GetSql(); 
} 

public class ProductCategoryNameSpecification : ISpecification<Product> 
{ 
    readonly string CategoryName; 
    public ProductCategoryNameSpecification(string categoryName) 
    { 
    CategoryName = categoryName; 
    } 

    public bool Matches(Product instance) 
    { 
    return instance.Category.Name == CategoryName; 
    } 

    public string GetSql() 
    { 
    return "CategoryName like '" + { escaped CategoryName } + "'"; 
    } 
} 

Kişisel depo şimdi de mantıksal operatör bunlara uygulamak üzere alt şartname ve bir gösterge içerecektir genel CompositeSpecification sınıf oluşturabilir

var specifications = new List<ISpecification<Product>>(); 
specifications.Add(
new ProductCategoryNameSpecification("Tops")); 
specifications.Add(
new ProductColorSpecification("Blue")); 

var products = ProductRepository.GetBySpecifications(specifications); 

özellikleri ile çağrılabilir VE/OR

Yine de LINQ ifadelerini birleştirmeye daha yatkın olurdum.

Güncelleme - Son olarak bir yüklemi içine bu ifadeyi dönüştürebilir ve sonra Çalıştır bir "ve"

var colorExpression = Expression.Equal( Expression.Property(product, "Color"), Expression.Constant("Red")); var andExpression = Expression.And(categoryNameExpression, colorExpression); 

şöyle zamanında

var product = Expression.Parameter(typeof(Product), "product"); 
var categoryNameExpression = Expression.Equal(
    Expression.Property(product, "CategoryName"), 
    Expression.Constant("Tops")); 

de LINQ örneği ekleyebilir. ..

var predicate = 
    (Func<Product, bool>)Expression.Lambda(andExpression, product).Compile(); 
var query = Enumerable.Where(YourDataContext.Products, predicate); 

foreach(Product currentProduct in query) 
    meh(currentProduct); 

Muhtemelen derlemem çünkü ben doğrudan tarayıcıya yazdım, ancak genelde doğru olduğuna inanıyorum.

Diğer bir güncelleme kaynağı bir Liste olduğu için bellekte değerlendirirken olacağını Bu durumda :-)

List<Product> products = new List<Product>(); 
products.Add(new Product { CategoryName = "Tops", Color = "Red" }); 
products.Add(new Product { CategoryName = "Tops", Color = "Gree" }); 
products.Add(new Product { CategoryName = "Trousers", Color = "Red" }); 
var query = (IEnumerable<Product>)products; 
query = query.Where(p => p.CategoryName == "Tops"); 
query = query.Where(p => p.Color == "Red"); 
foreach (Product p in query) 
    Console.WriteLine(p.CategoryName + "/" + p.Color); 
Console.ReadLine(); 

ama kaynak bence örneğin Linq2SQL desteklenen bir veri bağlamı olsaydı bu, SQL kullanarak değerlendirir.

Kavramlarınızı açık hale getirmek için Spesifikasyon modelini kullanmaya devam edebilirsiniz.

public class Specification<T> 
{ 
    IEnumerable<T> AppendToQuery(IEnumerable<T> query); 
} 

iki yaklaşım arasındaki temel fark, (örneğin XML tamamen bir sorgu yapı olarak ilk bir yapının bir sorgu oluşturmak için kullanılabilir, oysa ikinci, açık özelliklerine göre bilinen bir sorgu inşa edilmiş olmasıdır örneğin.)

Bu burada gitmeden olarak nerede şeyler sadece eklenemez :-)

+0

Bana LINQ ifadelerini birleştirmenin bir örneğini gösterebilir misiniz? –

+0

Size bir örnek ekledik, bir göz atın ve nasıl kaldığınızı görün. –

2

(çeşitli şekillerde filtreleme ürünleri) çözme oldukça yaygın bir sorun olmalıdır şekil çünkü

Bu soruyu soruyorum nedenidir ortak arayüz tabanlı depo yaklaşımı. Şahsen, muhtemelen giderdim burada iki yoldan biri:

  • seçeneklerin kombinasyonlarını destekler

    Bir arama yöntemi:

    IList<Product> GetProducts(string category, string store, ...);

(daha sonra seçici olarak kombinasyonları arasında uygulamak filtreler (yani null "herhangi bir") - bir komut oluştururken ya da benzer bir şey yapan bir SPROC uygulamasına geçmek için aşağı doğru ilerler.

  • LINQ ile, belki de bir öntanımlı ifade?

    IList<Product> GetProducts(Expression<Func<Product,bool>> predicate);

ayrıca arayan tarafından kompozisyonu kullanabilirsiniz LINQ ile Tabii

, fakat bunun için kapalı bir/tam test deposunu yazmak için zordur:

`IQueryable<Product> Products {get;}` 

(ve Arayanın kullanımı var .Where (x => x.Category == "foo")) - Bu son uzun vadeli hakkında çok emin değilim ...

+0

yüklem formu tam olarak ne olduğunu ben Gönderdim. Çok esnek ve lambda ifadeleriyle, başka herhangi bir yol gibi özlü. –

+0

Gerçekten de; ve Expression <...> olarak nesne tabanlı depolarla kullanılmak üzere derlenebilir. Dezavantajı ise, desteklenmeyen sorgulama işlemlerinde LOLA riski hala var ... –

+0

In * teorisi * IQueryable (dönüş) seçeneğini beğenirim, ancak DAL'yi UI'den ayırmayı zorlaştırır (örneğin, veri erişimi olduğunda garanti başlar ve biter) - ama yine de bazı secnarios için geçerli bir seçenek. –

0

Bu küçük bilgime dayanarak bunu yanıtlıyorum desen

Dekoratör desen yerine, (. Eğer sonuç almak & bir filtre ekleyebilirsiniz dikkate yeni sonuçlar elde & üzerinde yeni filtreler uygulayın) burada yardımcı Ben Kategori sınıf ve bir Mağaza sınıfını olursun

1

olabilir sadece dizeleri:

class Category 
{ 
    public Category(string s) 
    { 
    ... 
    } 
    ... 
} 

ve sonra belki:

Product.GetProducts(
    Category category, //if this is null then don't filter on category 
    Store store //if this is null then don't filter on store 
) 
{ 
    ... 
} 

Category ve Store sınıfları ilişkili olabilir (her ikisi de Filter sınıfının alt sınıfları olabilir).

0

Ben filtreler için bir strateji kendileri gibi bir şey ile gitmek ve CategoryFilter ve StoreFilter sınıflar yazardı. Sonra filtreleri birleştirmek için bir kompozit veya dekoratör kullanırdım.

0

başlamak için yeterli olmalıdır? Böyle

var products = datacontext.Products; 

if(!String.IsNullOrEmpty(type)) 
    products = products.Where(p => p.Type == type); 

if(!String.IsNullOrEmpty(store)) 
    products = products.Where(p => p.Store == store); 

foreach(var p in products) 
    // Do whatever 

falan ...

İlgili konular