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

发布于 2024-09-28 17:13:16 字数 2012 浏览 1 评论 0原文

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

这些 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 完全控制您的数据库(当然这会带来安全隐患)可以管理)。

如果这里有人知道如何“开源”这样的项目或任何想要加入该产品开发的公司 - 请随时与我联系:[电子邮件受保护]

提前致谢, 埃坦

We have built an internal tool that generates the whole data access, each table has a class the represents it's data and all the common operations.(think lightweight Entity framework).

These DataAccess objects always have a constructor that receives a connection string, and a Load function that receives a SqlDataReader.

Something like this:

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

I want to write a utility Data Access static method that will allow me to write my SQL and get a list of objects in return, following that previous object example:

string SQL = "SELECT * FROM Customers WHERE Name=@Name";
List<Customer> customers = GetList<Customer>(connStr, SQL, new SqlParameters("@Name", "John"));

I somehow can't figure how to deal with it, I have tried interfaces but they don't allow constructors or static methods, and using generics - i can't call the methods i need to init the object(constructor + load), here is my latest try, commented out the section that doesn't work:


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

Btw, we are interested in open sourcing our DataAccess generator, it's amazing - it allow very effecient access to DB objects + creates a javascript data access layer that gives you FULL CONTROL of your DB from javascript(ofcourse this has security implications which can be managed).

If anyone here knows how to "open source" a project like this or any company that wants to join the development of this product - please feel free contacting me: [email protected]

Thanks in advance,
Eytan

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

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

发布评论

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

评论(4

谜泪 2024-10-05 17:13:16

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 的参数化形式足够快(与通过网络进行数据访问相比,反射可以忽略不计)。如果您需要参数化版本,可以使用表达式等来完成(如果您想要示例,请告诉我)。

The Load is easy enough - you could have:

interface IDataEntity {
    void Load(SqlDataReader reader);
}

then:

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

The new T(connStr) is trickier - does it really need this value? A public property would be easier:

interface IDataEntity {
    void Load(SqlDataReader reader);
    string ConnectionString {get;set;}
}
class Customer : IDataEntity 
{ ... }

etc. There isn't any inbuilt (language) support for parameterised generic constructors. You can hack around it, but in many cases the parameterised forms of Activator.CreateInstance are fast enough (when compared to data-access over a network, reflection is negligible). If you need a parameterised version it can be done with Expression etc (let me know if you want an example).

伴我老 2024-10-05 17:13:16

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

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

并且不要忘记:

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

Why not passing constr to a method:

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

And do not forget:

public static List<T> GetList<T>(string connStr, string SQL, params SqlParameter[] prms)
  where T : new
{
ぃ双果 2024-10-05 17:13:16

您需要进行反射,除非您想以构造函数的方式更改类的工作方式。

你可以这样做:(

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

You need to go to reflection, unless you want to change how your classes work in the way of constructors.

You can do this:

while (reader.Read())
{
    T item = Activator.CreateInstance(typeof(T), new object[] { connStr }) as T;
    item.Load(reader);
    list.Add(item);
}

(note here, I made it the responsibility of the above loop to call .Read on the reader)

Note that unless you expose the Load method through an interface, you need to call that through reflection as well.

I would do this:

public interface IDataObject
{
    void Load(IDataReader reader);
}

And then implement this for your Customer:

public class Customer : IDataObject
{
    ...

And then add a constraint to your GetList method:

public List<T> GetList<T>(string connStr, string sql, params SqlParameter[] parameters)
    where T : IDataObject
人│生佛魔见 2024-10-05 17:13:16

这不会如你所愿。 .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);

This will not work as you wish. SQL classes for .net are not strongly typed. It it impossible to say from query like SELECT * FROM Customers... object of what class has to be constructed. This is possible if all your entities derive from single class that declares:

public virtual Entity Load(SqlDataReader reader)

and your generic method has constraint:

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

then you could do:

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