Mapping common rules
All data objects must to be signed with marker interface (empty interface) IDataObject. All objects describes subqueries must to be signed with marker interface ISqlObject.
Tip
Ability of defining mapping to marker interface is key feature of XData allowed to get flexible and natural objects deriving hierarchy with full featured and handy methods to work with it (using extension methods).
Mapping rules can be declared statically with attributes of class and its properties (see static mapping), and dynamically using LINQ style expression (see dynamic mapping).
Mapping inheritance
Complex projects can contain many reusable parts. Including query parts (tables, subqueries, stored procedure calls). Sometimes these reused parts correspond to the inheritance hierarchy, sometimes they don’t.
XData supports the inheritance of parts of the mapping of data objects both within the hierarchy of inheritance of objects reflected to the database and the construction of its hierarchy for the reuse of parts of mapping.
Only mapping description is depends on static or dynamic method of declaration.
Common types properties
Common types properties (string, bool, int...) including nullable (int?, bool?...), and enum properties is declared as common class properties. Data access to this properties is usual. Only mapping description is depends on static or dynamic method of declaration.
Binary properties
To work with large binary object (BLOB) using lazy coupling XData represent property type Lob. This type has followed...
properties:
- byte[] Value - binary object value
- (read-only) bool Assigned - check object has value (including empty value) or NULL
methods:
- GetSize() - returns size of binary object
- (extension) Modify(Action<byte[]> action) - helper method to simplify changing of binary object value
operation:
- += - "syntax sugar", _data.SomeBlob += _someVariable identical with _data.SomeBlob.Value = _someVariable.
Examples:
newInvoice.Scan += _image;
...
if(newInvoice.Scan.Assigned)
_size = newInvoice.Scan.GetSize();
...
invoice.Scan.Modify(x => x = new byte[0]);
Xml properties
To work with Xml using lazy coupling XData represent property type Xml. This type has followed...
properties:
- XDocument Document - get or set Xml value
- (read-only) bool Assigned - check Xml field for NULL value
methods:
- Extract(string path, params KeyValuePair<string, string>[] namespaces) - returning result of XPath expression path using namespaces (pair: namespace - URL)
- (extension) Modify(Action
action) - helper method to change Xml value
operations:
- += - "syntax sugar", _data.SomeXml += _someVariable identical with _data.SomeXml.Document = _someVariable.
Example:
newInvoice.Source += new XDocument(new XElement("invoice",
new XAttribute("number", number), new XAttribute("state", newInvoice.DocStateCode)));
...
dataScope.GetRepository<Invoice>(Owner, context: Context)
.First(x => x.Source.Extract("(/invoice/@state)[1]") == "ACTIVE").DocNumb
...
invoice.Source.Modify(x => x = new XDocument());
Link properties
To work with external links to data objects outside current object without operating surogate keys, XData represent special property type - Link<TVal,TSrc>, where TVal - type of property for preview, TSrc - type of external object. For type Link is defined...
properties:
- TVal Value - this property to get preview of linked object
- TSrc Source - this property can be used to set linked object
- (read-only) bool Assigned - check link value for NULL value
operations:
- += - "syntax sugar", _data.SomeLink += _someDictionaryObject identical with _data.SomeLink.Source = _someDictionaryObject.
newInvoice.DocState += dataScope.GetDictionaryValue<DocState>(x => x.Code == "CREATED");
To make link properties work correct, at mapping level need to be defined pairs of properties source and current objects, that values will be copied from source object to current when link is applied. Link description is deferent for static and dynamic methods of mapping.
Read only properties
Read only properties XData require to perform as in example below:
public string DocStateCode { get { return this.GetProperty(x => x.DocStateCode); } }
Data access is provided through extension method GetProperty, but property is read only.
Child repositories references
Detail related repositories XData require to perform as:
public IRepository<InvoiceSpec> Spec {
get {
return this.GetRepository().GetChild<InvoiceSpec>();
}
}
No additional mapping description are needed. Only external reference is required.
Tip
To use hierarchical objects and XData Unit of Work realization (WorkSet), child repositories references are required.
Hidden properties
Some columns in database is needed to full description of mapping, but not required to business logic. This columns is declared as part of mapping named Column is selected from database, but not mapped on data object properties.
For example - XData can operate primary (PK) and foreign (FK) keys of business objects and work with master-detail references between them, but mapping to property of external references is not required to operate with key.
Second case - optimistic concurrency resolving (see concurrency resolving) required concurrency token field, but this field is not required to business logic.
Third example - when data is grouped, needed to declare some field to link with external reference, but in this case possibly we can`t include this field in SELECT expression because of GROUP BY syntax. This case is resolved by Hidden property of column declaration. When Hidden is true - column will be not included into SELECT expression of resulted SQL query.
Hidden properties description is various to static and dynamic mapping declaration methods.
Master detail relations
External references is the base element of master-detail references definition in XData. It describes link rules between related detail entities from master entities. XData support multi master and multi detail relation (many-to-many). And it's possible to define multiple relations between every two data objects.
External references is the declarations of possible master-detail relations between data objects. Each of external reference is describe master property (or column) to detail filter relation, used to filter detail collections data when master current object is set.
Tip
External references applied to manually attached detail data sources only.
External references definition is differs for static and dynamic methods of mapping.
Reference rules is defined at mapping level, and when detail object will be attached using GetChild method, that rules will be applied.
To fill child repository data in some unmapped array property (when the reference has type "one-to-many") can be used special overload of GetChild method. And when reference has type "one-to-one" can be used GetChild overload. That properties will be filled by child data automatically during data acquire when child repositories has been connected using that overloads of method.
var rep = dataScope.GetRepository<Invoice>();
var child = rep.GetChild(z => z.InvoiceSpec);
Warning
Data hierarchy can be acquired by that way has not limited deep level, but important keep in mind the price of that is performance. Use child repository data loading into unmapped properties when needed only!
To break master-detail reference call DetachChild method. When data scope or one of referenced repositories will be disposed - reference is beaked automatically.
Detail object reference filter value is set when SetCurrent extension method is called for master object.
if (newInvoice.Submit())
{
newInvoice.SetCurrent();
return true;
}
Master-detail linked repositories can be used for cascade delete operation over multiple related data objects:
newInvoice.SetDeleted(true);
newInvoice.Submit();
or cascade delete applied for filtered subset of repository objects and their detail objects:
invoices.Clear(x => x.DocStateCode == "REJECTED");
Tree
XData can use CTE (common table expression) as part of repository data source definition. Supported as plain (WITH), as recursive (WITH RECURSIVE) expressions.
CTE is defined in mapping differently for static and dynamic mapping paradigm, but it can be used with similar way: defined one or more tables with unique aliases and name equals CTE name.
Recursive CTE can be used as independent data source too. In this case CTE name must to be declared as base table of repository.
SQL functions & procedures
Stored procedures and functions can be used as data source of mapping (if used DBMS and ADO .Net provider is support this).
Important
At this moment we has various limitations of using SQL procedures as on DBMS SQL dialect level, as on ADO .Net provider realization level. Please, choose DBMS and ADO .Net provider with keep in mind limitations they have. XData support many procedure mapping styles and call conventions, but can not garant all of them is supported by DBMS and ADO .Net provider You choose.
XData has support followed SQL procedure mapping styles and call conventions and it combinations:
- Using procedure result set as data source
- Using procedure with multiple result sets
- Using out parameters
- Using return value of stored procedure
- Using simple type array as parameter
- Using cursor as parameter
- Using UTD (user defined type) as parameter
- Using table valued function as data source
- Using scalar function as single property source
Warning
Class mapped to UDT must support native XML serialization!
All this mapping features described as for static as for dynamic mapping paradigm.