SORU
20 Ocak 2013, Pazar


İyi tasarlanmış sorgu komutları ve/veya teknik özellikleri

Sorunları tipik Depo desen (uzman sorguları için yöntemler büyüyen listesi vb..: http://ayende.com/blog/3955/repository-is-the-new-singleton) tarafından sunulan iyi bir çözüm için oldukça uzun bir süre için arama oldum.

Gerçekten Komutunu kullanarak sorgular fikri, özellikle Belirtim desen kullanarak gibi. Ancak, bu benim sorunum. o sadece ilgili ölçütlerin basit seçimler (temelde, burada fıkra), ve değil anlaşma ile diğer konularda sorguları, gibi birleştirme, gruplama, alt seçim veya projeksiyon, vb. temelde, tüm ekstra çemberler çok sayıda sorgu gerekir geçmesi için doğru ayarlanmış veri.

(not: terim "" Komut desen de sorgu nesneleri olarak da bilinir. komutu kullanın Bir ayrım sorguları ve komutları (update, delete, ınsert)) arasında yapılmış olduğu komut/sorgu ayrılık olarak komut bahsetmiyorum

Tüm sorgu edilmiş alternatifler arıyorlar, ama hala sadece komut sınıfları bir patlama için spagetti Depoları takas değilsin o kadar esnek değilim.

Örneğin Linqspecs kullandım ve seçim kriterleri için anlamlı isimler atamak için güçlü olmak bazı değer bulsam da, sadece yeterli değil. Belki de birden fazla yaklaşımlar birleştiren harmanlanmış bir çözüm arıyorum.

Diğerleri de bu sorunu çözmek, ya da farklı bir sorunu çözmek için gelişmiş olabilir çözümler arıyorum ama hala bu gereksinimleri karşılar. Bağlantılı makalede, Ayende doğrudan arabiriminin bağlamı kullanarak önerir, ama şimdi de sorgu bilgilerin yer alması gerekiyor, çünkü büyük ölçüde iş katmanı zorlaştırmaktadır hissediyorum.

Bu ödül, bekleme süresi sona erdiğinde en kısa sürede sunacağım. Lütfen çözümlerinizi ödül layık, iyi açıklamalar yapmak ve en iyi çözümü seçin ve koşucular kadar upvote.

NOT: ORM göre bir şey arıyorum. EF veya açıkça arabiriminin değil, ama bu en yaygın ve en iyi uyabilecek. Eğer kolayca diğer ORM ... ... adapte edilebilir eğer bir bonus olur. Seri uyumlu da güzel olurdu.

GÜNCELLEME: gerçekten çok iyi bir öneri yok şaşırdım. İnsanlar tamamen CQRS ya gibi görünüyor, ya da tamamen Depo kamp yapıyorlar. Uygulamalarım en CQRS (en CQRS savunucuları için kullanması gereken kolay bir şey söyle) emri için yeterince karmaşık değildir.

GÜNCELLEME: ufak bir karışıklık Var burada. Yeni bir veri erişim teknolojisi, daha ziyade iş ve veri arasında oldukça iyi tasarlanmış bir arayüz arıyorum.

Aradığım ideal, Sorgu nesneleri, özellikleri desen, ve depo arasında çapraz bir tür. Dediğim gibi yukarıda, Teknik desen sadece fırsatlar ile where yönü, diğer yönleri sorgu gibi katılır, alt-seçer, vb. Depoları anlaşma ile tüm sorgu, ama dışarı El bir süre sonra. Sorgu nesneleri aynı zamanda tüm Sorgu ile başa çıkmak, ama sadece sorgu nesneleri patlamalar ile depoları değiştirmek istemiyorum.

CEVAP
25 Ocak 2013, Cuma


Yasal Uyarı:Herhangi bir cevap henüz olmadığına göre, bir süre önce, neredeyse birebir kopyalanmış okudum harika bir blog yazısı bir kısmını göndermeye karar verdim. Tam blog yazısı here bulabilirsiniz. Öyle işte:


Aşağıdaki iki arabirim tanımlayabiliriz:

public interface IQuery<TResult>
{
}

public interface IQueryHandler<TQuery, TResult> where TQuery : IQuery<TResult>
{
    TResult Handle(TQuery query);
}

IQuery<TResult> TResult genel tür kullanarak döndürdüğü veri ile belirli bir sorgu tanımlayan bir ileti belirtir. Önceden tanımlı arayüzü ile böyle bir sorgu mesajı tanımlayabiliriz:

public class FindUsersBySearchTextQuery : IQuery<User[]>
{
    public string SearchText { get; set; }
    public bool IncludeInactiveUsers { get; set; }
}

Bu sınıf User nesneleri bir dizi neden olacaktır, iki parametre ile sorgu işlemi tanımlar. Bu iletiyi işleyen sınıfı aşağıdaki gibi tanımlanabilir

public class FindUsersBySearchTextQueryHandler
    : IQueryHandler<FindUsersBySearchTextQuery, User[]>
{
    private readonly NorthwindUnitOfWork db;

    public FindUsersBySearchTextQueryHandler(NorthwindUnitOfWork db)
    {
        this.db = db;
    }

    public User[] Handle(FindUsersBySearchTextQuery query)
    {
        return db.Users.Where(x => x.Name.Contains(query.SearchText)).ToArray();
    }
}

Artık tüketiciler jenerik bağlı izin verebiliriz IQueryHandler arayüz:

public class UserController : Controller
{
    IQueryHandler<FindUsersBySearchTextQuery, User[]> findUsersBySearchTextHandler;

    public UserController(
        IQueryHandler<FindUsersBySearchTextQuery, User[]> findUsersBySearchTextHandler)
    {
        this.findUsersBySearchTextHandler = findUsersBySearchTextHandler;
    }

    public View SearchUsers(string searchString)
    {
        var query = new FindUsersBySearchTextQuery
        {
            SearchText = searchString,
            IncludeInactiveUsers = false
        };

        User[] users = this.findUsersBySearchTextHandler.Handle(query);    
        return View(users);
    }
}

Hemen bu model şimdi UserController içine enjekte etmek için ne karar veririz çünkü bize esneklik veriyor. Gerçek uygulama sarar bu tamamen farklı bir uygulama ya da bir, UserController (ve bu arabirim diğer tüm tüketiciler) değişiklik yapmak zorunda kalmadan enjekte edebiliriz.

IQuery<TResult> arabirimi bizim veya Kodu IQueryHandlers belirtme enjekte zamanı derleme destek verir. Ne zaman değişmesi FindUsersBySearchTextQuery dönmek UserInfo[] yerine (tarafından uygulanması IQuery<UserInfo[]>) UserController başarısız derleme, bu yana Genel tür kısıtlaması IQueryHandler<TQuery, TResult> mümkün olmayacaktır göster FindUsersBySearchTextQuery User[].

Bir tüketici ancak, IQueryHandler arabirim enjekte etti hala çözülmesi gereken bazı sorunlar daha az belirgin. Tüketicilerimizin bağımlılıkları sayısı çok büyük ve yapıcı çok fazla argüman alır-enjeksiyon - yapıcı yol açabilir. Bir sınıf yürütür sorgu sayısı oluşturucu bağımsız değişkenleri içine sürekli değişiklikler gerektirecek sık sık değiştirebilirsiniz.

Soyutlama fazladan bir katman ile 27* *çok fazla enjeksiyon sayesinde bu sorunu düzeltebiliriz. Tüketiciler ve sorgu işleyicileri arasında oturur bir arabulucu oluşturun:

public interface IQueryProcessor
{
    TResult Process<TResult>(IQuery<TResult> query);
}

IQueryProcessor bir genel yöntem ile genel olmayan bir arayüz. Arayüz tanımında gördüğünüz gibi IQueryProcessor IQuery<TResult> arabirim bağlıdır. Bu bize IQueryProcessor bağımlı tüketicilerimizin derleme zamanı desteği sağlar. Hadi IQueryProcessor yeni UserController yeniden yazmak:

public class UserController : Controller
{
    private IQueryProcessor queryProcessor;

    public UserController(IQueryProcessor queryProcessor)
    {
        this.queryProcessor = queryProcessor;
    }

    public View SearchUsers(string searchString)
    {
        var query = new FindUsersBySearchTextQuery
        {
            SearchText = searchString,
            IncludeInactiveUsers = false
        };

        // Note how we omit the generic type argument,
        // but still have type safety.
        User[] users = this.queryProcessor.Process(query);

        return this.View(users);
    }
}

UserController şimdi bizim tüm sorgular kullanabilen IQueryProcessor bağlıdır. UserController'SearchUsers s yöntemi IQueryProcessor.Process yöntemi başlatıldı sorgu nesnesi iletir. FindUsersBySearchTextQuery uygular beri IQuery<User[]> arayüzü Execute<TResult>(IQuery<TResult> query) genel yöntem için geçirebiliriz. Tür kesmesi, derleyici genel türünü belirlemek mümkün değildir ve bu bize açıkça türü devlet zorunda kaydeder. teşekkürler C# Process yöntemin dönüş türü bilinir.

Şimdi IQueryProcessor uygulama sorumluluğu hakkı IQueryHandler. Bu dinamik yazarak, ve isteğe bağlı olarak Bağımlılık Enjeksiyon çerçeve kullanılması gerekir, ve kod sadece birkaç satır ile yapılabilir

sealed class QueryProcessor : IQueryProcessor
{
    private readonly Container container;

    public QueryProcessor(Container container)
    {
        this.container = container;
    }

    [DebuggerStepThrough]
    public TResult Process<TResult>(IQuery<TResult> query)
    {
        var handlerType = typeof(IQueryHandler<,>)
            .MakeGenericType(query.GetType(), typeof(TResult));

        dynamic handler = container.GetInstance(handlerType);

        return handler.Handle((dynamic)query);
    }
}

QueryProcessor sınıfı tarafından sağlanan sorgu örneği Türüne göre IQueryHandler<TQuery, TResult> belirli bir türü oluşturur. Bu tür verilen konteyner sınıf türünün bir örneği elde etmek için kullanılır. Ne yazık ki aramamız gerekiyor Handle yöntemi kullanılarak yansıma (kullanarak C# 4.0 dymamic anahtar kelime bu durumda), çünkü bu noktada mümkün değildir döküm işleyicisi örneği bu yana Genel TQuery tartışma değil mevcut derleme zamanında. Handle yöntemi yeniden adlandırıldı veya diğer bağımsız değişkenleri alır sürece ancak, bu çağrı asla başarısız olur ve eğer isterseniz, çok kolay bu sınıf için bir birim test yazmak için. Yansıma kullanarak hafif bir düşüş verecektir, ama gerçekten endişelenecek bir şey değildir.


Endişelerinizi bir cevap vereyim

Tüm sorgu edilmiş alternatifler arıyorum ama hala sadece spagetti takas olmadığını yeterince esnek Komut sınıfları bir patlama için depoları.

Bu tasarımı kullanarak bir sonucu sistemde küçük sınıfların bir sürü olacak, ama küçük/odaklanmış sınıflar (açık adları ile) çok olması iyi bir şeydir. Bu yaklaşım, bir sorgu sınıfta grup olarak açıkça çok iyi o zaman bir depo içinde aynı yöntem için farklı parametreler ile pek çok aşırı yükleme yapıyor. Hala bir depo yöntemlerden daha az sorgu sınıfları bir sürü olsun.

Bunu Paylaş:
  • Google+
  • E-Posta
Etiketler:

YORUMLAR

SPONSOR VİDEO

Rastgele Yazarlar

  • Grace Su

    Grace Su

    6 Ocak 2006
  • HER0R

    HER0R

    16 Aralık 2007
  • whatever

    whatever

    30 EYLÜL 2005