Structurizer is a small project that initially comes from an old project of mine, SisoDB. In there it was used to assist the indexer to index objects in a database. The idea is that it builds a meta-model for your object type and uses IL to extract and build a key-value representation of it. OK, sounds nice, but what can this be used for? Indexing, logging, checksum generation ... and other situations where you need a generic key-value representation of your objects.
Step 1 - Install
NuGet is your friend here (I hope) even though it works in mysterious ways sometimes.
install-package structurizer
Step 2 - Define a model
Lets keep this simple and go with an simplified Order model.
public class Order
{
public int Id { get; set; }
public string OrderNo { get; set; }
public DateTime PlacedAt { get; set; }
public List<OrderLine> Lines { get; set; }
}
public class OrderLine
{
public string ArticleNo { get; set; }
public int Qty { get; set; }
public List<Prop> Props { get; set; }
}
public class Prop
{
public string Name { get; set; }
public string Value { get; set; }
}
Step 3 - Configure types in Structurizer
Structurizer uses a StructureBuilder
to create one Structure
per object-instance being passed. For the builder to work, you need to register the type in StructureTypeConfigurations
.
var typeConfigs = new StructureTypeConfigurations();
typeConfigs.Register<Order>();
This will by default create StructureIndexes
for all public properties of the object. To limit you can either Include
or Exclude
members.
Including
typeConfigs.Register<Order>(cfg => cfg
.UsingIndexMode(IndexMode.Inclusive)
.Members(i => i.OrderNo)
.Members(i => i.Lines[0].ArticleNo)
.Members(i => i.Lines[0].Qty));
Excluding
typeConfigs.Register<Order>(cfg => cfg
.UsingIndexMode(IndexMode.Exclusive)
.Members(i => i.Lines[0].Props));
Step 4 - Create the builder
The StructureBuilder
is what actually creates the Structures
that contains the StructureIndexes
. To be able to create a Structure
it needs the TypeConfigurations
.
Please, please, please, understand that you should really keep the StructureBuilder
around as it caches information about the meta model for your types. So don't re-create the builder all the time.
var structureBuilder = StructureBuilder.Create(typeConfigs);
Step 5 - Create Structures with Structure indexes
Use the StructureBuilder
and just call CreateStructure
or if you have a collection of objects, CreateStructures
.
Please note that any extracted NullValues
will not be returned. So Null
is not seen as a value.
var orderStructure = structureBuilder.CreateStructure(order);
DumpStructure(orderStructure);
private static void DumpStructure(IStructure structure)
{
Console.WriteLine($"===== {structure.Name} =====");
foreach (var index in structure.Indexes)
Console.WriteLine(DefaultIndexValueFormatter.Format(index));
}
Each StructureIndes
contains:
- Name
- Path
- Value
- DataType
- DataTypeCode
These can be used to e.g. format how values should be output or control how values should be indexed or something. Lets do some simple formatting.
public static class DefaultIndexValueFormatter
{
public static string Format(IStructureIndex index)
{
switch (index.DataTypeCode)
{
case DataTypeCode.String:
case DataTypeCode.Guid:
case DataTypeCode.Enum:
return $"Path\t{index.Path}=\"{index.Value}\"";
case DataTypeCode.DateTime:
return $"Path\t{index.Path}=\"{((DateTime)index.Value):O}\"";
default:
return $"Path\t{index.Path}={index.Value}";
}
}
}
The output, running using
typeConfigs.Register<Order>(cfg => cfg
.UsingIndexMode(IndexMode.Exclusive)
.Members(i => i.Lines[0].Props));
will be:
===== Order =====
Path Id=1
Path OrderNo="2016-1234"
Path PlacedAt="2016-11-13T16:56:36.3679890+01:00"
Path Lines[0].ArticleNo="Article-Line0"
Path Lines[1].ArticleNo="Article-Line1"
Path Lines[0].Qty=42
Path Lines[1].Qty=3
Each StructureIndex
also has a Name
property. The difference between Name
and Path
is only visible when there's an index for a value in a path, steeming from something that is enumerable:
"Name": "Lines.ArticleNo"
"Path": "Lines[0].ArticleNo"
"Value": "Article-Line0"
That's it. Feel free to use it. It's MIT licensed.
Cheers,
//Daniel