Вопрос касательно интерфейсов c # и, возможно, дженериков - PullRequest
3 голосов
/ 20 октября 2010

Мы создали внутренний инструмент, который генерирует весь доступ к данным, у каждой таблицы есть класс, который представляет ее данные и все общие операции (представьте себе легковесную платформу Entity).

Эти объекты DataAccess всегда имеют конструктор, который получает строку подключения, и функцию Load, которая получает SqlDataReader.

Примерно так:

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

Я почему-то не могу понять, как с этим справиться, я пробовал интерфейсы, но они не позволяют конструкторы или статические методы, и используют обобщенные типы - я не могу вызвать методы, которые мне нужны для инициализации объекта (constructor + загрузить), вот моя последняя попытка, закомментированный раздел, который не работает:


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 ]

3 голосов
/ 20 октября 2010

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 и т. Д. (Дайте мне знать, если вам нужен пример).

2 голосов
/ 20 октября 2010

Вам нужно перейти к рефлексии, если вы не хотите изменить способ работы ваших классов с помощью конструкторов.

Вы можете сделать это:

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
2 голосов
/ 20 октября 2010

Почему бы не передать constr методу:

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

И не забудьте:

public static List<T> GetList<T>(string connStr, string SQL, params SqlParameter[] prms)
  where T : new
{
1 голос
/ 20 октября 2010

Это не будет работать, как вы хотите. Классы SQL для .net не являются строго типизированными. Из запроса типа 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);
...