特定于类、属性或方法的ExtensionMethod

本文关键字:方法 ExtensionMethod 属性 于类 | 更新日期: 2023-09-27 18:17:08

我更喜欢使用大量的扩展方法,如ToFloat, ToInt, ToLoong, ToGuid, ToSqlParameter

自从我在现在的位置工作以来,有些人一直在抱怨他们在日常对象上看到的扩展方法的数量之多。

对于我的问题,我将使用示例:"ToSqlParameter"

    public static SqlParameter ToSqlParameter(this object source, string name, bool structured = false)
    {
        var para = new SqlParameter { ParameterName = name, Value = source };
        if (structured) { para.SqlDbType = SqlDbType.Structured; }
        return para;
    }

当前,每个对象都有可用的扩展方法。

我知道添加像where T : class这样的东西只会使它在类上工作,但是因为这个方法应该能够在string, bool,甚至objects上使用,所以我无能为力。

现在我的问题是,是否有可能创建扩展方法,以便它只能在应用于List<SqlParameter>

时使用

:

var str = "Some Strnig";
var list = new List<SqlParameter>();
var list2 = new List<string>();
list.Add(str.ToSqlParameter("@str")); //Should work
list2.Add(str.ToSqlParameter("@str")); //Should return 'SqlParameter not found'

这可能吗?

特定于类、属性或方法的ExtensionMethod

不,这是不可能的

扩展方法仅根据您试图调用它们的类型而被发现,而不是基于周围的上下文。

您的最后一行代码将得到一个不同的错误,因为ToSqlParameter的结果不能添加到列表中,但该方法将可用。

"过滤"扩展方法的唯一方法是:

  1. 它们可以被调用的类型,这不是一个选项,因为你想在object
  2. 通过命名空间,从而通过显式添加using X;指令使其可用。

另一方面,如果所有对象都可以简单地包装在SqlParameter中,那么这里有一种不同的方法,使用反射。

这是一个原型(未测试):

public static void AddParameters(this SqlCommand command, object parameters)
{
    foreach (var propertyInfo in parameters.GetType().GetProperties())
    {
        object propertyValue = propertyInfo.GetValue(parameters, null);
        command.Parameters.AddWithValue("@" + propertyInfo.Name, propertyValue);
    }
}

你可以这样使用:

public void DeletePersonById(int id)
{
    var cmd = new SqlCommand();
    cmd.Connection = ...
    cmd.CommandText = "delete from persons where person_id = @id");
    cmd.AddParameters(new { id });
    cmd.ExecuteNonQuery();
    ...

请注意,这个方法只创建非常基本的SqlParameter对象,您可能希望扩展它以更好地处理特殊类型,如具有大小的字符串等。

关于我用于简单程序的一些基本代码的更完整的示例,请查看这里:Dropbox链接到SO16962113.linq。这是一个可以运行的LINQPad程序,它将在本地创建一个SQL Server数据库,并为查询设置一些虚拟数据。

相关语法(方法在LINQPad程序中实现):

using (var conn = new SqlConnection("...").AutoOpen())
{
    // prepare a fresh new database
    conn.Execute("if exists (select * from sysdatabases where name = 'SO16962113') drop database SO16962113");
    conn.Execute("create database SO16962113");
    conn.Execute("use SO16962113");
    // execute some queries
    conn.Execute("create table persons (person_id int primary key not null identity(1,1), person_name varchar(100))");
    conn.Execute("insert into persons (person_name) values ('James'), ('Jon'), ('Mary'), ('Jack')");
    conn.Query<Person>("select * from persons where person_id in (@id1, @id2)", new { id1 = 2, id2 = 4 }).Dump();
    // clean up
    conn.Execute("use master");
    conn.Execute("drop database SO16962113");
}

基本上没有。分辨率只基于str的类型——在list.Add/list2.Add之外发生的事情与ToSqlParameter的分辨率无关。

我不得不同意你的同事——扩展object或不受约束的T通常不是一个好主意,因为这会对发现产生不利影响。您可以也许把所有这些扩展方法放在一个非常特定的名称空间(例如Foo.Bar.DbHelpers),这样它们就不会一直可见(只有当using该名称空间时)。

另一个选择是扩展列表本身 -即this IList<SqlParameter>或类似的

不,这是不可能的,但是一个可能的解决方案是将您的扩展方法namespace,以便那些利用它们的人将为那些有意义的语句添加using

看,如果我知道我要做一些类型转换,但不处理任何SqlParameter对象,那么我就只添加using Extensions.Casting;,这样我就有了一个更小的方法子集。