Skip to main content

Reference

Reference

Both overloads are in Vali_Flow.NoSql.MongoDB.Extensions.ValiFlowMongoExtensions.

ToMongo<T>(this ValiFlow<T> flow, Func<object?, BsonValue?>? customConverter = null)

Translates the conditions accumulated in a ValiFlow<T> builder into a BsonDocument filter.

ParameterTypeRequiredDescription
flowValiFlow<T>YesThe builder containing the conditions.
customConverterFunc<object?, BsonValue?>?NoHook for mapping CLR types not handled by the built-in switch. Return null to fall through to the default conversion.

Returns: BsonDocument — pass directly to Find, CountDocuments, DeleteMany, etc.

ToMongo<T>(this Expression<Func<T, bool>> expression, Func<object?, BsonValue?>? customConverter = null)

Same translation for a pre-built Expression<Func<T, bool>>.

Expression<Func<Order, bool>> expr = o => o.Status == "Pending" && o.Total > 50m;
BsonDocument filter = expr.ToMongo();

Supported Operations

ValiFlow methodIR nodeMongoDB output
.EqualTo(x => x.Field, v)EqualNode(IsNegated: false){ field: value }
.NotEqualTo(x => x.Field, v)EqualNode(IsNegated: true){ field: { $ne: value } }
.GreaterThan(x => x.Field, v)ComparisonNode(GT){ field: { $gt: value } }
.GreaterThanOrEqualTo(x => x.Field, v)ComparisonNode(GTE){ field: { $gte: value } }
.LessThan(x => x.Field, v)ComparisonNode(LT){ field: { $lt: value } }
.LessThanOrEqualTo(x => x.Field, v)ComparisonNode(LTE){ field: { $lte: value } }
.Contains(x => x.Field, "txt")LikeNode(Contains){ field: { $regex: /txt/i } }
.StartsWith(x => x.Field, "pre")LikeNode(StartsWith){ field: { $regex: /^pre/i } }
.EndsWith(x => x.Field, "suf")LikeNode(EndsWith){ field: { $regex: /suf$/i } }
.In(x => x.Field, list)InNode{ field: { $in: [...] } }
.IsNull(x => x.Field)NullNode(IsNull){ field: null }
.IsNotNull(x => x.Field)NullNode(IsNotNull){ field: { $ne: null } }
.And(a, b)AndNode{ $and: [a, b] }
.Or(a, b)OrNode{ $or: [a, b] }
.Not(inner)NotNode{ $nor: [inner] }

All string pattern matches use the i regex flag (case-insensitive). Special regex characters in the pattern are escaped before embedding.


Type Mapping

The built-in ToBsonValue switch handles these CLR types:

CLR typeBsonValue produced
nullBsonNull.Value
boolBsonBoolean
intBsonInt32
longBsonInt64
doubleBsonDouble
floatBsonDouble
decimalBsonDecimal128
stringBsonString
DateTimeBsonDateTime (UTC)
DateTimeOffsetBsonDateTime (.UtcDateTime)
GuidBsonBinaryData (GuidRepresentation.Standard)
EnumBsonInt32 (underlying integer value)
anything elseBsonValue.Create(v)

Custom Value Converter

Use customConverter when your domain types are not in the table above, or when you need different serialization than the default (for example, storing a decimal as BsonDouble instead of BsonDecimal128).

The converter is called before the built-in switch. Return null to let the default handle the value.

// Domain type
record Money(decimal Amount, string Currency);

// Converter: store Money as a BsonDecimal128 of the amount
BsonDocument filter = new ValiFlow<Product>()
.GreaterThan(x => x.Price, new Money(100m, "USD"))
.ToMongo(value =>
{
if (value is Money m)
return new BsonDecimal128(m.Amount);
return null; // fall through for all other types
});

Another common case — force decimal to store as BsonDouble:

BsonDocument filter = new ValiFlow<Order>()
.GreaterThan(x => x.Total, 250.00m)
.ToMongo(value =>
{
if (value is decimal d)
return new BsonDouble((double)d);
return null;
});

Advanced Example

// Custom converter for Money type
var filter = new ValiFlow<Order>()
.GreaterThan(o => o.Total, 100m)
.ToMongo(obj => obj is Money m ? new BsonDecimal128(m.Amount) : null);