GithubHelp home page GithubHelp logo

Comments (6)

fiseni avatar fiseni commented on June 3, 2024

Hi @gabrielheming,

We already support projections in the specs, you have to inherit from Specification<T, TResult>.
If you want to have a single specification per entity, and reuse the same spec for arbitrary projections, then you may want to check our sample apps. More specifically the sample3. In this sample, we're combining specs with Automapper for projections, and the usage becomes super simple. Of course, you can use other mapping libraries too.

// Projecting directly to response DTOs. In addition the response is paginated and wrapped in PagedResponse<T>.
app.MapGet("/customers", async (IReadRepository<Customer> repo,
                                [AsParameters] CustomerFilter filter,
                                CancellationToken cancellationToken) =>
{
    var spec = new CustomerSpec(filter);
    var result = await repo.ProjectToListAsync<CustomerDto>(spec, filter, cancellationToken);
    return Results.Ok(result);
});

from specification.

fiseni avatar fiseni commented on June 3, 2024

Btw, if you prefer doing the mapping manually, then you may create a spec extension for common criteria and create projection specification per result type. So, in this case, the projection specs will act as mappers (since you're doing it manually).

public static class CustomerSpecExtensions
{
    public static ISpecificationBuilder<Customer> ApplyCommon(this ISpecificationBuilder<Customer> builder)
    {
        // Apply your common queries for Customer here
        builder.OrderBy(x => x.Name);
        return builder;
    }
}

public class CustomerDtoSpec : Specification<Customer, CustomerDto>
{
    public CustomerDtoSpec()
    {
        Query.ApplyCommon();
        Query.Select(x => new CustomerDto(
            x.Id, 
            x.Name, 
            x.Age, 
            x.Addresses.Select(a => new AddressDto(a.Id, a.Street, a.CustomerId)).ToList()));
    }
}

from specification.

gabrielheming avatar gabrielheming commented on June 3, 2024

Hi @fiseni, thank you for your feedback.

We already support projections in the specs, you have to inherit from Specification<T, TResult>.

I thought it was clear that I was already using it, and it led to the creation of the ToProjection extension method solution to solve (through composition) the inheritance issue. However, I have to admit that I tend to go naturally to a composition approach when a virtual member is called in a constructor. On the other hand, mapping to a common projection with different criteria will lead to duplication. For instance: 2 different Specs but projects the same output.

My point is a similar approach to your example from sample3 without having to rely on an external library and using only the Specification library. That is the general question that I will rephrase: Do you think it might be useful for the community as a implementation or do you rather not have such implementation on the package?

Your example with ApplyCommon keeps the inheritance problem that sample3 solves.

On my take, I didn't want to add more methods on the already existing IReadRepository<T> and IRepository<T>. Though I will check the approach from sample3 to have a check on performance. I am testing part of my implementation using source generator to check any improvement, and I will see the results.

Thanks again for your feedback, it gave me more approaches to check on.

from specification.

fiseni avatar fiseni commented on June 3, 2024

@gabrielheming @ardalis
Yes, I do understand your points. But, adding another type IProjectionSpecification<T, TResult> might not be the best way to move forward. We try to minimize the number of types that the library exposes. Also, we already have a construct ISpecification<T, TResult> that represents the same thing. So, we should find a way how to integrate that one into this usage. Roughly, I'm thinking in this direction

ISpecification<Customer> spec = new CustomerSpec();
ISpecification<Customer, CustomerDto> projectionSpec = new CustomerDtoSpec();

// Get a new spec by combining the state from the base spec.
var newSpec = projectionSpec.Combine(spec);

If we put it this way, then it's clear that we're trying to implement composite specifications. There have been thorough discussions on this topic, about the pros and cons of the composite specs. So, if we want to implement this feature then it should be done a bit more carefully and considering the broader scope, not just this specific case. Perhaps it's time we re-evaluate composite specs and the best way to move forward in that regard.

from specification.

gabrielheming avatar gabrielheming commented on June 3, 2024

@fiseni

we already have a construct ISpecification<T, TResult> that represents the same thing

Perhaps it's time we re-evaluate composite specs and the best way to move forward in that regard.

True, and I agree with you.

On my scenario, I've used a new type to take advantage of source generator as it specifies that the direction will be from ISpecification<T> to IProjectionSpecification<T, TResult> resulting an ISpecification<T, TResult>, without generating code for something that is not going to be use. Though it could probably be done using an attribute and possibly some configuration.

Nevertheless, I would argue that the specification composition should be strict for the criteria, while projection an extension on top of the specification.

Furthermore, I agree with you that discussing composition in details could be the proper approach.

from specification.

fiseni avatar fiseni commented on June 3, 2024

Ok, we'll plan this. But, before starting this work, we need substantial refactoring of the internals. It's something I've been planning for quite some time. Only then, we can start implementing this feature.

from specification.

Related Issues (20)

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.