5

There's this common pattern in my software where a database query is done and a list of objects is created from that query where the objects are constructed from a SqlDataReader.

For example:

public List<Authorization> ReadAuthorizations() {
  List<Authorization> authorizations = new List<Authorization>();

  using (SqlConnection connection = GetConnection(mConnString)) {
    using (SqlDataReader authReader = CmdFactory.Instance.ReadAuthorizations(connection))                                   {
      while (authReader.Read()) {
        Authorization auth = new Authorization(authReader);
        authorizations.Add(auth);
      }
    }
 }

 return authorizations;
}

You can replace Authorization with any kind of object but the pattern is the same every time. The only variables are the object types and the function that is used to query the database.

List<Authorization> ReadAuthorizations()
List<Login> ReadLogins()
List<Batch> ReadBatches()
// etc

Now I was thinking that this just screams for a generic function and I came up with this:

public List<T> Read<T>(Func<SqlConnection, SqlDataReader> func) where T : new()
{
    List<T> objects = new List<T>();

    using (SqlConnection connection = GetConnection(_ropsString))
    {
        using (SqlDataReader reader = func(connection))
        {
            while (reader.Read())
            {
                T obj = new T(reader);
                objects.Add(obj);
            }
        }
    }

    return objects;
}

This ALMOST works except that the object to construct is not allowed to take parameters, VS2k10 says:

'T': cannot provide arguments when creating an instance of a variable type

Is there a way to solve this so the variable type can get a constructor argument?

4

3 回答 3

3

有很多方法可以给这只猫剥皮,但要让你朝着你选择探索的方向前进:

使用通用约束来指定where T : IPopulateFromReader, new()reader 参数并将其移动到方法调用中:

T obj = new T();
obj.PopulateFromReader(reader);
objects.Add(obj);

interface IPopulateFromReader
{
    void PopulateFromReader(IDataReader reader);
}

更严肃地说,也许看看 ORM 解决方案,例如:

名单还在继续。关键是,它们成功地提供了对数据库访问的抽象并减轻了阻抗不匹配问题,使您可以专注于域模型和业务逻辑,而忽略样板 DAL 代码。通常还支持管理数据库模式(尽管对此的支持取决于库/框架的重点)。

于 2013-10-24T09:20:31.900 回答
0

我会推荐 Dapper:我一直用它来做这种工作:

小巧玲珑

    public class Dog
    {
        public int? Age { get; set; }
        public Guid Id { get; set; }
        public string Name { get; set; }
        public float? Weight { get; set; }

        public int IgnoredProperty { get { return 1; } }
    }            

    var guid = Guid.NewGuid();
    var dogs = connection.Query<Dog>("select Age = @Age, Id = @Id", new { Age = (int?)null, Id = guid });

    dogs.Count()
        .IsEqualTo(1);

    dogs.First().Age
        .IsNull();

    dogs.First().Id
       .IsEqualTo(guid);
于 2013-10-24T09:27:03.177 回答
0

我知道这不是最漂亮的方法,但是您可以放置​​一个接口约束,然后尝试将读取器作为属性(或构造方法)传递给构造的对象。

T obj = new T();
obj.Reader = reader;
objects.Add(obj);
于 2013-10-24T09:21:28.970 回答