.Net 中的反射可以通过查看用户定义的类型名称来帮助动态构造表值参数/SqlMetaData[] 对象吗?

发布于 2024-11-30 14:59:59 字数 2914 浏览 4 评论 0原文

我已经开始在 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 列,想象一下您的代码。

I have started using Table Valued Parameters in Sql Server 2k8 for batch operations. I liked this feature a lot and feel it came after a long wait.

However, inorder to pass a TVP from .Net code there is too much of hardwork involved to construct the SQLMetaData[] and then filling up values in a loop.

How do you avoid the maintenance of keeping the User Defined Types in Sql Server and SQLMetaData[] objects in your .Net code in Synchronization?
When i change a type definition in SQL, there is no easy way of knowing where all did I use that type in huge code of .Net.

Can .Net Reflection rescue a programmer to construct SQLMetadata by giving the name of User Defined Type and help in filling the data by providing object arrays.

Consider This Example:

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)
    {

    }
}

Now if your table has 100 columns, imagine your code.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(2

尸血腥色 2024-12-07 14:59:59

您可以使用反射来做到这一点。首先,必须有一种方法来覆盖名称和长度的默认值。为此,请定义 Attribute

[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)。

另外,您的实际要求可能更复杂,因为您可能需要忽略某些属性或类似的东西。但这应该很容易添加。

You can do that using reflection. First, there has to be a way to override the default values for names and lengths. To do that, define Attributes:

[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;
    }
}

And use them on your type:

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; }
}

Then you can create a method that maps a collection of this type to collection of 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();
}

This code is uses quite a lot of reflection, so it can be too slow for you. If that's the case, you would need to implement some kind of caching. One way of doing that would be creating an Expression that does all this work and then compiling it into a delegate (.Net 4 only, because you would need BlockExpression).

Also, your actual requirements may be more complicated, because you may need to ignore some properties, or something similar. But that should be easy to add.

转角预定愛 2024-12-07 14:59:59

问题中没有足够的代码示例,但对于类似的事情,我会做一些事情,比如编写一个单独的 .NET 可执行文件来读取 SQL 元数据并为每个 UDT 生成帮助器类(看起来很像您的示例)。代码生成的优点是运行时速度更快,更重要的是,您可以像手写一样阅读和单步执行源代码。这也不是特别难——尤其是现在partial 关键字存在。

There isn't enough in the question to give a code sample, but for something like this I would do something like write a separate .NET executable to read the SQL metadata and generate helper classes (looking much like your example) for each UDT. The advantage of code generation is that it's a little faster at run time, and, more importantly, that you can read and step through the source code just as if it were hand-written. It's also not especially hard to do - especially now that the partial keyword exists.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文