在经典的 ADO.NET 中包含






4.91/5 (4投票s)
在经典的 ADO.NET 中实现和使用 INCLUDE 方法。
引言
EF 中的 INCLUDE
方法是一个非常有用的工具,存在于 ObjectSet
和 ObjectQuery
类中。它有助于选择您需要使用的所有相关实体,并且只需一次调用即可完成,从而获得出色的性能。
如果由于某种原因您无法在项目中使用 EF,则无法使用 INCLUDE
方法,因为在经典的 ADO.NET(非 EF)中,它不存在。
在经典的 ADO.NET 中,大多数情况下,您需要获取一个实体及其相关实体的数据,您需要多次调用数据库来选择所需的所有信息,这非常耗时。
因此,我为经典的 ADO.NET 创建了 INCLUDE
方法,以提高从相关实体获取数据时的性能。
Using the Code
要使用此方法,您只需要遵循以下规则
- 数据库中的每个表都有其
Entity
类。 - 在表中及其
Entity
类中使用相同的字段名称保持一致。 - 创建适当的存储过程以实现
INCLUDE
方法。 - 从
EntityBase
类继承以创建您的实体。 - 使用
SqlHelper
类与您的数据库交互。
EntityBase 类
此类只有一个方法来获取在子类中标记为 OUTPUT
的字段。
public abstract class EntityBase
{
protected EntityBase()
{
}
public string[] GetOutputFieldNames()
{
List<string> result = new List<string>();
PropertyInfo[] properties = this.GetType().GetProperties();
foreach (PropertyInfo property in properties)
{
FieldDirectionAttribute[] fieldDirectionsAtt =
(FieldDirectionAttribute[])property.GetCustomAttributes(
typeof(FieldDirectionAttribute), true);
if (fieldDirectionsAtt != null && fieldDirectionsAtt.Length > 0 &&
(fieldDirectionsAtt[0].Direction == ParameterDirection.InputOutput ||
fieldDirectionsAtt[0].Direction == ParameterDirection.Output))
result.Add(property.Name);
}
return result.ToArray();
}
}
SqlHelper 类
ExecuteQueryStoredProcedure
方法是一个基本方法,用于使用 ADO.NET 和存储过程从数据库填充 dataset
对象。
public sealed class SqlHelper
{
public static DataSet ExecuteQueryStoredProcedure(SqlConnection sqlConnection,
string storedProcedureName, List<SqlParameter> parameters)
{
DataSet result = new DataSet();
using (SqlCommand sqlCommand = new SqlCommand())
{
sqlCommand.Connection = sqlConnection;
sqlCommand.CommandType = CommandType.StoredProcedure;
sqlCommand.CommandText = storedProcedureName;
if (parameters != null && parameters.Count > 0)
sqlCommand.Parameters.AddRange(parameters.ToArray());
if (sqlCommand.Connection.State != ConnectionState.Open)
sqlCommand.Connection.Open();
SqlDataAdapter sqlDataAdapter = new SqlDataAdapter();
sqlDataAdapter.SelectCommand = sqlCommand;
sqlDataAdapter.Fill(result);
}
return result;
}
此 ExecuteQueryStoredProcedure
方法负责使用 INCLUDE
查询根据要填充的 Entity
类型填充 dataset
。CreateDynamicQuery
是根据 Entity
类型创建 INCLUDE
查询的方法。创建 INCLUDE
查询后,此方法调用前一个方法,使用 INCLUDE
脚本填充 dataset
,最后将主键放入 dataset
中传入的表中。
private static DataSet ExecuteQueryStoredProcedure<T>(SqlConnection sqlConnection,
string storedProcedureName, List<SqlParameter> parameters,
params string[] includePaths) where T : EntityBase
{
DataSet result = null;
List<string> includePathList;
string includeQuery = CreateDynamicQuery<T>(out includePathList, includePaths);
if (parameters == null)
parameters = new List<SqlParameter>();
parameters.Add(new SqlParameter("@Include", includeQuery));
result = ExecuteQueryStoredProcedure(sqlConnection, storedProcedureName, parameters);
if (result != null && result.Tables.Count > 0)
{
Type mainType = typeof(T);
result.Tables[0].TableName = mainType.Name;
SetPrimaryKey(result.Tables[0], mainType);
if (result.Tables.Count == (includePathList.Count + 1))
{
for (int i = includePathList.Count; i > 0; i--)
{
result.Tables[i].TableName = includePathList[i - 1];
Type entityType = GetType(mainType, includePathList[i - 1]);
SetPrimaryKey(result.Tables[i], entityType);
}
}
}
return result;
}
GetEntity
方法是此类 的核心;它从 DataRow
获取一个实体(从 dataset
获取相关实体)。
private static EntityBase GetEntity(DataRow dataRow, Type type,
DataSet dataSet, EntityBase dontIncludeEntity)
{
EntityBase result = null;
if (dataRow != null && dataRow.Table.Columns.Count > 0)
{
result = (EntityBase)Activator.CreateInstance(type);
DataColumnCollection dataColumns = dataRow.Table.Columns;
foreach (PropertyInfo property in type.GetProperties())
{
if (dataColumns.Contains(property.Name))
{ //If the property is a column in the table.
object propertyValue = null;
if (dataRow != null && dataRow[property.Name] != null)
{
propertyValue = dataRow[property.Name];
if (propertyValue != null)
{
if (propertyValue is DBNull)
propertyValue = null;
else if (property.PropertyType.IsEnum)
{
//if (Enum.IsDefined(property.PropertyType, propertyValue)
propertyValue = Enum.Parse
(property.PropertyType, propertyValue.ToString());
}
}
}
property.SetValue(result, propertyValue, null);
}
else if (dataSet != null && dataSet.Tables.Count > 0 &&
dataSet.Tables.Contains(property.Name))
{
//If the property is not a column in the table (It's an Entity property)
//Fill Entity property out using data from a related table (include).
Type entityType = property.PropertyType;
Type dontIncludeType =
(dontIncludeEntity != null) ? dontIncludeEntity.GetType() : null;
DataTable includedDataTable = dataSet.Tables[property.Name];
if (!entityType.IsArray && (dontIncludeType == null ||
dontIncludeType != null && dontIncludeType != entityType))
{
object[] foreignKeyValues = GetForeignKeyValues(dataRow, property);
DataRow includedRow = includedDataTable.Rows.Find(foreignKeyValues);
object entityValue = GetEntity(includedRow, entityType, dataSet, result);
result.GetType().GetProperty(property.Name).SetValue(result, entityValue, null);
}
else if (!entityType.IsArray)
{
result.GetType().GetProperty(property.Name).SetValue
(result, dontIncludeEntity, null);
}
}
}
}
return result;
}
实体类
这是一个 Entity
(Customer entity
) 的示例。
[EntityAttributeBase("Customers", "Id")]
public class Customer : EntityBase
{
[EnumStringType]
public enum CustomerSize
{
None,
Small,
Medium,
Large
}
[FieldDirection(ParameterDirection.InputOutput)]
public long Id { get; set; }
public string Name { get; set; }
public string AddressLine1 { get; set; }
public string AddressLine2 { get; set; }
public string City { get; set; }
public string State { get; set; }
public string ZipCode { get; set; }
public string CountryCode { get; set; }
public string ContactName { get; set; }
public string ContactEmail { get; set; }
public string ContactPhone { get; set; }
public CustomerSize Size { get; set; }
[NavigationProperty]
public Merchant[] Merchants { get; set; }
[FieldDirection(ParameterDirection.InputOutput)]
public Byte[] RowVersion { get; set; }
}
要使用此代码,您只需要创建从 EntityBase
类继承的实体并使用 SqlHelper
类。
示例
这是一个具体的例子,以便更好地理解这种方法。
这是一个假设的案例,一家公司(一家支付网关公司)的数据库结构如下图所示
在这种情况下,网关公司有 Customers
,Customers
有 Merchants
,而这些 Merchants
有 Terminals
。
一旦您获取了 Terminal
,您就可以使用 INCLUDE
功能(“Merchant.Customer
”)获取其 Merchant
和 Customer
。
以下是如何从数据访问类使用此方法的示例
public Terminal GetTerminalByCode(string code)
{
List<SqlParameter> parameters = new List<SqlParameter>();
parameters.Add(new SqlParameter("@Code", code));
return SqlHelper.GetEntity<Terminal>(_sqlConnection,
"Terminals_GetByCode", parameters, "Merchant.Customer");
}
调用 SqlHelper
类的 GetEntity
方法,您可以获取所需的实体及其依赖项。
此方法需要一个连接对象、负责从数据库获取实体的存储过程的名称、存储过程的参数以及所需的 include 路径。
以下是上一个示例中调用的存储过程的结构
CREATE PROCEDURE [dbo].[Terminals_GetByCode]
@Code nvarchar(10),
@Include nvarchar(MAX) = null
AS
BEGIN
SET NOCOUNT ON;
SELECT t.*
INTO #tmpTable
FROM Terminals t
WHERE t.Code = @Code;
SELECT tmp.* FROM #tmpTable tmp;
IF (@Include is not null) BEGIN
exec (@Include);
END
drop table #tmpTable;
END
还有一件事:存储过程需要 #tmpTable
,因为它被 @Include
变量使用。