在我的软件中存在这种常见模式,其中完成数据库查询并且从该查询创建对象列表,其中对象是从SqlDataReader
构造的。
例如:
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;
}
您可以将Authorization
替换为任何类型的对象,但每次都是相同的模式。唯一的变量是对象类型和用于查询数据库的函数。
List<Authorization> ReadAuthorizations()
List<Login> ReadLogins()
List<Batch> ReadBatches()
// etc
现在我觉得这只是尖叫一个通用功能,我想出了这个:
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;
}
除了要构造的对象不允许带参数外,这个ALMOST有效,VS2k10说:
'T':在创建变量类型的实例
时无法提供参数
有没有办法解决这个问题,因此变量类型可以获得构造函数参数?
答案 0 :(得分:3)
有许多方法可以让这只猫受到伤害,但要让你朝着你选择探索的方向前进:
使用泛型约束指定where T : IPopulateFromReader, new()
并将reader参数移动到方法调用中:
T obj = new T();
obj.PopulateFromReader(reader);
objects.Add(obj);
interface IPopulateFromReader
{
void PopulateFromReader(IDataReader reader);
}
更严重的是,或许可以看看ORM解决方案,如:
列表还在继续。关键是,他们成功地提供了数据库访问的抽象并缓解了阻抗不匹配问题,使您可以专注于域模型和业务逻辑并忽略样板DAL代码。通常还支持管理数据库模式(尽管对此的支持因库/框架的重点而异)。
答案 1 :(得分:0)
我知道这不是最漂亮的方式,但你可以设置一个接口约束,然后尝试将读者作为属性(或构造方法)传递给构造的对象。
T obj = new T();
obj.Reader = reader;
objects.Add(obj);
答案 2 :(得分: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);