Domanda

Cercando di fare davvero un semplice repository e livello di servizio modello qui. (.NET 4, C #, LINQ, anche se questa domanda è in parte indipendente dal linguaggio). Nota:. Questo è solo R & S

Il mio obiettivo è quello di ridurre al minimo la quantità di definizioni di metodo nel mio livello di servizio.

Ecco il mio Repository contratto:

interface IFooRepository
{
   IEnumerable<Foo> Find();
   void Insert(Foo foo);
   void Update(Foo foo);
   void Delete(Foo foo);
}

Niente di nuovo.

Ora, ecco cosa im (cercando) di avere nel mio Contratto di servizio:

interface IFooDataService
{
   public IEnumerable<Foo> Find(FooSearchArgs searchArgs);
}

In sostanza, una particolare "Pippo" ha molte proprietà (ID, nome, ecc), che mi piacerebbe essere in grado di cercare su.

Quindi, io non voglio avere 1x Trova metodo per ogni proprietà diversa, voglio solo uno -. In questo modo quando creo proprietà aggiuntive non ho di modificare i contratti

I "FooSearchArgs" è solo un POCO semplice con tutte le diverse proprietà "foo" di esso.

Quindi, questo è quello che im cercando di fare, ecco le mie domande:

  • È questo disegno difficile? Se sì, quali sono le alternative?
  • Come posso implementare questo filtraggio nel livello di servizio? Avrei dovuto controllare quali vengono impostate le proprietà di "FooSearchArgs", allora continuate a filtrare verso il basso? (Se questo, poi query.where, se questo, query.where, ecc) Qualcuno ha un'idea di un metodo di estensione LINQ IEnumerable intelligente per fare questo? (Cioè repository.WhereMeetsSearchCriteria(fooSearchArgs))

Apprezzare l'aiuto.

È stato utile?

Soluzione

Usiamo qualcosa di molto simile. Una cosa che devi decidere è se avete intenzione di esporre al di fuori IQueryable del repository. Il vostro metodo find restituisce IEnumerable che potrebbe essere l'IQueryable restituito dalla clausola WHEN.

Il vantaggio di restituzione del IQueryable è che si può ulteriormente perfezionare i criteri al di fuori del vostro livello repository.

repository.Find(predicate).Where(x => x.SomeValue == 1);

L'espressione verrà compilato solo quando si arriva a utilizzare i dati restituiti e qui in bugie svantaggio. Perché si colpisce solo il database quando effettivamente arriva a utilizzare i risultati si potrebbe finire cercando di chiamare il database dopo la sessione (NHibernate) o le connessioni sono stati chiusi.

La mia preferenza personale è quello di utilizzare il modello di specifica in cui si passa il metodo di trovare un oggetto ISpecification viene usato per fare la query.

public interface ISpecification<TCandidate>
{
    IQueryable<TCandidate> GetSatisfyingElements(IQueryable<TCandidate> source);
}

public class TestSpecification : ISpecification<TestEntity>
{
    public IQueryable<TestEntity> GetSatisfyingElements(IQueryable<TestEntity> source)
    {
        return source.Where(x => x.SomeValue == 2);
    }
}

public class ActiveRecordFooRepository: IFooRepository
{
    ...

    public IEnumerable<TEntity> Find<TEntity>(ISpecification<TEntity> specification) where TEntity : class 
    {
        ...

        return specification.GetSatisfyingElements(ActiveRecordLinq.AsQueryable<TEntity>()).ToArray();

        ...
    }

    public TEntity FindFirst<TEntity>(ISpecification<TEntity> specification) where TEntity : class 
    {
        return specification.GetSatisfyingElements(ActiveRecordLinq.AsQueryable<TEntity>()).First();
    }
}

Dopo che si esegue la query le chiamate repository ToArray o ToList sul risultante IQueryable restituito dalla specifica in modo che la query viene valutata e poi. Anche se questo può sembrare meno flessibile di esporre IQueryable si tratta con diversi vantaggi.

  1. Le query vengono eseguiti immediatamente e impedisce una chiamata al database stato fatto dopo le sessioni hanno chiuso.
  2. Poiché le vostre domande sono ora raggruppati in specifiche sono unità testabili.
  3. Le specifiche sono riutilizzabili significato non si dispone di duplicazione del codice quando si tenta di eseguire query simili e eventuali bug nelle query solo bisogno di essere fissato in un unico luogo.
  4. Con il giusto tipo di applicazione è anche possibile concatenare le vostre specifiche insieme.

repository.Find(
    firstSpecification
        .And(secondSpecification)
        .Or(thirdSpecification)
        .OrderBy(orderBySpecification));

Altri suggerimenti

Func come parametro per le vostre del livello di servizio Trova metodo, invece dei FooSearchArgs, un'opzione? Enumerables hanno un metodo di Dove (LINQ) che prende un Func come parametro, quindi si potrebbe utilizzare per filtrare i risultati.

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