Pregunta

Tratando de hacer una muy simple patrón de repositorio y capa de servicio aquí. (4 .NET, C #, LINQ, aunque esta cuestión es parcialmente independiente del idioma). Nota:. Esto es sólo de I + D

Mi objetivo es reducir al mínimo la cantidad de definiciones de métodos en mi capa de servicio.

Aquí está mi contrato Repositorio:

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

Nada nuevo.

Ahora, esto es lo que im (intentar) para tener en mi contrato de servicio:

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

En esencia, cualquier particular, "Foo" tiene muchas propiedades (id, nombre, etc.), lo que me gustaría ser capaz de buscar al.

Por lo tanto, no quiero tener 1x Encuentra método para cada propiedad diferente, sólo quiero uno -. De esa manera cuando creo propiedades adicionales que no tengo que modificar los contratos

Los "FooSearchArgs" es sólo un POCO sencilla con todas las diferentes propiedades de "Foo" a él.

Por lo tanto, eso es lo que estoy tratando de hacer, aquí están mis preguntas:

  • ¿Es este un mal diseño? Si es así, ¿cuáles son las alternativas?
  • ¿Cómo puedo aplicar este filtrado en la capa de servicio? ¿Tendría que comprobar qué propiedades de "FooSearchArgs" se establecen, a continuación, seguir filtrando hacia abajo? (Si pasa esto, query.where, si esto, query.where, etc) Alguien tiene una idea de un método de extensión de LINQ IEnumerable inteligente para hacer esto? (Es decir repository.WhereMeetsSearchCriteria(fooSearchArgs))

Apreciar la ayuda.

¿Fue útil?

Solución

Utilizamos algo muy similar. Una cosa que hay que decidir es si se va a exponer fuera IQueryable del repositorio. Su método de búsqueda devuelve IEnumerable que podría ser el IQueryable regresó de su cláusula cuando.

La ventaja de devolver el IQueryable es que se puede perfeccionar sus criterios arriba fuera de su capa de repositorio.

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

La expresión sólo se compilará cuando se llega a utilizar los datos devueltos y aquí en mentiras la desventaja. Debido a que sólo llegar a la base de datos cuando en realidad se llega a utilizar los resultados que podría terminar tratando de llamar a la base de datos después de su sesión (nhibernate) o las conexiones han sido cerradas.

Mi preferencia personal es usar el patrón de especificación en la que se pasa el método de encontrar un objeto ISpecification se utiliza para hacer la consulta.

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();
    }
}

Después de que se ejecute la consulta de las llamadas repositorio toArray o ToList en la IQueryable resultante volvió de la especificación para que la consulta se evalúa en el acto. Si bien esto puede parecer menos flexible que la exposición de IQueryable viene con varias ventajas.

  1. Las consultas se ejecutan de inmediato y evita una llamada a la base de datos se hizo después de las sesiones se han cerrado.
  2. Debido a sus consultas están agrupados en las especificaciones que son la unidad comprobable.
  3. Las especificaciones son reutilizables significado que no tiene la duplicación de código cuando se trata de ejecutar consultas similares y cualquier error en las consultas sólo tienen que fijarse en un solo lugar.
  4. Con el tipo correcto de la aplicación también se puede encadenar sus especificaciones juntos.

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

Otros consejos

Está pasando una Func como un parámetro a su capa de servicio Encuentra método, en lugar de los FooSearchArgs, una opción? Enumerables tienen un método Dónde (LINQ) que toma un Func como un parámetro, por lo que se podría utilizar para filtrar los resultados.

Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top