关于c#接口和泛型的问题

时间:2010-10-20 11:40:18

标签: c# asp.net generics data-access-layer

我们已经构建了一个生成整个数据访问的内部工具,每个表都有一个表示它的数据和所有常见操作的类。(想想轻量级实体框架)。

这些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"));

我不知道如何处理它,我已经尝试过接口但它们不允许构造函数或静态方法,并且使用泛型 - 我无法调用我需要初始化对象的方法(构造函数+ load),这是我的最新尝试,注释掉了不起作用的部分:


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

提前致谢, 伊藤

4 个答案:

答案 0 :(得分:3)

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等完成(如果你想要一个例子,请告诉我。)

答案 1 :(得分:2)

为什么不将constr传递给方法:

T item = new T();
item.SetConn(connStr);

不要忘记:

public static List<T> GetList<T>(string connStr, string SQL, params SqlParameter[] prms)
  where T : new
{

答案 2 :(得分: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

答案 3 :(得分:1)

这不会按你的意愿行事。 .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);