Reusing existing .NET types with CodeFluent Entities
First of all happy new year to our readers 🙂
Yes, using CodeFluent Entities, you can store existing .NET types instances to the persistence layer (whatever that is: SQL Server, Oracle database, etc.). There are actually many ways to do it.
Ok, let’s suppose we want to store, say, graphical shapes in the database. We define this very simple model, defining coordinates, width and height of the shape bounding rectangle:
<Shape> <Id /> <BoundsX typeName="int" /> <BoundsY typeName="int" /> <BoundsW typeName="int" /> <BoundsH typeName="int" /> </Shape>
Here, .NET connoisseurs should object that this is heresy! There is an existing .NET type “Rectangle”, in the System.Drawing namespace, from the standard System.Drawing assembly, that already defines something similar. Can we use it? Yes, we can!
Out first shoot at this is:
<Shape> <Id /> <Bounds typeName="System.Drawing.Rectangle" /> </Shape>
That works fine because CodeFluent Entities tries to serialize everything it does not “know”. Known types are (using C# aliases when it exists): int, uint, long, ulong, short, ushort, byte, sbyte, byte, bool, float, double, decimal, string, char, System.Guid, System.DateTime, System.TimeSpan, explicit CodeFluent blob types (declared using “file”, “video”, “image”, “audio”), all enum (System.Enum) types.
For all other “unknown” types, CodeFluent Entities uses serialization. Ok, but what kind of serialization?
First, CodeFluent checks to see if the type implements the little known “IBinarySerialize” (from the Microsoft.SqlServer.Server namespace) interface. This is especially useful for SQL 2008 types (see this article http://forums.softfluent.com/default.aspx?g=posts&t=40 for more details).
Then, CodeFluent Entities uses what has been configured on the property. This is defined by the model attribute ‘persistenceSerializationMode’. The default value is ‘Binary | ArrayOfBytesType’. It means the .NET instance will be serialized using .NET standard serialization (not XML, not SOAP), and stored as an array of bytes.
Thus, our rectangle will be serialized as an array of bytes. With this model, the corresponding “Bounds” column data type in the database will be TEXT/NTEXT (SQL server) or BLOB (Oracle).
With the following C# using the generated BOM (note we use the standard .NET Rectangle type, as expected):
Shape shape = new Shape(); shape.Bounds = new System.Drawing.Rectangle(10, 20, 30, 40); shape.Save();
We will have this in SQL Server:
Now, we could also declare this
<Shape> <Id /> <Bounds typeName="System.Drawing.Rectangle" persistenceSerializationMode="Xml | StringType" /> </Shape>
Because we used Xml combined with StringType, the corresponding “Bounds” column data type in the database will be XML (supported in SQL Server 2005 or higher and Oracle Database 10 or higher). With the same C# code, we will have this in SQL Server:
If we click on the Xml column, we can see the Rectangle instance was automatically serialized as plain Xml, thanks to CodeFluent Entities:
Now, let’s suppose we want to load all our shapes ordered – server side – by the rectangle area, or we want to write some back end process that need access to the inner properties of the stored rectangles. What we want to do is have the X, Y, Width and Height properties available in the persistence layer. The solution is to declare the Rectangle .NET type as an existing lightweight entity. This is how we do this:
<cf:project xmlns:cf="http://www.softfluent.com/codefluent/2005/1" defaultNamespace="Test" assemblyPaths="System.Drawing"> <Rectangle namespace="System.Drawing, System.Drawing"> <X /> <Y /> <Height /> <Width /> </Rectangle> <Shape> <Id /> <Bounds typeName="System.Drawing.Rectangle" /> <cf:method name="LoadOrderedByBoundsSize" body="load() raw"> SELECT * FROM Shape ORDER BY Bounds_Shape_Rectangle_Width * Bounds_Shape_Rectangle_Height </cf:method> </Shape> </cf:project>
In this model, you can see we declare a regular entity named “Rectangle” in the namespace System.Drawing. The ‘namespace’ attribute notation here includes the assembly name as well (without culture and version). Please note the project node also has an ‘assemblyPaths’ that helps CodeFluent Entities to determine if an entity is an existing type or not. The value of ‘assemblyPaths’ is just a comma separated list of physical assembly paths, or GAC names.
When an existing type is declared as an entity, this entity does not have the notion of key properties. You just need to declare what properties you want CodeFluent Entities to use when persisting an instance of it. These properties must exist and the name is case sensitive. In the case of Rectangle, X, Y, Height and Width are real properties of System.Int32 type. Rectangle also has Bottom or Left properties, but since they are redundant, we don’t declare them. The persistence type is automatically determined by CodeFluent using Reflection techniques.
In this example, the method is a RAW method because CodeFluent does not allow the * notation in an order by.
In the database we now have 4 columns representing the Bounds property. If we use the same previous C# code, this is what we have in the database:
Have fun with CodeFluent Entities!
CodeFluent Entities R&D team.