我已经开始在Sql Server 2k8中使用表值参数进行批处理操作。我非常喜欢这个功能,感觉它是在漫长的等待之后才出现的。
然而,为了从。net代码传递TVP,要构造SQLMetaData[]然后在循环中填充值,涉及的工作太多了。
你如何避免在你的。net代码中保持同步的Sql Server和SQLMetaData[]对象中的用户定义类型的维护?当我在SQL中更改类型定义时,很难知道我在。net的大量代码中在哪里使用了该类型。. net Reflection是否可以通过提供用户定义类型的名称来帮助程序员构造SQLMetadata,并通过提供对象数组来帮助填充数据?
考虑这个例子:
SqlMetaData[] tvp_TradingAllocationRule = new SqlMetaData[13];
try
{
tvp_TradingAllocationRule[0] = new SqlMetaData("ID", SqlDbType.UniqueIdentifier);
tvp_TradingAllocationRule[1] = new SqlMetaData("Name", SqlDbType.VarChar, 255);
tvp_TradingAllocationRule[2] = new SqlMetaData("Description", SqlDbType.VarChar, -1);
tvp_TradingAllocationRule[3] = new SqlMetaData("Enabled", SqlDbType.Bit);
tvp_TradingAllocationRule[4] = new SqlMetaData("Category", SqlDbType.VarChar, 255);
tvp_TradingAllocationRule[5] = new SqlMetaData("Custom1", SqlDbType.VarChar, 255);
tvp_TradingAllocationRule[6] = new SqlMetaData("Custom2", SqlDbType.VarChar, 255);
tvp_TradingAllocationRule[7] = new SqlMetaData("Custom3", SqlDbType.VarChar, 255);
tvp_TradingAllocationRule[8] = new SqlMetaData("CreatedBy", SqlDbType.VarChar, 20);
tvp_TradingAllocationRule[9] = new SqlMetaData("CreatedTS", SqlDbType.DateTime);
tvp_TradingAllocationRule[10] = new SqlMetaData("ModifiedBy", SqlDbType.VarChar, 20);
tvp_TradingAllocationRule[11] = new SqlMetaData("ModifiedTS", SqlDbType.DateTime);
tvp_TradingAllocationRule[12] = new SqlMetaData("IsFactory", SqlDbType.Bit);
}
catch (Exception ex)
{
throw new Exception("Error Defining the tvp_TradingActionCondition in .Net" + ex.Message);
}
foreach (TradingRuleMetadata ruleMetadata in updatedRules)
{
SqlDataRecord tradingAllocationRule = new SqlDataRecord(tvp_TradingAllocationRule);
try
{
tradingAllocationRule.SetGuid(0, ruleMetadata.ID);
tradingAllocationRule.SetString(1, ruleMetadata.Name);
tradingAllocationRule.SetString(2, ruleMetadata.Description);
tradingAllocationRule.SetBoolean(3, ruleMetadata.Enabled);
tradingAllocationRule.SetString(4, ruleMetadata.Category);
tradingAllocationRule.SetString(5, ruleMetadata.Custom1);
tradingAllocationRule.SetString(6, ruleMetadata.Custom2);
tradingAllocationRule.SetString(7, ruleMetadata.Custom3);
tradingAllocationRule.SetString(8, ruleMetadata.CreatedBy);
tradingAllocationRule.SetDateTime(9, ruleMetadata.CreatedDate);
tradingAllocationRule.SetString(10, ruleMetadata.ModifiedBy);
tradingAllocationRule.SetDateTime(11, ruleMetadata.ModifiedDate);
tradingAllocationRule.SetBoolean(12, ruleMetadata.IsFactory);
tvp_TradingAllocationRuleRecords.Add(tradingAllocationRule);
}
catch (Exception ex)
{
}
}
现在,如果您的表有100列,想象一下您的代码:
您可以使用反射来做到这一点。首先,必须有一种方法来覆盖名称和长度的默认值。为此,定义Attribute
s:
[AttributeUsage(AttributeTargets.Property, AllowMultiple = false)]
class LengthAttribute : Attribute
{
private readonly int m_length;
public int Length
{
get { return m_length; }
}
public LengthAttribute(int length)
{
m_length = length;
}
}
[AttributeUsage(AttributeTargets.Property, AllowMultiple = false)]
class ColumnNameAttribute : Attribute
{
private readonly string m_name;
public string Name
{
get { return m_name; }
}
public ColumnNameAttribute(string name)
{
m_name = name;
}
}
用在你的字体上:
class TradingRuleMetadata
{
public Guid ID { get; set; }
public string Name { get; set; }
[Length(-1)]
public string Description { get; set; }
public bool Enabled { get; set; }
public string Category { get; set; }
public string Custom1 { get; set; }
public string Custom2 { get; set; }
public string Custom3 { get; set; }
[Length(20)]
public string CreatedBy { get; set; }
[ColumnName("CreatedTS")]
public DateTime CreatedDate { get; set; }
[Length(20)]
public string ModifiedBy { get; set; }
[ColumnName("ModifiedTS")]
public DateTime ModifiedDate { get; set; }
public bool IsFactory { get; set; }
}
然后您可以创建一个方法,将此类型的集合映射到SqlDataRecord
的集合:
private static readonly Dictionary<Type, SqlDbType> SqlDbTypes =
new Dictionary<Type, SqlDbType>
{
{ typeof(Guid), SqlDbType.UniqueIdentifier },
{ typeof(string), SqlDbType.VarChar },
{ typeof(bool), SqlDbType.Bit },
{ typeof(DateTime), SqlDbType.DateTime }
};
static IList<SqlDataRecord> GetDataRecords<T>(IEnumerable<T> data)
{
Type type = typeof(T);
var properties = type.GetProperties();
SqlMetaData[] metaData = new SqlMetaData[properties.Length];
try
{
for (int i = 0; i < properties.Length; i++)
{
var property = properties[i];
string name = property.Name;
var columnNameAttribute = GetAttribute<ColumnNameAttribute>(property);
if (columnNameAttribute != null)
name = columnNameAttribute.Name;
var dbType = SqlDbTypes[property.PropertyType];
if (dbType == SqlDbType.VarChar)
{
int length = 255;
var lengthAttribute = GetAttribute<LengthAttribute>(property);
if (lengthAttribute != null)
length = lengthAttribute.Length;
metaData[i] = new SqlMetaData(name, dbType, length);
}
else
metaData[i] = new SqlMetaData(name, dbType);
}
}
catch (Exception ex)
{
throw new Exception();
}
var records = new List<SqlDataRecord>();
foreach (T item in data)
{
SqlDataRecord record = new SqlDataRecord(metaData);
try
{
var values = properties.Select(p => p.GetValue(item, null)).ToArray();
record.SetValues(values);
records.Add(record);
}
catch (Exception ex)
{
}
}
return records;
}
static T GetAttribute<T>(PropertyInfo property)
{
return (T)property.GetCustomAttributes(typeof(T), true).SingleOrDefault();
}
这段代码使用了相当多的反射,因此对您来说可能太慢了。如果是这种情况,您将需要实现某种缓存。这样做的一种方法是创建一个Expression
来完成所有这些工作,然后将其编译成一个委托()。Net 4,因为您需要BlockExpression
)。
同样,您的实际需求可能更复杂,因为您可能需要忽略某些属性或类似的东西。但这应该很容易添加。
问题中没有足够的代码示例,但是对于这样的事情,我会做一些事情,例如编写一个单独的. net可执行文件来读取SQL元数据并为每个UDT生成助手类(看起来很像您的示例)。代码生成的优点是,它在运行时要快一些,更重要的是,您可以像阅读手工编写的源代码一样阅读和逐步执行源代码。这也不是特别难——特别是现在有了partial关键字。