-
-
Notifications
You must be signed in to change notification settings - Fork 59
Quick start guide. Customers and orders. Traditional DBreeze
Alexey Solovyov edited this page Mar 23, 2017
·
1 revision
In this guide we will create customers, prototypes of business orders for these customers and determine different search functions.
Let's create WinForm application, add NuGet reference to protobuf-net and DBreeze. On the form create a button and replace code of the form with this one:
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;
using DBreeze;
using DBreeze.Utils;
namespace DBreezeQuickStart
{
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
}
public static DBreeze.DBreezeEngine engine = null;
protected override void OnFormClosing(FormClosingEventArgs e)
{
base.OnFormClosing(e);
if (engine != null)
engine.Dispose();
}
void InitDb()
{
if (engine == null)
{
engine = new DBreezeEngine(new DBreezeConfiguration { DBreezeDataFolderName = @"S:\temp\DBreezeTest\DBR1" });
//engine = new DBreezeEngine(new DBreezeConfiguration { DBreezeDataFolderName = @"C:\temp" });
//Setting default serializer for DBreeze
DBreeze.Utils.CustomSerializator.ByteArraySerializator = ProtobufSerializer.SerializeProtobuf;
DBreeze.Utils.CustomSerializator.ByteArrayDeSerializator = ProtobufSerializer.DeserializeProtobuf;
}
}
[ProtoBuf.ProtoContract]
public class Customer
{
[ProtoBuf.ProtoMember(1, IsRequired = true)]
public long Id { get; set; }
[ProtoBuf.ProtoMember(2, IsRequired = true)]
public string Name { get; set; }
}
[ProtoBuf.ProtoContract]
public class Order
{
public Order()
{
udtCreated = DateTime.UtcNow;
}
[ProtoBuf.ProtoMember(1, IsRequired = true)]
public long Id { get; set; }
[ProtoBuf.ProtoMember(2, IsRequired = true)]
public long CustomerId { get; set; }
/// <summary>
/// Order datetime creation
/// </summary>
[ProtoBuf.ProtoMember(3, IsRequired = true)]
public DateTime udtCreated { get; set; }
}
/// <summary>
/// -------------------------------------- STARTING TEST HERE -------------------------------------
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void button1_Click(object sender, EventArgs e)
{
//One time db init
this.InitDb();
//Simple test
////Test insert
//using (var tran = engine.GetTransaction())
//{
// tran.Insert<int, int>("t1", 1, 1);
// tran.Insert<int, int>("t1", 1, 2);
// tran.Commit();
//}
////Test select
//using (var tran = engine.GetTransaction())
//{
// var xrow = tran.Select<int, int>("t1",1);
// if (xrow.Exists)
// {
// Console.WriteLine(xrow.Key.ToString() + xrow.Value.ToString());
// }
// //or
// foreach (var row in tran.SelectForward<int, int>("t1"))
// {
// Console.WriteLine(row.Value);
// }
//}
//More complex test
//!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! RUN ONLY ONCE, THEN CLEAR DB
//Inserting CustomerId 1
var customer = new Customer() { Name = "Tino Zanner" };
Test_InsertCustomer(customer);
//Inserting orders for customer 1
//for (int i = 0; i < 5; i++)
//{
// Test_InsertOrder(new Order { CustomerId = customer.Id });
//}
//or inserting batch of orders
Test_InsertOrders(
Enumerable.Range(1, 5)
.Select(r => new Order { CustomerId = customer.Id })
);
//Inserting CustomerId 2
customer = new Customer() { Name = "Michael Hinze" };
Test_InsertCustomer(customer);
//Inserting orders for customer 2
//for (int i = 0; i < 8; i++)
//{
// Test_InsertOrder(new Order { CustomerId = customer.Id });
//}
//or inserting batch of orders
Test_InsertOrders(
Enumerable.Range(1, 8)
.Select(r => new Order { CustomerId = customer.Id })
);
//Getting all orders
Console.WriteLine("All orders");
Test_GetOrdersByDateTime(DateTime.MinValue, DateTime.MaxValue);
//Getting Orders of customer 1
Console.WriteLine("Orders of customer 1");
Test_GetOrdersByCustomerIdAndDateTime(1, DateTime.MinValue, DateTime.MaxValue);
//Getting Orders of customer 2
Console.WriteLine("Orders of customer 2");
Test_GetOrdersByCustomerIdAndDateTime(2, DateTime.MinValue, DateTime.MaxValue);
/*
Result:
Inserted CustomerId: 1, Name: Tino Zanner
Inserted CustomerId: 2, Name: Michael Hinze
All orders
28.08.2015 07:15:57.734 orderId: 1
28.08.2015 07:15:57.740 orderId: 2
28.08.2015 07:15:57.743 orderId: 3
28.08.2015 07:15:57.743 orderId: 4
28.08.2015 07:15:57.743 orderId: 5
28.08.2015 07:15:57.757 orderId: 6
28.08.2015 07:15:57.758 orderId: 7
28.08.2015 07:15:57.758 orderId: 8
28.08.2015 07:15:57.759 orderId: 9
28.08.2015 07:15:57.759 orderId: 10
28.08.2015 07:15:57.759 orderId: 11
28.08.2015 07:15:57.760 orderId: 12
28.08.2015 07:15:57.760 orderId: 13
Orders of customer 1
28.08.2015 07:15:57.734 orderId: 1
28.08.2015 07:15:57.740 orderId: 2
28.08.2015 07:15:57.743 orderId: 3
28.08.2015 07:15:57.743 orderId: 4
28.08.2015 07:15:57.743 orderId: 5
Orders of customer 2
28.08.2015 07:15:57.757 orderId: 6
28.08.2015 07:15:57.758 orderId: 7
28.08.2015 07:15:57.758 orderId: 8
28.08.2015 07:15:57.759 orderId: 9
28.08.2015 07:15:57.759 orderId: 10
28.08.2015 07:15:57.759 orderId: 11
28.08.2015 07:15:57.760 orderId: 12
28.08.2015 07:15:57.760 orderId: 13
*/
return;
}
/// <summary>
///
/// </summary>
/// <param name="cust"></param>
void Test_InsertCustomer(Customer cust)
{
try
{
using (var tran = engine.GetTransaction())
{
//We don't need this line because we write only into one root table.
//Add more table names for safe transaction operations among multiple
//root tables (read docu)
tran.SynchronizeTables("Customers");
//In table Customers under key 1 we will have nested table with customers
var tbl = tran.InsertTable<int>("Customers", 1, 0);
//Under index 2 we will have monotonically grown id
if (cust.Id < 1)
{
//Insert
//Getting new ID for the customer
cust.Id = tran.Select<int, long>("Customers", 2).Value + 1;
//and inserting id back into key 2
tran.Insert<int, long>("Customers", 2, cust.Id);
}
//Inserting or updating of the customer
tbl.Insert<long, Customer>(cust.Id, cust);
//Committing entry
tran.Commit();
}
//Checking if customer is saved
using (var tran = engine.GetTransaction())
{
//using SelectTable instead of InsertTable (read docu). In short if we plan to write and/or to read
//from nested table during one transaction then we use InsertTable, if only to read - then SelectTable.
var tbl = tran.SelectTable<int>("Customers", 1, 0);
var row = tbl.Select<long, Customer>(cust.Id);
if (row.Exists)
Console.WriteLine("Inserted CustomerId: {0}, Name: {1}", row.Value.Id, row.Value.Name);
else
Console.WriteLine("Insert failed");
}
}
catch (Exception)
{
throw;
}
}
/// <summary>
///
/// </summary>
/// <param name="order"></param>
void Test_InsertOrder(Order order)
{
try
{
/*
In our case, we will store orders of all customers in one table "Orders".
Of course we could create for every customer his own table, like Order1, Order2...etc
Later we are planning to search orders:
1. by Order.Id
2. by Order.udtCreated From-To
3. by Order.CustomerId and Order.udtCreated From-To
To fulfill 2 and 3 conditions we will need to store several extra indicies.
*/
using (var tran = engine.GetTransaction())
{
//We don't need this line because we write only into one root table.
//Add more table names for safe transaction operations among multiple
//root tables (read docu)
tran.SynchronizeTables("Orders");
//Under key 1 we want to store nested table with orders
var tbl = tran.InsertTable<int>("Orders", 1, 0);
//Under key 2 we will store monotonically grown id for orders
//Index table for the second search condition under key 3
var tblDateIndex = tran.InsertTable<int>("Orders", 3, 0);
//Index table for the third search condition under key 4
var tblCustomerAndDateIndex = tran.InsertTable<int>("Orders", 4, 0);
byte[] key = null;
if (order.Id < 1)
{
//Insert, getting new ID
order.Id = tran.Select<int, long>("Orders", 2).Value + 1;
//and inserting id back into index 2
tran.Insert<int, long>("Orders", 2, order.Id);
//Inserting secondary index into tblDateIndex.
//Index will be complex and will let us search orders by creation DateTime
key = order.udtCreated.To_8_bytes_array().Concat(order.Id.To_8_bytes_array_BigEndian());
//Here we have composite key date+uniqueOrderIndex (read docu). Value will be Id of the order stored in tbl.
//As a value we could also use the same order as in tbl (redundant storage for the higher speed) or pointer to the key/value in tbl for SelectDirect (read docu)
tblDateIndex.Insert<byte[], long>(key, order.Id);
//Inserting secondary index into tblCustomerAndDateIndex
//Key will start from CustomerId, then comes dateTime and then unique id of the order
key = order.CustomerId.To_8_bytes_array_BigEndian().ConcatMany(order.udtCreated.To_8_bytes_array(), order.Id.To_8_bytes_array_BigEndian());
tblCustomerAndDateIndex.Insert<byte[], long>(key, order.Id);
}
//Inserting or updating customer
tbl.Insert<long, Order>(order.Id, order);
//Committing entry
tran.Commit();
}
}
catch (Exception)
{
throw;
}
}
/// <summary>
///
/// </summary>
/// <param name="order"></param>
void Test_InsertOrders(IEnumerable<Order> orders)
{
try
{
/*
In our case, we will store orders of all customers in one table "Orders".
Of course we could create for every customer his own table, like Order1, Order2...etc
Later we are planning to search orders:
1. by Order.Id
2. by Order.udtCreated From-To
3. by Order.CustomerId and Order.udtCreated From-To
To fulfill 2 and 3 conditions we will need to store several extra indicies.
*/
using (var tran = engine.GetTransaction())
{
//We don't need this line because we write only into one root table.
//Add more table names for safe transaction operations among multiple
//root tables (read docu)
tran.SynchronizeTables("Orders");
//Under key 1 we want to store nested table with orders
var tbl = tran.InsertTable<int>("Orders", 1, 0);
//Under key 2 we will store monotonically grown id for orders
//Index table for the second search condition under key 3
var tblDateIndex = tran.InsertTable<int>("Orders", 3, 0);
//Index table for the third search condition under key 4
var tblCustomerAndDateIndex = tran.InsertTable<int>("Orders", 4, 0);
byte[] key = null;
foreach (var ord in orders)
{
if (ord.Id < 1)
{
//Insert, getting new ID
ord.Id = tran.Select<int, long>("Orders", 2).Value + 1;
//and inserting id back into index 2
tran.Insert<int, long>("Orders", 2, ord.Id);
//Inserting secondary index into tblDateIndex.
//Index will be complex and will let us search orders by creation DateTime
key = ord.udtCreated.To_8_bytes_array().Concat(ord.Id.To_8_bytes_array_BigEndian());
//Here we have composite key date+uniqueOrderIndex (read docu). Value will be Id of the order stored in tbl.
//As a value we could also use the same order as in tbl (redundant storage for the higher speed) or pointer to the key/value in tbl for SelectDirect (read docu)
tblDateIndex.Insert<byte[], long>(key, ord.Id);
//Inserting secondary index into tblCustomerAndDateIndex
//Key will start from CustomerId, then comes dateTime and then unique id of the order
key = ord.CustomerId.To_8_bytes_array_BigEndian().ConcatMany(ord.udtCreated.To_8_bytes_array(), ord.Id.To_8_bytes_array_BigEndian());
tblCustomerAndDateIndex.Insert<byte[], long>(key, ord.Id);
}
//Inserting or updating customer
tbl.Insert<long, Order>(ord.Id, ord);
}
//Committing all changes
tran.Commit();
}
}
catch (Exception)
{
throw;
}
}
/// <summary>
///
/// </summary>
/// <param name="from"></param>
/// <param name="to"></param>
void Test_GetOrdersByDateTime(DateTime from, DateTime to)
{
try
{
using (var tran = engine.GetTransaction())
{
var tbl = tran.SelectTable<int>("Orders", 1, 0);
var tblDateIndex = tran.SelectTable<int>("Orders", 3, 0);
byte[] keyFrom = from.To_8_bytes_array().Concat(long.MinValue.To_8_bytes_array_BigEndian());
byte[] keyTo = to.To_8_bytes_array().Concat(long.MaxValue.To_8_bytes_array_BigEndian());
foreach (var row in tblDateIndex.SelectForwardFromTo<byte[], long>(keyFrom, true, keyTo, true))
{
var order = tbl.Select<long, Order>(row.Value);
if (order.Exists)
Console.WriteLine(order.Value.udtCreated.ToString("dd.MM.yyyy HH:mm:ss.fff") + " orderId: " + order.Value.Id);
}
}
}
catch (Exception)
{
throw;
}
}
void Test_GetOrdersByCustomerIdAndDateTime(long customerId, DateTime from, DateTime to)
{
try
{
using (var tran = engine.GetTransaction())
{
var tbl = tran.SelectTable<int>("Orders", 1, 0);
var tblCustomerAndDateIndex = tran.SelectTable<int>("Orders", 4, 0);
byte[] keyFrom = customerId.To_8_bytes_array_BigEndian().ConcatMany(from.To_8_bytes_array(), long.MinValue.To_8_bytes_array_BigEndian());
byte[] keyTo = customerId.To_8_bytes_array_BigEndian().ConcatMany(to.To_8_bytes_array(), long.MaxValue.To_8_bytes_array_BigEndian());
foreach (var row in tblCustomerAndDateIndex.SelectForwardFromTo<byte[], long>(keyFrom, true, keyTo, true))
{
var order = tbl.Select<long, Order>(row.Value);
if (order.Exists)
Console.WriteLine(order.Value.udtCreated.ToString("dd.MM.yyyy HH:mm:ss.fff") + " orderId: " + order.Value.Id);
}
}
}
catch (Exception)
{
throw;
}
}
}
public static class ProtobufSerializer
{
/// <summary>
/// Deserializes protobuf object from byte[]
/// </summary>
/// <typeparam name="T"></typeparam>
/// <param name="data"></param>
/// <returns></returns>
public static T DeserializeProtobuf<T>(this byte[] data)
{
T ret = default(T);
using (System.IO.MemoryStream ms = new System.IO.MemoryStream(data))
{
ret = ProtoBuf.Serializer.Deserialize<T>(ms);
ms.Close();
}
return ret;
}
/// <summary>
/// Deserializes protobuf object from byte[]. Non-generic style.
/// </summary>
/// <param name="data"></param>
/// <param name="T"></param>
/// <returns></returns>
public static object DeserializeProtobuf(byte[] data, Type T)
{
object ret = null;
using (System.IO.MemoryStream ms = new System.IO.MemoryStream(data))
{
ret = ProtoBuf.Serializer.NonGeneric.Deserialize(T, ms);
ms.Close();
}
return ret;
}
/// <summary>
/// Serialize object using protobuf serializer
/// </summary>
/// <param name="data"></param>
/// <returns></returns>
public static byte[] SerializeProtobuf(this object data)
{
byte[] bt = null;
using (System.IO.MemoryStream ms = new System.IO.MemoryStream())
{
ProtoBuf.Serializer.NonGeneric.Serialize(ms, data);
bt = ms.ToArray();
ms.Close();
}
return bt;
}
}
}