Domanda

Sono attualmente la creazione di un nuovo progetto, e ho incontrato un paio di cose, in cui ho bisogno di un po 'di ingresso.

Questo è quello che sto considerando:

  • Vorrei un repository generico

  • Non voglio tornare IQueryable dal mio repository.

  • Vorrei incapsulare mie domande nelle specifiche.

  • Ho implementato il modello specifica

  • Ha bisogno di essere facilmente verificabile

Ora, questo è dove ho un po 'bloccato e la mia domanda è: quale modo potrebbe essere il modo più elegante di chiamare il metodo di ricerca con una o più specifiche:

(perfetto): bannerRepository.Find().IsAvailableForFrontend().IsSmallMediaBanner()

o esprimere interrogazioni come lambda con le mie caratteristiche

(Lambda): bannerRepository.Find.Where(banner => banner.IsFrontendCampaignBanner && banner.IsSmallMediaBanner)

o forse qualche altro modo del tutto? La cosa più importante è, che il ragazzo l'attuazione della parte anteriore MVC, dovrebbe avere una buona esperienza intuitiva del repository.

Quello che spero di raggiungere è quello di mantenere la flessibilità som per quanto riguarda la possibilità di coniugare le specifiche, e dare l'esperienza di "filtering" con i specfications, ma senza perdite di IQueryable al controller, ma più come un ISpecifiable, che permette solo di modificare la query con le specifiche e non con LINQ. Ma io sono appena tornato a perdite logica di query al controller in questo modo?

È stato utile?

Soluzione

Ho visto alcuni fluente API che utilizza le proprietà per le specifiche, in modo da non aggiungere il rumore parentesi ai clienti.

bannerRepository.Find.IsAvailableForFrontend.IsSmallMediaBanner.Exec()

Essere Exec () un metodo per l'esecuzione delle specifiche contro il pronti contro termine.

, ma anche se non si utilizzano le proprietà, vorrei andare per l'API fluente, in quanto ha la minima rumorosità.

Altri suggerimenti

  

o forse qualche altro modo completamente?

Beh, in realtà non ho ricevuto esattamente l'implementazione repository (ad esempio quale sarà il metodo .Find() tornare?), Ma avrei scelto un'altra direzione:

public class Foo 
{ 
    public Int32 Seed { get; set; }
}

public interface ISpecification<T> 
{
    bool IsSatisfiedBy(T item);
}

public interface IFooSpecification : ISpecification<Foo> 
{
    T Accept<T>(IFooSpecificationVisitor<T> visitor);
}

public class SeedGreaterThanSpecification : IFooSpecification
{
    public SeedGreaterThanSpecification(int threshold)
    {
        this.Threshold = threshold;
    }
    public Int32 Threshold { get; private set; }
    public bool IsSatisfiedBy(Foo item) 
    {
        return item.Seed > this.Threshold ;
    }
    public T Accept<T>(IFooSpecificationVisitor<T> visitor)
    {
        return visitor.Visit(this);
    }
}
public interface IFooSpecificationVisitor<T>
{
    T Visit(SeedGreaterThanSpecification acceptor);
    T Visit(SomeOtherKindOfSpecification acceptor);
    ...
}
public interface IFooRepository 
{
    IEnumerable<Foo> Select(IFooSpecification specification);
}
public interface ISqlFooSpecificationVisitor : IFooSpecificationVisitor<String> { }
public class SqlFooSpecificationVisitor : ISqlFooSpecificationVisitor
{
    public string Visit(SeedGreaterThanSpecification acceptor)
    {
        return "Seed > " + acceptor.Threshold.ToString();
    }
    ...
}
public class FooRepository
{   
    private ISqlFooSpecificationVisitor visitor;

    public FooRepository(ISqlFooSpecificationVisitor visitor)
    {
        this.visitor = visitor;
    }

    public IEnumerable<Foo> Select(IFooSpecification specification)
    {
        string sql = "SELECT * FROM Foo WHERE " + specification.Accept(this.visitor);
        return this.DoSelect(sql);
    }

    private IEnumerable<Foo> DoSelect(string sql)
    {
        //perform the actual selection;
    }
}

Così ho un'entità, la sua interfaccia specifica e diversi implementatori coinvolti in un modello di visitatore, la sua interfaccia repository accettare un'interfaccia specifica e la sua repository implementazione, accettando un visitatore in grado di tradurre le specifiche in clausole SQL (ma è solo una questione di questo caso, ovviamente). Infine, vorrei comporre specificazione "di fuori" l'interfaccia repository (tramite interfaccia fluida).

Forse questo è solo un idea ingenua, ma trovo abbastanza semplice. Spero che questo aiuti.

Personalmente vorrei andare con il modo in cui lambda. Può essere a causa del mio amore per lambda ma fornisce una sacco di spazio per una messa a punto repository generico.

In considerazione quanto segue:

bannerRepository.Find.Where(banner => banner.IsFrontendCampaignBanner && banner.IsSmallMediaBanner)

Non so che cosa il vostro modello assomiglia ma si potrebbe refactoring alcune cose qui:

Crea un'interfaccia generica chiamata 'IRepository' di tipo contenente tutti i metodi per l'accesso ai dati.

Si potrebbe assomigliare a questo:

interface IRepository<T> where T : class
{
    IEnumerable<T> FindAll(Func<T, bool> exp);

    T FindSingle(Func<T, bool> exp);
}   

Crea un 'repository' classe astratta attuare questa interfaccia:

class Repository<T> : IRepository<T> where T : class
{
    TestDataContext _dataContext = TestDataContext();

    public IEnumerable<T> FindAll(Func<T, bool> exp)
    {
        _dataContext.GetTable<T>().Where<T>(exp);
    }

    public T FindSingle(Func<T, bool> exp)
    {
        _dataContext.GetTable<T>().Single(exp);
    }
}

Ora possiamo creare un'interfaccia per la tabella banner / oggetti che implementa il nostro 'IRepository' e una classe concreta che estende la classe astratta 'Repository' e l'attuazione della 'IBannerInterface':

interface IBannerRepository : IRepository<Banner>
{
}

E il repository di corrispondenza per la sua attuazione:

class BannerRepository : Repository<Banner>, IBannerRepository
{
}

Io suggerirei di usare questo approccio in quanto ti dà un sacco di flessibilità, così come abbastanza potere di controllare tutti i piccoli enti che avete.

La chiamata quei metodi sarà super facile in questo modo:

BannerRepository _repo = new BannerRepository();

_repo.FindSingle(banner => banner.IsFrontendCampaignBanner && banner.IsSmallMediaBanner);

Sì, significa che si deve fare un certo lavoro, ma è un inferno più facile per voi di cambiare la fonte di dati in seguito.

Speranza che aiuta!

Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top