Selecting Projections

Once you have a minimal path to the results you want, you can select a projection from that point. Create an anonymous object after select containing all of the information you need. This can include nested specifications.

Continuing from the cart example, let's look at the details that we want to include. First, let's get the name of the customer. The model looks like this:

%0 Customer.Name Customer.Name Customer.Name->Customer.Name prior Customer Customer Customer.Name->Customer customer Environment Environment Customer->Environment environment Jinaga.User Jinaga.User Environment->Jinaga.User creator

Within our projection, we'll add a property to get the customer names.

var cartsInEnvironmentForUser = Given<Environment, User>.Match((environment, user, facts) =>
    from customerBuyer in facts.OfType<CustomerBuyer>()
    where customerBuyer.buyer == user
    from customer in facts.OfType<Customer>()
    where customerBuyer.customer == customer
    where customer.environment == environment
    from cart in facts.OfType<Cart>()
    where cart.customer == customer
    select new
    {
        Cart = cart,
        CustomerNames =
            from customerName in facts.OfType<CustomerName>()
            where customerName.customer == customer
            where !facts.Any<CustomerName>(next => next.prior.Contains(customerName))
            select customerName.value
    });

The nested specification uses !facts.Any<T> to filter out customer names that have been superseded. This ensures that we only see the current customer names. We will talk about conditions in a bit more detail later.

The nested specification also selects a field of the customerName fact. We want to display the name, so we will need its value. Fields can appear in projections. If you want to select another fact, however, you will instead need to label it and include it in the path.

Let's continue. We also want the cart items. Add this to the projection.

var cartsInEnvironmentForUser = Given<Environment, User>.Match((environment, user, facts) =>
    from customerBuyer in facts.OfType<CustomerBuyer>()
    where customerBuyer.buyer == user
    from customer in facts.OfType<Customer>()
    where customerBuyer.customer == customer
    where customer.environment == environment
    from cart in facts.OfType<Cart>()
    where cart.customer == customer
    select new
    {
        Cart = cart,
        CustomerNames =
            from customerName in facts.OfType<CustomerName>()
            where customerName.customer == customer
            where !facts.Any<CustomerName>(next => next.prior.Contains(customerName))
            select customerName.value,
        Items =
            from cartItem in facts.OfType<CartItem>()
            where cartItem.cart == cart
            select cartItem
    });

It's not just the item that we want. We also want to display the product name and quantity. Here's the part of the model that deals with those properties:

%0 Cart.Item Cart.Item Cart Cart Cart.Item->Cart cart Product Product Cart.Item->Product product Customer Customer Cart->Customer customer Month Month Cart->Month month Environment Environment Product->Environment environment Product.Name Product.Name Product.Name->Product product Product.Name->Product.Name prior Cart.Item.Quantity Cart.Item.Quantity Cart.Item.Quantity->Cart.Item item Cart.Item.Quantity->Cart.Item.Quantity prior Customer->Environment environment Month->Environment environment Jinaga.User Jinaga.User Environment->Jinaga.User creator

From this graph, we can draw another pair of paths. One path leads us from Cart.Item through Product to Product.Name. The other path leads us from Cart.Item directly to Cart.Item.Quantity. Add these two paths to the nested projection.

var cartsInEnvironmentForUser = Given<Environment, User>.Match((environment, user, facts) =>
    from customerBuyer in facts.OfType<CustomerBuyer>()
    where customerBuyer.buyer == user
    from customer in facts.OfType<Customer>()
    where customerBuyer.customer == customer
    where customer.environment == environment
    from cart in facts.OfType<Cart>()
    where cart.customer == customer
    select new
    {
        Cart = cart,
        CustomerNames =
            from customerName in facts.OfType<CustomerName>()
            where customerName.customer == customer
            where !facts.Any<CustomerName>(next => next.prior.Contains(customerName))
            select customerName.value,
        Items =
            from cartItem in facts.OfType<CartItem>()
            where cartItem.cart == cart
            select new
            {
                CartItem = cartItem,
                ProductNames =
                    from productName in facts.OfType<ProductName>()
                    where productName.product == cartItem.product
                    where !facts.Any<ProductName>(next => next.prior.Contains(productName))
                    select productName.value,
                Quantities =
                    from quantity in facts.OfType<CartItemQuantity>()
                    where quantity.item == cartItem
                    select quantity
            }
    });

These nested projections give us all the information we need about the cart and its items. Carefully construct each path within the projections to select exactly the results you want.

Continue With

Encapsulating Specifications

Jinaga is a product of Jinaga LLC.

Michael L Perry, President