特定于类、属性或方法的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'
这可能吗?
不,这是不可能的
扩展方法仅根据您试图调用它们的类型而被发现,而不是基于周围的上下文。
您的最后一行代码将得到一个不同的错误,因为ToSqlParameter
的结果不能添加到列表中,但该方法将可用。
"过滤"扩展方法的唯一方法是:
- 它们可以被调用的类型,这不是一个选项,因为你想在
object
- 通过命名空间,从而通过显式添加
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;
,这样我就有了一个更小的方法子集。