关于 c# 接口和泛型的问题
Posted
技术标签:
【中文标题】关于 c# 接口和泛型的问题【英文标题】:A question regarding c# interfaces and maybe generics 【发布时间】:2011-04-28 00:01:58 【问题描述】:我们已经构建了一个内部工具来生成整个数据访问,每个表都有一个代表它的数据和所有常见操作的类。(想想轻量级实体框架)。
这些 DataAccess 对象总是有一个接收连接字符串的构造函数和一个接收 SqlDataReader 的 Load 函数。
类似这样的:
class Customer
public string ConnStr;
public int Id;
public string Name;
Public customers(string connStr)
ConnStr = connStr;
public Customer Load(SqlDataReader)
if(reader.Read())
Id = reader["Id"].ToString();
Name = reader["Name"].ToString();
我想编写一个实用程序数据访问静态方法,该方法允许我编写我的 SQL 并获得一个对象列表作为回报,遵循前面的对象示例:
string SQL = "SELECT * FROM Customers WHERE Name=@Name";
List<Customer> customers = GetList<Customer>(connStr, SQL, new SqlParameters("@Name", "John"));
我不知道如何处理它,我尝试过接口,但它们不允许构造函数或静态方法,并且使用泛型 - 我无法调用初始化对象所需的方法(构造函数 +加载),这是我最近的尝试,注释掉了不起作用的部分:
public static List<T> GetList<T>(string connStr, string SQL, params SqlParameter[] prms)
List<T> list = new List<T>();
using (SqlConnection conn = new SqlConnection(connStr))
conn.Open();
SqlCommand cmd = new SqlCommand(SQL, conn);
foreach (SqlParameter param in prms)
cmd.Parameters.Add(param);
using (SqlDataReader reader = cmd.ExecuteReader())
//T item = new T(connStr);
//item.Load(reader);
//list.Add(item);
return list;
顺便说一句,我们有兴趣开源我们的 DataAccess 生成器,这太棒了 - 它允许非常有效地访问数据库对象 + 创建一个 javascript 数据访问层,让您可以从 javascript 完全控制您的数据库(当然这有安全隐患可以管理)。
如果这里有人知道如何“开源”这样的项目或任何想加入该产品开发的公司 - 请随时与我联系:eytan@titkadem.co.il
提前致谢, 伊坦
【问题讨论】:
OT,但从纯粹主义者的角度来看,那些域对象对持久性了解很多,这不一定是他们的责任。我还认为 DataContext 可以为您完成大部分绑定,而无需实体中的任何数据访问代码,免费ExecuteQuery<T>
Lasse 提出了一个有趣的观点,即谁调用了 Read;在许多方面,最好接受IDataRecord
而不是IDataReader
/SqlDataReader
- 假设它们只需要一行。
【参考方案1】:
Load
很简单 - 你可以:
interface IDataEntity
void Load(SqlDataReader reader);
然后:
public static List<T> GetList<T>(string connStr, string SQL,
params SqlParameter[] prms) where T : IDataEntity, new()
....
T item = new T();
item.Load(reader);
list.Add(item);
new T(connStr)
更棘手 - 它真的需要这个值吗?公共属性会更容易:
interface IDataEntity
void Load(SqlDataReader reader);
string ConnectionString get;set;
class Customer : IDataEntity
...
等等。参数化的泛型构造函数没有任何内置(语言)支持。您可以绕过它,但在许多情况下,Activator.CreateInstance
的参数化形式足够快(与通过网络访问数据相比,反射可以忽略不计)。如果您需要参数化版本,可以使用Expression
等来完成(如果您需要示例,请告诉我)。
【讨论】:
嗨 Marc,非常感谢,唯一的更正是 new() 约束应该出现在 IDataEntity 之后。【参考方案2】:你需要进行反射,除非你想改变你的类以构造函数的方式工作。
你可以这样做:
while (reader.Read())
T item = Activator.CreateInstance(typeof(T), new object[] connStr ) as T;
item.Load(reader);
list.Add(item);
(请注意,上述循环的职责是调用阅读器上的 .Read)
请注意,除非您通过接口公开 Load 方法,否则您还需要通过反射调用它。
我会这样做:
public interface IDataObject
void Load(IDataReader reader);
然后为您的客户实施这个:
public class Customer : IDataObject
...
然后在你的 GetList 方法中添加一个约束:
public List<T> GetList<T>(string connStr, string sql, params SqlParameter[] parameters)
where T : IDataObject
【讨论】:
Read
的好地方;实际上,我想知道IDataRecord
会不会更好。
使用 IDataRecord 可能会更好,同意。【参考方案3】:
这不会如您所愿。 .net 的 SQL 类不是强类型的。不可能从像 SELECT * FROM Customers...
这样的查询中说出必须构造什么类的对象。如果您的所有实体都派生自一个声明的类,这是可能的:
public virtual Entity Load(SqlDataReader reader)
你的泛型方法有约束:
public static List<T> GetList<T>(string connStr, string SQL, params SqlParameter[] prms)
where T : Entity, new()
那么你可以这样做:
T entity = new T();
entity.Load(reader);
【讨论】:
【参考方案4】:为什么不将 const 传递给方法:
T item = new T();
item.SetConn(connStr);
别忘了:
public static List<T> GetList<T>(string connStr, string SQL, params SqlParameter[] prms)
where T : new
【讨论】:
以上是关于关于 c# 接口和泛型的问题的主要内容,如果未能解决你的问题,请参考以下文章