|
|||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
Announcements
Chapters
Services
Feature Zones
|
IntroductionWhen I started working in database programming and complex business implementations, the need for a data access framework was definitive. I started to build my own classes that accessed data objects. Of course, starting from 0, the effort was bigger and I thought that a "general" data access framework could help me and my team. The short time didn't allow me to imagine such an infrastructure, for at least one database management system and one programming language. But when I started worked at the Prodigy company, the desire was materialized. I discovered a complex framework implementation which reflected many of the SQL Server database tables' features, like tables and column definitions, constraints and relationships. Of course, the data access classes that mapped database tables were auto-generated with a specific tool. But the application scope is not to explain that complex framework. Data Access classes generation is a common feature, used by many programmers and, even Microsoft has its Entrerprise Library and will implement LINQ which likely be a powerful tool. The DACBuilder intends not to implement another data access layer generator, but to show how easily you can generate classes in different programming languages, mapped on tables in different data sources. The key for this generation is XML and XSL transformations. An exampleHere is an example of code generated in VBScript, for a database management system accessed by an ODBC driver. Consider a table named "Items", with the following structure: ID – int
PK Name – varchar(255)
Price – decimal (18,2)
CategoryID – int – FK to "Categories" tableConst adUseServer = 2
Const adCmdText = 1
Const adCmdStoredProc = 4
Const adInteger = 3
Const adVarChar = 200
Const adDate = 7
Const adDecimal = 14
Const adBoolean = 11
Const adBinary = 128
Const adVariant = 12
Const adParamOutput = 2
Class DACItems
'BEGIN Private members
Private xID
Private xName
Private xPrice
Private xCategoryID
Private cnnString
Private cnn
'END Private members
Public Sub Initialize()
xID = -1
xName = ""
xPrice = -1
xCategoryID = -1
cnnString = "" 'fill in with the specified value
End Sub
Public Sub Uninitialize()
Set cnn = Nothing
End Sub
'BEGIN Properties
Public Property Let ID(vData)
xID = vData
End Property
Public Property Get ID()
ID = xID
End Property
Public Property Let Name(vData)
xName = vData
End Property
Public Property Get Name()
Name = xName
End Property
Public Property Let Price(vData)
xPrice = vData
End Property
Public Property Get Price()
Price = xPrice
End Property
Public Property Let CategoryID(vData)
xCategoryID = vData
End Property
Public Property Get CategoryID()
CategoryID = xCategoryID
End Property
'END Properties
'BEGIN Connection
Private Sub OpenConnection()
Set cnn = CreateObject("ADODB.Connection")
With cnn
.Provider = "MSDASQL"
.CursorLocation = adUseServer 'Must use Server side cursor.
.ConnectionString = cnnString
.Open
End With
End Sub
Private Sub CloseConnection()
cnn.Close
Set cnn = Nothing
End Sub
Public Property Let ConnectionString(vData)
cnnString = vData
End Property
Public Property Get ConnectionString ()
ConnectionString = cnnString
End Property
'END Connection
'BEGIN Standard methods
Public Function Add()
Dim bRet
OpenConnection
Dim cmd
Set cmd = CreateObject("ADODB.Command")
Set cmd.ActiveConnection = cnn
cmd.CommandType = adCmdText
cmd.CommandText = "INSERT INTO Items VALUES(?, ?, ?, ?)"
Dim param
Set param = cmd.CreateParameter("", adInteger)
param.Value = xID
cmd.Parameters.Append param
Set param = cmd.CreateParameter("", adVarChar, 255)
param.Value = xName
cmd.Parameters.Append param
Set param = cmd.CreateParameter("", adDecimal, 18, 2)
param.Value = xPrice
cmd.Parameters.Append param
Set param = cmd.CreateParameter("", adInteger)
param.Value = xCategoryID
cmd.Parameters.Append param
Dim result
cmd.Execute result
CloseConnection
bRet = result > 0
Set cmd = Nothing
Add = bRet
End Function
Public Function Edit()
Dim bRet
OpenConnection
Dim cmd
Set cmd = CreateObject("ADODB.Command")
Set cmd.ActiveConnection = cnn
cmd.CommandType = adCmdText
cmd.CommandText = "UPDATE Items SET Name = ?," & _
" Price = ?, CategoryID = ? WHERE ID = ?"
Dim param
Set param = cmd.CreateParameter("", adVarChar, 255)
param.Value = xName
cmd.Parameters.Append param
Set param = cmd.CreateParameter("", adDecimal, 18, 2)
param.Value = xPrice
cmd.Parameters.Append param
Set param = cmd.CreateParameter("", adInteger)
param.Value = xCategoryID
cmd.Parameters.Append param
Set param = cmd.CreateParameter("", adInteger)
param.Value = xID
cmd.Parameters.Append param
Dim result
cmd.Execute result
CloseConnection
bRet = result > 0
Set cmd = Nothing
Edit = bRet
End Function
Public Function Delete()
Dim bRet
OpenConnection
Dim cmd
Set cmd = CreateObject("ADODB.Command")
Set cmd.ActiveConnection = cnn
cmd.CommandType = adCmdText
cmd.CommandText = "DELETE FROM Items WHERE ID = ?"
Dim param
Set param = cmd.CreateParameter("@ID", adInteger)
param.Value = xID
cmd.Parameters.Append param
Dim result
cmd.Execute result
CloseConnection
bRet = result > 0
Set cmd = Nothing
Delete = bRet
End Function
Public Function Read()
Dim bRet
Dim rs
Set rs = CreateObject("ADODB.Recordset")
OpenConnection
Dim cmd
Set cmd = CreateObject("ADODB.Command")
cmd.CommandType = adCmdText
cmd.CommandText = "SELECT * FROM Items WHERE ID = ?"
Dim param
Set param = cmd.CreateParameter(", adInteger)
param.Value = xID
cmd.Parameters.Append param
Set rs = cmd.Execute
If Not(rs.EOF) Then
xID = CInt(rs("ID"))
xName = CStr(rs("Name"))
xPrice = CDbl(rs("Price"))
xCategoryID = CInt(rs("CategoryID"))
bRet = True
End If
CloseConnection
bRet = False
Set rs = Nothing
Set cmd = Nothing
Read = bRet
End Function
Public Function ReadCategoriesItems()
Dim rs
Set rs = CreateObject("ADODB.Recordset")
OpenConnection
Dim cmd
Set cmd = CreateObject("ADODB.Command")
Set cmd.ActiveConnection = cnn
cmd.CommandType = adCmdText
cmd.CommandText = "SELECT * FROM Items WHERE CategoryID = ?"
Dim param
Set param = cmd.CreateParameter("", adInteger)
param.Value = xCategoryID
cmd.Parameters.Append param
Set rs = cmd.Execute
Set cmd = Nothing
ReadCategoriesItems = rs
End Function
'END Standard Methods
'BEGIN Other DAC logic
'END Other DAC logic
End Class
You may see that the auto-generated class has some properties, mapped on columns that belong to the Items table, and some methods ( DB Systems, table structure, and XML outputThe code generation is made starting just from a table structure. If you define the appropriate fields structure, tables, and the relationships between them, the DACBuilder job is simple and powerful. So, the starting point is the database. The tables structure can be reflected in objects. The DACBuilder application brings the structure of a specified table or view and, if applicable, the structure of a specified stored procedure or a function (routine). It filters on tables or views, for persistent objects, and on stored procedures and functions for routines. There are multiple choices for code generation:
The code is generated in a rich text edit box and not directly in a file on disk, but you can save the file with the specified extension, depending on what it contains (SQL script or code). It has a simple color syntax highlighting based on the current generated content language. For .NET languages, it has a compiling option and an assembly generation command (with run-time reference of the required assemblies), of course with error management. Depending on what database system you choose, the application reads the tables contained by the specified database. The implementation is available just for SQL Server, Access, and MySQL, and for every system there exists specific methods to reach the metadata information. In every situation, the output is an XML document which is transformed using a template for every chosen action. The XML structure is a simple one, with two levels (table information – level 1, columns information – level 2). The XML for the table in the given example is: <table name="Items" friendly_name="Items"
base="" namespace="" abbreviation="">
<column column_name="ID" column_friendly_name="ID"
ordinal_position="1" is_nullable="No" data_type="int"
numeric_precision="10" numeric_precision_radix="10"
numeric_scale="0" identity="0"
constraint_type="PRIMARY KEY" referenced_table="" />
<column column_name="Name" column_friendly_name="Name"
ordinal_position="2" is_nullable="No" data_type="varchar"
character_maximum_length="255" character_octet_length="255"
constraint_type="" referenced_table="" />
<column column_name="Price" column_friendly_name="Price"
ordinal_position="3" is_nullable="No" data_type="decimal"
numeric_precision="18" numeric_precision_radix="10"
numeric_scale="2" constraint_type="" referenced_table="" />
<column column_name="CategoryID" column_friendly_name="CategoryID"
ordinal_position="4" is_nullable="No" data_type="int"
numeric_precision="10" numeric_precision_radix="10"
numeric_scale="0" constraint_type="FOREIGN KEY"
referenced_table="Categories" />
</table>
The meaning for the attributes contained in the XML nodes is explained in the following table:
For SQL Server, the procedure which returns metadata information is called For MS Access systems, the method to get metadata information is performed using OLEDB features, as the For MySQL systems, the analysis is performed in a specific way, using the For both MS Access and MySQL systems, and any other new system which could be implemented, the XML output must be transformed in the DACBuilder XML format (which was explained before). The transformation is performed using a specific template (msaccess_2_sql.xsl or mysql_2_sql.xsl). The object responsible for obtaining information about metadata in the database systems is called public enum DBTypes
{
SQL = 0
, ORACLE = 1
, MSACCESS = 2
, MYSQL = 3
}
The Oracle system for An example of how to get information about a table in a database of a specific system is provided in the DBHelper dbH = new DBHelper(ConnectionString, dbType);
XmlDocument doc = dbH.GetTableStructure(cboTables.SelectedValue.ToString());
switch(dbType)
{
case DBTypes.MYSQL:
CommonGenerator mysql_DACGen =
new CommonGenerator(cboTables.SelectedValue.ToString(), doc);
mysql_DACGen.Generate(templatesPath + "mysql_2_sql.xsl");
doc.LoadXml(mysql_DACGen.CommonCode);
break;
case DBTypes.MSACCESS:
CommonGenerator msaccess_DACGen =
new CommonGenerator(cboTables.SelectedValue.ToString(), doc);
msaccess_DACGen.Generate(templatesPath + "msaccess_2_sql.xsl");
doc.LoadXml(msaccess_DACGen.CommonCode);
break;
default:
break;
}
The XPathNavigator nav = StructureDOM.DocumentElement.CreateNavigator();
XmlDocument xsldoc = new XmlDocument();
xsldoc.Load(xslTemplatePath);
XmlNamespaceManager nsmgr = new XmlNamespaceManager(xsldoc.NameTable);
nsmgr.AddNamespace("xsl", "http://www.w3.org/1999/XSL/Transform");
XmlUrlResolver resolver = new XmlUrlResolver();
XslTransform trans = new XslTransform();
trans.Load(xsldoc, resolver, this.GetType().Assembly.Evidence);
TextWriter writer = new StringWriter();
XmlTextWriter xmlWriter = new XmlTextWriter(writer);
xmlWriter.Formatting = Formatting.Indented;
trans.Transform(nav, null, xmlWriter, null);
string sOutput = writer.ToString();
Routines handling is available only for the SQL Server system. The routines list is brought using the same The XML output obtained by gathering metadata information is then added to a Template parametersRegarding objects generation, the technique is the same for all objects. Starting from a table or a routine structure, applying different templates, different objects are generated, either as a SQL statement (stored procedure), as a class in a specified programming language, or as a schema or HTML code. Because the XSL templates can be customized using a
Having a template with this collection of parameters ( <xsl:param name="auto_increment" select="boolean(/table/column[@identity=1])"/>
<xsl:param name="pk">PRIMARY KEY</xsl:param>
<xsl:param name="fk">FOREIGN KEY</xsl:param>
<xsl:param name="getID">0</xsl:param>
<xsl:param name="insert_sp" select="concat(/table/@friendly_name, '_INSERT')"/>
<xsl:param name="update_sp" select="concat(/table/@friendly_name, '_UPDATE')"/>
<xsl:param name="delete_sp" select="concat(/table/@friendly_name, '_DELETE')"/>
<xsl:param name="select_sp" select="concat(/table/@friendly_name, '_SELECT')"/>
<xsl:param name="use_noname_parameters">0</xsl:param>
<xsl:param name="use_DataAccessComponent">0</xsl:param>
<xsl:param name="use_CustomFields">0</xsl:param>
The configuration of a form instance will be:
Depending on the parameter values, the generated objects can substantially differ, which gives applications a powerful extensibility. The form is called xslParametersDoc = new XmlDocument();
xslParametersDoc.Load(xslFileName);
XmlNamespaceManager nsmgr = new
XmlNamespaceManager(xslParametersDoc.NameTable);
nsmgr.AddNamespace("xsl",
"http://www.w3.org/1999/XSL/Transform");
XmlNodeList parameters =
xslParametersDoc.SelectNodes("/xsl:styl" +
"esheet/xsl:param", nsmgr);
DataTable dtParameters = new DataTable();
dtParameters.Columns.Add(new
DataColumn("name", typeof(string)));
dtParameters.Columns.Add(new
DataColumn("value", typeof(string)));
dtParameters.Columns.Add(new
DataColumn("select", typeof(bool)));
The form is instantiated with the frmParameters frm = new frmParameters(dtParameters);
...
dtParameters.DefaultView.AllowNew = false;
dgParameters.DataSource = dtParameters.DefaultView;
Only the frm.SelectParameters += new
SelectParametersEventHandler(frm_SelectParameters);
The delegate public delegate void
SelectParametersEventHandler(object sender,
SelectParametersEventArgs e);
The class XmlNamespaceManager nsmgr = new
XmlNamespaceManager(xslParametersDoc.NameTable);
nsmgr.AddNamespace("xsl",
"http://www.w3.org/1999/XSL/Transform");
XmlNode node;
XmlElement elem;
bool bIsSelect;
foreach(DataRow dr in e.Parameters.Rows)
{
node = xslParametersDoc.SelectSingleNode("/xsl:stylesh" +
"eet/xsl:param[@name='" +
dr["name"].ToString() + "']", nsmgr);
if(node != null)
{
elem = (XmlElement)node;
bIsSelect = (bool)dr["select"];
if(bIsSelect)
elem.SetAttribute("select", dr["value"].ToString());
else
node.InnerText = dr["value"].ToString();
}
}
If the user press OK on DAC TransformationNext, I will try to explain a DAC transformation. Using the table in the example at the beginning of the article, we will generate a simple data access container (DAC) with access on a SQL Server system, and as programming language - C#. First, we choose the table Items from the tables combo box. Next, in the structure menu, choose the Get structure command. This will automatically fill the
We can fill the values for
The main stylesheet template is: <xsl:template match="/">using System;
using System.Data;
using System.Data.SqlClient;
using System.Configuration;
<xsl:if test="$use_CustomFields=1">
using System.Reflection;
using System.IO;
using DotNetScripting;
using CustomFields;
</xsl:if>
namespace <xsl:value-of select="/table/@namespace"/>.DAC
{
public class <xsl:value-of select="/table/@abbreviation"/>
DAC<xsl:value-of select="/table/@friendly_name"/>
<xsl:if test="$use_DataAccessComponent=1">
: DataAccessContainer</xsl:if>
{
public <xsl:value-of
select="/table/@abbreviation"/>DAC
<xsl:value-of select="/table/@friendly_name"/>()
{
<xsl:if test="$use_DataAccessComponent=1">
this.TableName = "<xsl:value-of select="/table/@name"/>";
this.AutoIncrement =
<xsl:choose><xsl:when test="$auto_increment=1">
true</xsl:when><xsl:otherwise>false
</xsl:otherwise></xsl:choose>;
<xsl:if test="$use_CustomFields=1">
LoadCustomFields();
</xsl:if>
</xsl:if>
}
#region Private members
<xsl:apply-templates select="/table/column" mode="member"/>
private DataRow x<xsl:value-of select="/table/@friendly_name"/>Row;
#endregion
#region Properties
<xsl:apply-templates select="/table/column" mode="property"/>
public DataRow <xsl:value-of select="/table/@friendly_name"/>Row
{
get
{
return x<xsl:value-of select="/table/@friendly_name"/>Row;
}
}
#endregion
<xsl:if test="$use_DataAccessComponent!=1">
#region Connection
<xsl:call-template name="connection"/>
#endregion
</xsl:if>
#region Standard methods
<xsl:choose>
<xsl:when test="$use_DataAccessComponent!=1">
<xsl:call-template name="methods"/>
</xsl:when>
<xsl:when test="$use_DataAccessComponent=1">
<xsl:call-template name="methods_DataAccessComponent"/>
</xsl:when>
</xsl:choose>
#endregion
#region Other DAC logic
#endregion
<xsl:if test="$use_CustomFields=1">
<xsl:call-template name="CustomFields"/>
</xsl:if>
}
}
</xsl:template>
You can see how by filtering using the parameter values, the result is different. Other templates are also used, either by calling, or by applying. An example of calling a template is used for connection, when the <xsl:if test="$use_DataAccessComponent!=1">
#region Connection
<xsl:call-template name="connection"/>
#endregion
</xsl:if>
The template called <xsl:apply-templates select="/table/column" mode="member"/>
<xsl:apply-templates select="/table/column" mode="property"/>
The The classes code is available on the .NET DAC menu, Data Access Container or Data Access Component commands. You have to compile and create an assembly with these two classes (the .NET DAC->Compile and the .NET DAC->Create assembly methods). The generated assembly must be referenced in order to compile a generated DAC object in the In this way, the framework can be customized with additional features, because it is recommended to have a balanced approach of the DAC framework and the database features to get the best performance in an application. The DB routine executionThe DACBuilder application provides functionality for generating .NET code that allows SQL routine or query execution. It builds a class called Compiling codeThe .NET generated DAC objects code can be compiled and saved as binary in assemblies. The compiling and assembly generation is performed by objects residing in the The second button loads an assembly located on disk, while the third button removes it from the list of referenced assemblies. Resources generationThe DACBuilder application is able to generate resources in the managed ResX format. Resources generation is done as VS does, using a native data grid which has an doc = new XmlDataDocument();
doc.DataSet.ReadXmlSchema(templatesPath + "template.resx");
doc.Load(fileName);
doc.DataSet.Tables[0].RowChanged += new
DataRowChangeEventHandler(frmResources_RowChanged);
doc.DataSet.Tables[0].RowDeleted += new
DataRowChangeEventHandler(frmResources_RowDeleted);
dgResources.DataSource = doc.DataSet.Tables[0].DefaultView;
XML Schema generationThe XML schema generation is performed in a similar way, using a specific stylesheet (dataset_schema.xsl). The template contains specific XML schema elements, and the output is done by using the <xsl:output method="xml" omit-xml-declaration="yes"/>
SQL scripts generationThe SQL stored procedures are generated in the same way, using XSL stylesheets with different parameters. The stored procedures which can be generated are for insert, update, and delete, for selecting a single record, and for search using default parameters. Other features include table creation, and <xsl:template match="column" mode="parameter_default">
<xsl:if test="position()>1">,
</xsl:if> @<xsl:value-of select="@column_name"/>
[<xsl:value-of select="@data_type"/>]
<xsl:call-template name="data-length-precision-scale">
<xsl:with-param name="data-type" select="@data_type"/>
<xsl:with-param name="length" select="@character_maximum_length"/>
<xsl:with-param name="precision" select="@numeric_precision"/>
<xsl:with-param name="scale" select="@numeric_scale"/>
</xsl:call-template> = <xsl:call-template
name="select_primitive_type_default_SQL">
<xsl:with-param name="data_type" select="@data_type"/>
</xsl:call-template>
</xsl:template>
The procedure builds a <xsl:template match="column" mode="column_filter">
IF @<xsl:value-of select="@column_name"/>
<xsl:call-template
name="select_primitive_type_default_comparison_SQL">
<xsl:with-param name="data_type" select="@data_type"/>
</xsl:call-template>
BEGIN
IF @filter = N''
SET @filter = N' <xsl:value-of select="@column_name"/>
<xsl:call-template name="select_primitive_type_comparison_SQL">
<xsl:with-param name="data_type" select="@data_type"/>
<xsl:with-param name="object"
select="concat('@', @column_name)"/>
</xsl:call-template>
'
ELSE
SET @filter = N' AND <xsl:value-of select="@column_name"/>
<xsl:call-template
name="select_primitive_type_comparison_SQL">
<xsl:with-param name="data_type" select="@data_type"/>
<xsl:with-param name="object"
select="concat('@', @column_name)"/>
</xsl:call-template>
'
END
</xsl:template>
The procedure will build the SQL string, and append the filter if there is one (at least one parameter has a value different than its default value), and the string will serve as a parameter for the DECLARE @filter NVARCHAR(4000)
SET @filter = N''
<xsl:apply-templates select="/table/column" mode="column_filter"/>
IF @filter != N''
SET @stmt = @stmt + N'
WHERE
' + @filter
PRINT @stmt
EXEC sp_executesql @stmt
, N'
<xsl:apply-templates
select="/table/column" mode="parameter"/>
'
,
<xsl:apply-templates select="/table/column"
mode="parameter_itself_value"/>
The output generated for table Items will be: IF EXISTS(SELECT * FROM INFORMATION_SCHEMA.ROUTINES
WHERE ROUTINE_NAME = 'Items_SEARCH')
BEGIN
DROP PROCEDURE [Items_SEARCH]
END
GO
CREATE PROCEDURE [Items_SEARCH]
(
@ID [int] = -1,
@Name [varchar](255) = NULL,
@Price [decimal](18, 2) = -1,
@CategoryID [int] = -1
)
AS
DECLARE @stmt NVARCHAR(4000)
SET @stmt = N'
SELECT
[ID] AS [ID]
, [Name] AS [Name]
, [Price] AS [Price]
, [CategoryID] AS [CategoryID]
FROM [Items] AS [Items]
'
DECLARE @filter NVARCHAR(4000)
SET @filter = N''
IF @ID != -1
BEGIN
IF @filter = N''
SET @filter = N' ID = @ID
'
ELSE
SET @filter = N' AND ID = @ID
'
END
IF @Name IS NOT NULL
BEGIN
IF @filter = N''
SET @filter = N' Name LIKE ''%'' + @Name + ''%''
'
ELSE
SET @filter = N' AND Name LIKE ''%'' + @Name + ''%''
'
END
IF @Price != -1
BEGIN
IF @filter = N''
SET @filter = N' Price = @Price
'
ELSE
SET @filter = N' AND Price = @Price
'
END
IF @CategoryID != -1
BEGIN
IF @filter = N''
SET @filter = N' CategoryID = @CategoryID
'
ELSE
SET @filter = N' AND CategoryID = @CategoryID
'
END
IF @filter != N''
SET @stmt = @stmt + N'
WHERE
' + @filter
PRINT @stmt
EXEC sp_executesql @stmt
, N'
@ID [int],
@Name [varchar](255),
@Price [decimal](18, 2),
@CategoryID [int]
'
,
@ID = @ID, @Name = @Name, @Price = @Price, @CategoryID = @CategoryID
GO
Other SQL features are "meta" scripts for the UI generationThe UI features are useful to generate very simple interfaces for the DAC object. It provides both UI characteristics for visual objects such Windows Forms, web forms, or HTML pages, and class code with methods using the DAC object. There are stylesheets for each UI platform and .NET language (C# and VB.NET). The commands UI->Simple DAC Methods (Web and Windows) generates code which can be saved, compiled, and included in an assembly. Custom fields implementationThe Custom Fields implementation is a technique destined to enhance the functionality for a base table and its DAC object. This implementation was started at Prodigy, and it was customized on the existing data access framework. It allows end users to add new fields to any table in the database. The implementation is available only for SQL Server. The "meta" objects you need to work with custom fields are available in the Custom Fields menu DB Entities command (for SQL statements) and DAC Entities (for C# or VB.NET). DAC Entities must be compiled and added as a reference to the generated Custom Fields DAC objects. The DB implementation is simple, the system manages tables that has custom field properties, the primary key for this table (the system requires that the table must have a primary key only on a single column), and custom properties. These objects define the structure of the custom fields. The table CustomFieldsProperty contains the following structure:
The values for custom properties are stored in tables specific to the chosen type. The currently implemented ones are for
The custom fields management can be done by a non-programmer user, by adding, modifying, and deleting properties in the
For the table Items, we have defined four custom fields in this manner:
Using the Generate View command, a view with all the base columns and/or custom fields is generated. The view name is LEFT JOIN (
SELECT
CustomFieldsValuesString.FKParent AS ID
, CustomFieldsValuesString.PropertyValue AS [MeasureUnit]
FROM CustomFieldsValuesString
WHERE CustomFieldsValuesString.FKProperty = 5
) AS CustomFieldsValuesString_5 ON Items. = CustomFieldsValuesString_5.ID
The view was created using the DACBuilder application. Several SQL
Using the Generate Read SP command, a WHERE Items.ID = @PKObjectID
The Generate Class command provides the most interesting Custom Fields feature. It generates the custom fields DAC class for the selected table. Because this class is generated at run-time and have to be handled by a binary object, the properties and methods have to be homogenized. The class will have members and properties for every custom field defined. The properties type is In the DAC object generation, if you set the #region Custom Fields
private bool xHasCustomFields = false;
public bool HasCustomFields
{
get
{
return xHasCustomFields;
}
}
private string execAsmbDir;
private string customFieldsAssemblyLocation;
private Assembly customFieldsAssembly;
private string customFieldsObjectName;
private object xCustomFields = null;
public object CustomFields
{
get
{
return xCustomFields;
}
set
{
xCustomFields = value;
}
}
private void LoadCustomFields()
{
Assembly execAsmb = Assembly.GetExecutingAssembly();
execAsmbDir = execAsmb.Location;
execAsmbDir =
execAsmbDir.Substring(0 | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||