As indicated by the ORM name, the key to implementing the ORM is to resolve the ing between "Object-relation". For example, how to convert a datarow into an entity object, and how to map an operation on an entity object to an idbcommand. Take datarabbit as an example,In datarabbit, use the iormapping interface to abstract these mappings:
Public Interface Iormapping < Tentity >
{
Tentity getentityfrom (DatarowRow, Bool Withblob );
/// <Summary>
/// Fillparametervalue uses the entity content to fill in the parameter values of each idbdataparameter in the command.
/// </Summary>
Void Fillparametervalue (IdbcommandCommand, tentity entity );
}
There are at least four solutions for implementing the iormapping interface. Let's take the getentityfrom method as an example to describe these four solutions. This method is to convert a datarow into an entity object.
1.CodeGenerator
Currently, many code generators can directly generate the Dal layer that implements the ORM function. The principle behind this layer is to follow the outline of the data table (such as the columns and types of each column) to generate the corresponding class that implements the iormapping interface. The Code Generator completes these tasks before compilation.
2. Reflection
Convert a datarow to an entity object. If the granularity is finer, we need to solve how to assign the values of a column of datarow to the corresponding attributes of the entity object, we can use reflection to assign values to the attributes of the entity object at runtime, for example:
Entitytype. invokemember (columnname,Bindingflags. Public | Bindingflags. Ignorecase |
Bindingflags. Instance | Bindingflags. Setproperty, Null , Entity, row [columnname]);
The meaning of this line of code isRowThe value of the columnname column in is assignedEntityThe object's columnname attribute.
3. emit
At runtime, We can dynamically launch corresponding iormapping interface types based on each entity type. After these dynamic types are successfully launched, they can be instantiated and used. For example, we want to launch the code that implements the getentityfrom method:
Private Void Emitgetentityfropolichod (TypebuilderTypebuilder,MethodinfoBasemethod,TypeEntitytype,DataschemaDataschema)
{
MethodbuilderMethodbuilder = Typebuilder. definemethod ( "
Getentityfrom " , Basemethod. Attributes & ~ Methodattributes. Abstract, basemethod. callingconvention, basemethod. returntype, emithelper. getparameterstype (basemethod ));
Ilgenerator = Methodbuilder. getilgenerator ();
Label retlabel = Ilgenerator. definelabel ();
Ilgenerator. declarelocal (entitytype ); // Member member = NULL;
Ilgenerator. emit (Opcodes. Nop );
Ilgenerator. emit (Opcodes. newobj, entitytype. getconstructor ( New Type [] {});
Ilgenerator. emit (Opcodes. stloc_0 ); // Member = new member ();
Ilist < Propertyinfo > Bloblist = New List < Propertyinfo > ();
# Region Assign values to non-blob attributes
Foreach (Propertyinfo Property In Entitytype. getproperties ())
{
Columnschema = Dataschema. getcolumnschema (property. Name );
If (Columnschema = Null )
{
Continue ;
}
If (Property. propertytype = Typeof ( Byte []) && ( ! Columnschema. istimestamp ))
{
Bloblist. Add (property );
Continue ;
}
emitsetproperty (entitytype, ilgenerator, property);
}< br> # endregion
If(Bloblist. Count> 0)
{
Ilgenerator. emit (Opcodes. ldarg_2 );
Ilgenerator. emit (Opcodes. brfalse, retlabel );
# Region Assign values to blob attributes
Foreach (PropertyinfoProperty In Bloblist)
{
Emitsetproperty (entitytype, ilgenerator, property );
}
# Endregion
}
Ilgenerator. marklabel (retlabel );
Ilgenerator. emit (Opcodes. Nop );
Ilgenerator. emit (Opcodes. Ldloc_0 );
Ilgenerator. emit (Opcodes. Ret );
Typebuilder. definemethodoverride (methodbuilder, basemethod );//Define method overload
}
4. Use the Lamda expression
If it is in. net3.5, we can use a dynamically generated Lamda expression to assign values to the entity object attribute. The key point is how to generate a dynamic delegate for assigning values. For example:
Private Action < Tentity, Object > Createfunctionofsetproperty < Tentity > (MethodinfoSetpropertymethod,TypeColumntype)
{
ParameterexpressionParamentityobj = Expression. Parameter ( Typeof (Tentity ), " Entity " );
ParameterexpressionParamproval = Expression. Parameter ( Typeof ( Object ), " Propertyval " );
UnaryexpressionParamproval2 = Expression. Convert (paramproval, columntype );
MethodcallexpressionBody = Expression. Call (paramentityobj, setpropertymethod, paramproval2 );
Expression < Action < Tentity, Object > Setpropertyexpression = Expression.
Lambda < Action < Tentity, Object > (Body, paramentityobj, paramproval );
Action<Tentity,Object>Setpropertyaction=Setpropertyexpression.Compile();
Return(Entity, propertyval)=>{Setpropertyaction (entity, propertyval );};
}
This method returns a delegate. The returned delegate receives two parameters: entity object and the attribute value to be assigned. by calling this delegate, you can assign a value to an attribute of the entity object.
Now, we have briefly introduced the four solutions. Let's make a comparison.
(1) Except for the first scheme completed during the compilation period, the last three schemes are completed during the runtime.
(2) The first solution has the highest efficiency, but the most manual operations are required (for example, the Dal layer needs to be regenerated every time the table structure is modified ). The efficiency of the second solution is the lowest, and reflection results in a very high loss of efficiency. The efficiency of the last two solutions is good (almost close to that of the first solution ).
(3) The implementation of the emit solution should be the most difficult, followed by the Lamda expression solution.
(4) Lamda expressions can be used only on. net3.5 and later platforms.
Datarabbit 3. X and earlier versions use the emit solution, and later versions 4.0 and later support both the emit solution and the Lamda expression solution (the default is the emit solution, you can modify the configuration option to use the Lamda expression scheme ).