Marten allows you to add Linq parsing and querying support for your own custom methods.
Using the (admittedly contrived) example from Marten's tests, say that you want to reuse a small part of a Where()
clause across
different queries for "IsBlue()." First, write the method you want to be recognized by Marten's Linq support:
public class IsBlue : IMethodCallParser
{
private static readonly PropertyInfo _property = ReflectionHelper.GetProperty<ColorTarget>(x => x.Color);
public bool Matches(MethodCallExpression expression)
{
return expression.Method.Name == nameof(CustomExtensions.IsBlue);
}
public IWhereFragment Parse(IQueryableDocument mapping, ISerializer serializer, MethodCallExpression expression)
{
var locator = mapping.FieldFor(new MemberInfo[] {_property}).SqlLocator;
return new WhereFragment($"{locator} = 'Blue'");
}
}
Note a couple things here:
- If you're only using the method for Linq queries, it technically doesn't have to be implemented and never actually runs
- The methods do not have to be extension methods, but we're guessing that will be the most common usage of this
Now, to create a custom Linq parser for the IsBlue()
method, you need to create a custom implementation of the IMethodCallParser
interface shown below:
public interface IMethodCallParser
{
bool Matches(MethodCallExpression expression);
IWhereFragment Parse(IQueryableDocument mapping, ISerializer serializer, MethodCallExpression expression);
}
The IMethodCallParser
interface needs to match on method expressions that it could parse, and be able to turn the Linq expression into
part of a Postgresql "where" clause. The custom Linq parser for IsBlue()
is shown below:
public static bool IsBlue(this ColorTarget target)
{
return target.Color == "Blue";
}
Lastly, to plug in our new parser, we can add that to the StoreOptions
object that we use to bootstrap a new DocumentStore
as shown below:
[Fact]
public void query_with_custom_parser()
{
using (var store = DocumentStore.For(_ =>
{
_.Connection(ConnectionSource.ConnectionString);
// IsBlue is a custom parser I used for testing this
_.Linq.MethodCallParsers.Add(new IsBlue());
_.AutoCreateSchemaObjects = AutoCreate.All;
// This is just to isolate the test
_.DatabaseSchemaName = "isblue";
}))
{
store.Advanced.Clean.CompletelyRemoveAll();
var targets = new List<ColorTarget>();
for (var i = 0; i < 25; i++)
{
targets.Add(new ColorTarget {Color = "Blue"});
targets.Add(new ColorTarget {Color = "Green"});
targets.Add(new ColorTarget {Color = "Red"});
}
var count = targets.Where(x => x.IsBlue()).Count();
targets.Each(x => x.Id = Guid.NewGuid());
store.BulkInsert(targets.ToArray());
using (var session = store.QuerySession())
{
session.Query<ColorTarget>().Count(x => CustomExtensions.IsBlue(x))
.ShouldBe(count);
}
}
}