In most Jinaga.NET documentation, I use the LINQ query expression syntax.
This features keywords like from
, where
, and select
.
Some developers prefer the LINQ method syntax.
This syntax uses methods like Where
, Select
, and SelectMany
with lambda expressions.
Jinaga specifications work equally well in either format.
We've seen several examples of specifications that chain from
clauses together.
This is equivalent to using the SelectMany
method in the method syntax.
Let's look at an example.
This was the specification that we used to find all the carts in an environment for a user before simplifying it with Relation
properties.
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 &&
customer.environment == environment
from cart in facts.OfType<Cart>()
where cart.customer == customer
select cart);
This can be rewritten using SelectMany
like this:
var cartsInEnvironmentForUser = Given<Environment, User>.Match((environment, user, facts) =>
facts.OfType<CustomerBuyer>()
.Where(customerBuyer => customerBuyer.buyer == user)
.SelectMany(customerBuyer =>
facts.OfType<Customer>()
.Where(customer => customerBuyer.customer == customer &&
customer.environment == environment)
.SelectMany(customer =>
facts.OfType<Cart>()
.Where(cart => cart.customer == customer)
)
)
);
I find the query expression syntax easier to read. But the method syntax is more flexible. The choice is yours.
Facts always have to be related to prior facts.
This could be done by following the facts.OfType<T>()
method with a call to Where
.
But since this is so common, facts.OfType<T>
can take a predicate directly.
var albumsByUser = Given<User>.Match((user, facts) =>
facts.OfType<Album>(album => album.creator == user));
Combining this with the SelectMany
method, we can simplify the specification above.
var cartsInEnvironmentForUser = Given<Environment, User>.Match((environment, user, facts) =>
facts.OfType<CustomerBuyer>(customerBuyer => customerBuyer.buyer == user)
.SelectMany(customerBuyer =>
facts.OfType<Customer>(customer => customerBuyer.customer == customer &&
customer.environment == environment)
.SelectMany(customer =>
facts.OfType<Cart>(cart => cart.customer == customer)
)
)
);
Jinaga provides the facts.Any<T>
method to check for the existence of a fact.
This is equivalent to the LINQ Any
method when applied to facts.OfType<T>
.
Previously we looked at a specification that excluded albums that have been deleted.
var albumsByUser = Given<User>.Match((user, facts) =>
facts.OfType<Album>()
.Where(album => album.creator == user &&
!facts.Any<AlbumDelete>(delete => delete.album == album))
);
This can also be written using the Any
method like this:
var albumsByUser = Given<User>.Match((user, facts) =>
facts.OfType<Album>()
.Where(album => album.creator == user &&
!facts.OfType<AlbumDelete>().Any(delete => delete.album == album))
);
Or you can apply the Where
clause before the Any
method.
var albumsByUser = Given<User>.Match((user, facts) =>
facts.OfType<Album>()
.Where(album => album.creator == user &&
!facts.OfType<AlbumDelete>()
.Where(delete => delete.album == album)
.Any())
);
The facts.Any<T>
method is provided to simplify these expressions.