public void Range_queries() { // should be optimized as a between query (GeLe operator) var query = ExpressionTreeHelper.PredicateToQuery <Order>(o => o.Quantity <= 10 && o.Quantity >= 2); Assert.AreEqual(1, query.Elements.Count); Assert.AreEqual(1, query.Elements[0].Elements.Count); Assert.AreEqual(QueryOperator.GeLe, query.Elements[0].Elements[0].Operator); Assert.IsTrue(query.IsValid); Console.WriteLine(query.ToString()); // should be optimized ad GtLe query = ExpressionTreeHelper.PredicateToQuery <Order>(o => o.Quantity <= 10 && o.Quantity > 2); Assert.AreEqual(1, query.Elements.Count); Assert.AreEqual(1, query.Elements[0].Elements.Count); Assert.AreEqual(QueryOperator.GtLe, query.Elements[0].Elements[0].Operator); Assert.IsTrue(query.IsValid); Console.WriteLine(query.ToString()); // should be optimized ad GeLt query = ExpressionTreeHelper.PredicateToQuery <Order>(o => o.Quantity < 10 && o.Quantity >= 2); Assert.AreEqual(1, query.Elements.Count); Assert.AreEqual(1, query.Elements[0].Elements.Count); Assert.AreEqual(QueryOperator.GeLt, query.Elements[0].Elements[0].Operator); Assert.IsTrue(query.IsValid); Console.WriteLine(query.ToString()); // should be optimized ad GtLt query = ExpressionTreeHelper.PredicateToQuery <Order>(o => o.Quantity < 10 && o.Quantity > 2); Assert.AreEqual(1, query.Elements.Count); Assert.AreEqual(1, query.Elements[0].Elements.Count); Assert.AreEqual(QueryOperator.GtLt, query.Elements[0].Elements[0].Operator); Assert.IsTrue(query.IsValid); Console.WriteLine(query.ToString()); }
/// <summary> /// Specifies an extra trigger property. Multiple statements may be chained /// </summary> /// <typeparam name="TParent"></typeparam> /// <typeparam name="TProperty"></typeparam> /// <typeparam name="TTargetProperty"></typeparam> /// <param name="token"></param> /// <param name="propertySelector"></param> /// <returns></returns> public static FluentToken <TParent, TTargetProperty> Or <TParent, TProperty, TTargetProperty>( this FluentToken <TParent, TTargetProperty> token, Expression <Func <TParent, TProperty> > propertySelector) { token.PropertyNames.Add(ExpressionTreeHelper.PropertyName(propertySelector)); return(token); }
/// <summary> /// Specifies the first trigger property /// </summary> /// <typeparam name="TParent"></typeparam> /// <typeparam name="TTargetProperty"></typeparam> /// <param name="token"></param> /// <param name="propertySelectors"></param> /// <returns></returns> public static void OnChanged <TParent, TTargetProperty>( this FluentToken <TParent, TTargetProperty> token, params Expression <Func <TParent, object> >[] propertySelectors) { foreach (var propertySelector in propertySelectors) { token.PropertyNames.Add(ExpressionTreeHelper.PropertyName(propertySelector)); } var propertyName = ExpressionTreeHelper.PropertyName(token.TargetPropertySelector); bool Updater(TParent parent) { if (token.IfPredicate != null) { if (!token.IfPredicate(parent)) { return(false); } } var newValue = token.ValueComputer(parent); var oldValue = RuntimeCompiler <TParent, TTargetProperty> .Getter(token.TargetPropertySelector)(parent); if (Equals(oldValue, newValue)) { return(false); } RuntimeCompiler <TParent, TTargetProperty> .SetterFromGetter(token.TargetPropertySelector)(parent, newValue); return(true); } var rule = new Rule <TParent> { TriggerProperties = token.PropertyNames, Updater = Updater, TargetPropertyName = propertyName, IfExplained = token.IfExplained, ValueComputerExplained = token.ValueComputerExplained }; token.MappingRulesContainer.Rules.Add(rule); foreach (var name in token.PropertyNames) { if (!token.MappingRulesContainer.RulesByTrigger.TryGetValue(name, out var rules)) { rules = new List <Rule <TParent> >(); token.MappingRulesContainer.RulesByTrigger.Add(name, rules); } rules.Add(rule); } }
public static PackedObject Pack <TObject>(TObject instance, [NotNull] CollectionSchema typeDescription, string collectionName = null) { if (instance == null) { throw new ArgumentNullException(nameof(instance)); } if (typeDescription == null) { throw new ArgumentNullException(nameof(typeDescription)); } var result = new PackedObject { // scalar values that are visible server-side Values = new KeyValue[typeDescription.ServerSide.Count(k => !k.IsCollection)], // vector values that are visible server-side CollectionValues = new KeyValues[typeDescription.ServerSide.Count(k => k.IsCollection)] }; // process server-side values var pos = 0; var collectionPos = 0; foreach (var metadata in typeDescription.ServerSide) { var getter = ExpressionTreeHelper.Getter <TObject>(metadata.Name); var value = getter(instance); if (!metadata.IsCollection) { // if the primary key is an empty Guid generate a value if (metadata.IndexType == IndexType.Primary) { if (Guid.Empty.Equals(value)) { value = Guid.NewGuid(); } } result.Values[pos++] = new KeyValue(value, metadata); } else { if (value is IEnumerable values && !(value is string)) { if (metadata.IndexType == IndexType.Ordered) { throw new NotSupportedException($"The property {metadata.Name} is a collection. It can be indexed as a dictionary but not as an ordered index"); } var keyValues = values.Cast <object>().Select(v => new KeyValue(v, metadata)); result.CollectionValues[collectionPos++] = new KeyValues(metadata.Name, keyValues); }
/// <summary> /// Always the last NON OPTIONAL statement in a rule declaration /// Internally produces an instance of <see cref="Rule{TParent}" /> and adds it into the rules engine /// </summary> /// <typeparam name="TParent"></typeparam> /// <typeparam name="TTargetProperty"></typeparam> /// <param name="token"></param> public static void EndRule <TParent, TTargetProperty>(this FluentToken <TParent, TTargetProperty> token) { if (token.MappingRulesContainer == null) { throw new NotSupportedException("Error in fluent syntax. Start with a Set() statement"); } var propertyName = ExpressionTreeHelper.PropertyName(token.TargetPropertySelector); bool Updater(TParent parent) { if (token.IfPredicate != null) { if (!token.IfPredicate(parent)) { return(false); } } var newValue = token.ValueComputer(parent); var oldValue = RuntimeCompiler <TParent, TTargetProperty> .Getter(token.TargetPropertySelector)(parent); if (Equals(oldValue, newValue)) { return(false); } RuntimeCompiler <TParent, TTargetProperty> .SetterFromGetter(token.TargetPropertySelector)(parent, newValue); return(true); } var rule = new Rule <TParent> { TriggerProperties = token.PropertyNames, Updater = Updater, TargetPropertyName = propertyName }; foreach (var name in token.PropertyNames) { if (!token.MappingRulesContainer.RulesByTrigger.TryGetValue(name, out var rules)) { rules = new List <Rule <TParent> >(); token.MappingRulesContainer.RulesByTrigger.Add(name, rules); } rules.Add(rule); } }
public void Speed_test_sql_vs_linq() { var schema = TypedSchemaFactory.FromType <Order>(); // warm-up var categories = new string[] { "geek", "games" }; var query1 = ExpressionTreeHelper.PredicateToQuery <Order>( o => o.IsDelivered && categories.Contains(o.Category) || o.Amount > 100 && o.Amount < 200, schema.CollectionName); var query2 = new Parser().ParseSql($"select * from {schema.CollectionName} where isdelivered = true and category in (geek, games) or amount > 100 and amount < 200").ToQuery(schema); Assert.AreEqual(query1.ToString(), query2.ToString()); const int iterations = 1000; { var clock = new Stopwatch(); clock.Start(); for (int i = 0; i < iterations; i++) { query1 = ExpressionTreeHelper.PredicateToQuery <Order>( o => o.IsDelivered && categories.Contains(o.Category) || o.Amount > 100 && o.Amount < 200, schema.CollectionName); } clock.Stop(); Console.WriteLine($"{iterations} iterations with linq took {clock.ElapsedMilliseconds} ms"); } { var clock = new Stopwatch(); clock.Start(); for (int i = 0; i < iterations; i++) { query2 = new Parser().ParseSql($"select * from {schema.CollectionName} where isdelivered = true and category in (geek, games) or amount > 100 and amount < 200").ToQuery(schema); } clock.Stop(); Console.WriteLine($"{iterations} iterations with sql took {clock.ElapsedMilliseconds} ms"); } }
public void Query_performance() { var schema = TypedSchemaFactory.FromType <Order>(); var queries = WhereClausesForOrders() .Select(w => ExpressionTreeHelper.PredicateToQuery(w, schema.CollectionName)).ToList(); var count = queries.Count; var objects = Order.GenerateTestData(100_000); var packed = objects.Select(o => PackedObject.Pack(o, schema)).ToList(); var ds = new DataStore(schema, new NullEvictionPolicy(), new FullTextConfig()); ds.InternalPutMany(packed, true); var watch = new Stopwatch(); for (var i = 0; i < count; i++) { var qm = new QueryManager(ds); const int iterations = 100; // warm up var returned = qm.ProcessQuery(queries[i]).Count; // run watch.Restart(); for (var j = 0; j < iterations; j++) { returned = qm.ProcessQuery(queries[i]).Count; } watch.Stop(); Console.WriteLine($"{queries[i]} returned {returned} took {watch.ElapsedMilliseconds / iterations} ms"); Console.WriteLine("execution plan:"); Console.WriteLine(qm.ExecutionPlan); Console.WriteLine(); } }
public void Get_property_name_from_complex_expression() { var name = ExpressionTreeHelper.PropertyName((Bingo b) => b.Message.Length); Assert.AreEqual("Length", name); var fullName = ExpressionTreeHelper.FullPropertyName((Bingo b) => b.Message.Length); Assert.AreEqual("Message.Length", fullName); var fullName1 = ExpressionTreeHelper.FullPropertyName((Bingo b) => b.Message); Assert.AreEqual("Message", fullName1); var fullName2 = ExpressionTreeHelper.FullPropertyName((CdsTrade cds) => cds.CdsProduct.RefEntity.Length); Assert.AreEqual("CdsProduct.RefEntity.Length", fullName2); }
public void Test_functions_vs_queries() { var schema = TypedSchemaFactory.FromType <Order>(); var queries = WhereClausesForOrders() .Select(w => ExpressionTreeHelper.PredicateToQuery(w, schema.CollectionName)).ToList(); var predicates = WhereClausesForOrders().Select(w => w.Compile()).ToList(); var count = queries.Count; Assert.AreEqual(count, predicates.Count); var objects = Order.GenerateTestData(1000); var packed = objects.Select(o => PackedObject.Pack(o, schema)).ToList(); var ds = new DataStore(schema, new NullEvictionPolicy(), new FullTextConfig()); ds.InternalPutMany(packed, true); for (var i = 0; i < count; i++) { var fromObjects = objects.Where(predicates[i]).ToList(); var qm = new QueryManager(ds); var fromDataSource = qm.ProcessQuery(queries[i]); Console.WriteLine($"{queries[i]} returned {fromDataSource.Count}"); Console.WriteLine("execution plan:"); Console.WriteLine(qm.ExecutionPlan); Console.WriteLine(); Assert.AreEqual(fromObjects.Count, fromDataSource.Count); } }
public void Atomic_query_subset() { var products1 = new [] { 101, 102, 103 }; var products2 = new [] { 101, 102 }; var q1 = ExpressionTreeHelper.PredicateToQuery <Order>(o => o.IsDelivered && products1.Contains(o.ProductId)); var q2 = ExpressionTreeHelper.PredicateToQuery <Order>(o => o.IsDelivered && products2.Contains(o.ProductId)); Assert.IsTrue(q1.IsSubsetOf(q1)); Assert.IsTrue(q2.IsSubsetOf(q1)); Assert.IsFalse(q1.IsSubsetOf(q2)); var q3 = ExpressionTreeHelper.PredicateToQuery <Order>(o => o.Quantity <= 10); var q4 = ExpressionTreeHelper.PredicateToQuery <Order>(o => o.Quantity < 10); var q5 = ExpressionTreeHelper.PredicateToQuery <Order>(o => o.Quantity <= 9); Assert.IsTrue(q5.IsSubsetOf(q3)); Assert.IsTrue(q4.IsSubsetOf(q3)); Assert.IsFalse(q3.IsSubsetOf(q5)); // should be optimized as a between query (GeLe operator) var q6 = ExpressionTreeHelper.PredicateToQuery <Order>(o => o.Quantity <= 10 && o.Quantity >= 2); Assert.AreEqual(1, q6.Elements.Count); Assert.AreEqual(1, q6.Elements[0].Elements.Count); Assert.AreEqual(QueryOperator.GeLe, q6.Elements[0].Elements[0].Operator); var q7 = ExpressionTreeHelper.PredicateToQuery <Order>(o => o.Quantity <= 4 && o.Quantity >= 2); Assert.IsTrue(q7.IsSubsetOf(q6)); var q8 = ExpressionTreeHelper.PredicateToQuery <Order>(o => o.Quantity <= 11 && o.Quantity >= 2); Assert.IsFalse(q8.IsSubsetOf(q6)); }
public void Distinct_operator() { var objects = Order.GenerateTestData(100_000); var schema = TypedSchemaFactory.FromType <Order>(); var packed = objects.Select(o => PackedObject.Pack(o, schema)).ToList(); var ds = new DataStore(schema, new NullEvictionPolicy(), new FullTextConfig()); ds.InternalPutMany(packed, true); // empty query { // result from linq2object to be compared with query var raw = objects.Select(o => new { o.Category, o.ClientId }).Distinct().ToList(); var q = new OrQuery(schema.CollectionName) { Distinct = true }; q.SelectClause.Add(new SelectItem { Name = "Category", Alias = "Category" }); q.SelectClause.Add(new SelectItem { Name = "ClientId", Alias = "ClientId" }); var qm = new QueryManager(ds); var result = qm.ProcessQuery(q).Select(PackedObject.Unpack <Order>).ToList(); Assert.AreEqual(raw.Count, result.Count); } // atomic query { // result from linq2object to be compared with query var raw = objects.Where(o => o.IsDelivered).Select(o => new { o.Category, o.ClientId }).Distinct().ToList(); var q = ExpressionTreeHelper.PredicateToQuery <Order>(o => o.IsDelivered); q.Distinct = true; q.SelectClause.Add(new SelectItem { Name = "Category", Alias = "Category" }); q.SelectClause.Add(new SelectItem { Name = "ClientId", Alias = "ClientId" }); var qm = new QueryManager(ds); var result = qm.ProcessQuery(q).Select(PackedObject.Unpack <Order>).ToList(); Assert.AreEqual(raw.Count, result.Count); } // simple and query { // result from linq2object to be compared with query var raw = objects.Where(o => o.IsDelivered && o.Amount < 100).Select(o => new { o.Category, o.ClientId }).Distinct().ToList(); var q = ExpressionTreeHelper.PredicateToQuery <Order>(o => o.IsDelivered && o.Amount < 100); q.Distinct = true; q.SelectClause.Add(new SelectItem { Name = "Category", Alias = "Category" }); q.SelectClause.Add(new SelectItem { Name = "ClientId", Alias = "ClientId" }); var qm = new QueryManager(ds); var result = qm.ProcessQuery(q).Select(PackedObject.Unpack <Order>).ToList(); Assert.AreEqual(raw.Count, result.Count); } // complex or query { // result from linq2object to be compared with query var raw = objects.Where(o => o.IsDelivered && o.Amount < 100 || o.Category == "sf" && o.Quantity > 1).Select(o => new { o.Category, o.ClientId }).Distinct().ToList(); var q = ExpressionTreeHelper.PredicateToQuery <Order>(o => o.IsDelivered && o.Amount < 100 || o.Category == "sf" && o.Quantity > 1); q.Distinct = true; q.SelectClause.Add(new SelectItem { Name = "Category", Alias = "Category" }); q.SelectClause.Add(new SelectItem { Name = "ClientId", Alias = "ClientId" }); var qm = new QueryManager(ds); var result = qm.ProcessQuery(q).Select(PackedObject.Unpack <Order>).ToList(); Assert.AreEqual(raw.Count, result.Count); q.Take = 3; result = qm.ProcessQuery(q).Select(PackedObject.Unpack <Order>).ToList(); Assert.AreEqual(3, result.Count); } }
public void Order_by() { var objects = Order.GenerateTestData(100_000); var schema = TypedSchemaFactory.FromType <Order>(); var packed = objects.Select(o => PackedObject.Pack(o, schema)).ToList(); var ds = new DataStore(schema, new NullEvictionPolicy(), new FullTextConfig()); ds.InternalPutMany(packed, true); // empty query { var q = new OrQuery(schema.CollectionName) { OrderByProperty = "Amount" }; var qm = new QueryManager(ds); var result = qm.ProcessQuery(q).Select(PackedObject.Unpack <Order>).ToList(); Console.WriteLine(qm.ExecutionPlan.ToString()); Assert.AreEqual(objects.Count, result.Count); // check sorted ascending for (int i = 0; i < result.Count - 1; i++) { Assert.LessOrEqual((int)result[i].Amount * 10000, (int)result[i + 1].Amount * 10000); } q.OrderByIsDescending = true; result = qm.ProcessQuery(q).Select(PackedObject.Unpack <Order>).ToList(); Console.WriteLine(qm.ExecutionPlan.ToString()); Assert.AreEqual(objects.Count, result.Count); // check sorted descending for (int i = 0; i < result.Count - 1; i++) { Assert.GreaterOrEqual((int)result[i].Amount * 10000, (int)result[i + 1].Amount * 10000); } } // atomic query { var q = ExpressionTreeHelper.PredicateToQuery <Order>(o => o.IsDelivered == false); // result from linq2object to be compared with ordered query var raw = objects.Where(o => o.IsDelivered == false).ToList(); q.OrderByProperty = "Amount"; var qm = new QueryManager(ds); var result = qm.ProcessQuery(q).Select(PackedObject.Unpack <Order>).ToList(); Console.WriteLine(qm.ExecutionPlan.ToString()); Assert.AreEqual(raw.Count, result.Count); // check sorted ascending for (int i = 0; i < result.Count - 1; i++) { Assert.LessOrEqual((int)result[i].Amount * 10000, (int)result[i + 1].Amount * 10000); } q.OrderByIsDescending = true; result = qm.ProcessQuery(q).Select(PackedObject.Unpack <Order>).ToList(); Console.WriteLine(qm.ExecutionPlan.ToString()); Assert.AreEqual(raw.Count, result.Count); // check sorted descending for (int i = 0; i < result.Count - 1; i++) { Assert.GreaterOrEqual((int)result[i].Amount * 10000, (int)result[i + 1].Amount * 10000); } } // simple AND query { var q = ExpressionTreeHelper.PredicateToQuery <Order>(o => o.IsDelivered == false && o.Category == "geek"); var raw = objects.Where(o => o.IsDelivered == false && o.Category == "geek").ToList(); q.OrderByProperty = "Amount"; var qm = new QueryManager(ds); var result = qm.ProcessQuery(q).Select(PackedObject.Unpack <Order>).ToList(); Console.WriteLine(qm.ExecutionPlan.ToString()); Assert.AreEqual(raw.Count, result.Count); for (int i = 0; i < result.Count - 1; i++) { Assert.LessOrEqual((int)result[i].Amount * 10000, (int)result[i + 1].Amount * 10000); } q.OrderByIsDescending = true; result = qm.ProcessQuery(q).Select(PackedObject.Unpack <Order>).ToList(); Console.WriteLine(qm.ExecutionPlan.ToString()); Assert.AreEqual(raw.Count, result.Count); for (int i = 0; i < result.Count - 1; i++) { Assert.GreaterOrEqual((int)result[i].Amount * 10000, (int)result[i + 1].Amount * 10000); } } // complex OR query { var q = ExpressionTreeHelper.PredicateToQuery <Order>(o => o.IsDelivered == false && o.Category == "geek" || o.Category == "sf"); var raw = objects.Where(o => o.IsDelivered == false && o.Category == "geek" || o.Category == "sf").ToList(); q.OrderByProperty = "Amount"; var qm = new QueryManager(ds); var result = qm.ProcessQuery(q).Select(PackedObject.Unpack <Order>).ToList(); Console.WriteLine(qm.ExecutionPlan.ToString()); Assert.AreEqual(raw.Count, result.Count); for (int i = 0; i < result.Count - 1; i++) { Assert.LessOrEqual((int)result[i].Amount * 10000, (int)result[i + 1].Amount * 10000); } q.OrderByIsDescending = true; result = qm.ProcessQuery(q).Select(PackedObject.Unpack <Order>).ToList(); Console.WriteLine(qm.ExecutionPlan.ToString()); Assert.AreEqual(raw.Count, result.Count); for (int i = 0; i < result.Count - 1; i++) { Assert.GreaterOrEqual((int)result[i].Amount * 10000, (int)result[i + 1].Amount * 10000); } // check that TAKE operator is applied after ORDER BY q.Take = 1; var max = qm.ProcessQuery(q).Select(PackedObject.Unpack <Order>).Single(); Assert.AreEqual(max.Amount, result[0].Amount); } }
public void Check_execution_plans() { var schema = TypedSchemaFactory.FromType <AllKindsOfProperties>(); var objects = GenerateAllKinds(100_000); var packed = objects.Select(o => PackedObject.Pack(o, schema)).ToList(); var ds = new DataStore(schema, new NullEvictionPolicy(), new FullTextConfig()); ds.InternalPutMany(packed, true); var qm = new QueryManager(ds); // first time for warm-up qm.ProcessQuery(ExpressionTreeHelper.PredicateToQuery <AllKindsOfProperties>(a => a.Tags.Contains("food"))); qm.ProcessQuery(ExpressionTreeHelper.PredicateToQuery <AllKindsOfProperties>(a => a.Tags.Contains("food"))); Console.WriteLine(qm.ExecutionPlan); Assert.AreEqual(1, qm.ExecutionPlan.QueryPlans.Count); Assert.IsTrue(qm.ExecutionPlan.QueryPlans[0].SimpleQueryStrategy); Assert.IsFalse(qm.ExecutionPlan.QueryPlans[0].FullScan); // first time for warm-up qm.ProcessQuery(ExpressionTreeHelper.PredicateToQuery <AllKindsOfProperties>(a => a.Tags.Contains("food") && a.Tags.Contains("space"))); var result = qm.ProcessQuery(ExpressionTreeHelper.PredicateToQuery <AllKindsOfProperties>(a => a.Tags.Contains("food") && a.Tags.Contains("space"))); Assert.AreEqual(0, result.Count); Assert.AreEqual(1, qm.ExecutionPlan.QueryPlans.Count); Assert.IsFalse(qm.ExecutionPlan.QueryPlans[0].SimpleQueryStrategy); Assert.IsFalse(qm.ExecutionPlan.QueryPlans[0].FullScan, "this query should be solved by an index not a full-scan"); Console.WriteLine(qm.ExecutionPlan); // first time for warm-up qm.ProcessQuery( ExpressionTreeHelper.PredicateToQuery <AllKindsOfProperties>(a => a.Quantity > 1 && a.Quantity < 2)); result = qm.ProcessQuery( ExpressionTreeHelper.PredicateToQuery <AllKindsOfProperties>(a => a.Quantity > 1 && a.Quantity < 2)); Console.WriteLine(qm.ExecutionPlan); Assert.AreEqual(0, result.Count); Assert.AreEqual(1, qm.ExecutionPlan.QueryPlans.Count); Assert.IsTrue(qm.ExecutionPlan.QueryPlans[0].SimpleQueryStrategy, "this query should have been optimized as a range query and executed as a simple query"); Assert.IsTrue(qm.ExecutionPlan.QueryPlans[0].FullScan, "this query should be executed as full-scan as the index is not ordered"); // first time for warm-up qm.ProcessQuery( ExpressionTreeHelper.PredicateToQuery <AllKindsOfProperties>(a => a.Quantity >= 1 || a.Quantity <= 2)); result = qm.ProcessQuery( ExpressionTreeHelper.PredicateToQuery <AllKindsOfProperties>(a => a.Quantity >= 1 || a.Quantity <= 2)); Console.WriteLine(qm.ExecutionPlan); Assert.AreEqual(100_000, result.Count); Assert.AreEqual(2, qm.ExecutionPlan.QueryPlans.Count, "this query should have been decomposed in two queries"); Assert.IsTrue(qm.ExecutionPlan.QueryPlans[0].SimpleQueryStrategy); // empty query. Should return everything result = qm.ProcessQuery(OrQuery.Empty <AllKindsOfProperties>()); Assert.AreEqual(100_000, result.Count); Console.WriteLine(qm.ExecutionPlan); }
public void Split_transaction_request() { var schema = TypedSchemaFactory.FromType <Order>(); var requests = new List <DataRequest>(); // simple put requests for (int i = 0; i < 100; i++) { var order1 = new Order { Id = Guid.NewGuid(), Category = "geek", ProductId = 123, Quantity = 1 }; var putRequest = new PutRequest("orders"); var packed1 = PackedObject.Pack(order1, schema, "orders"); putRequest.Items.Add(packed1); requests.Add(putRequest); } // conditional put requests for (int i = 0; i < 100; i++) { var order1 = new Order { Id = Guid.NewGuid(), Category = "geek", ProductId = 123, Quantity = 1 }; var putRequest = new PutRequest("orders"); var packed1 = PackedObject.Pack(order1, schema, "orders"); putRequest.Items.Add(packed1); putRequest.Predicate = ExpressionTreeHelper.PredicateToQuery <Order>(o => o.IsDelivered, "orders"); requests.Add(putRequest); } // simple delete requests for (int i = 0; i < 100; i++) { var order1 = new Order { Id = Guid.NewGuid(), Category = "geek", ProductId = 123, Quantity = 1 }; var packed1 = PackedObject.Pack(order1, schema, "orders1"); var deleteRequest = new RemoveRequest("orders1", packed1.PrimaryKey); requests.Add(deleteRequest); } // delete many request var deleteMany = new RemoveManyRequest(ExpressionTreeHelper.PredicateToQuery <Order>(o => o.IsDelivered, "orders")); requests.Add(deleteMany); var transactionRequest = new TransactionRequest(requests) { TransactionId = Guid.NewGuid() }; var split = transactionRequest.SplitByServer(k => k.GetHashCode() % 5, 5); var total = split.Values.Sum(r => r.ChildRequests.Count); // 300 uniformly distributed + 5 (delete many) cloned on each server Assert.AreEqual(305, total); Assert.IsTrue(split.Values.All(s => s.TransactionId == transactionRequest.TransactionId)); var tr0 = split[0]; Assert.IsTrue(tr0.ConditionalRequests.All(r => r.HasCondition)); Assert.IsTrue(tr0.ConditionalRequests.Any()); var deleteManyCount = tr0.ChildRequests.Count(r => r is RemoveManyRequest); Assert.AreEqual(1, deleteManyCount); Assert.AreEqual(2, tr0.AllCollections.Length);// orders and orders1 }