【问题标题】:Most efficient way to map IDataReader to a C# object, also catering for deep-copy?将 IDataReader 映射到 C# 对象的最有效方法,也适合深度复制?
【发布时间】:2016-01-17 17:47:45
【问题描述】:

我们有一个场景,我们使用 Enterprise Library Data 包来满足我们的数据访问需求。我们已经实现了Generic Data Reader 来将我们的数据水合到业务对象中。然而,我们正在寻找一种更有效的方法来做到这一点,这也将满足对象内子集合的深层复制,而不使用反射,因为我们正在处理大量数据。通用数据阅读器也不能很好地处理大量数据。我知道如果我们使用 EF,它可以解决这个问题,但我们还没有处于可以替换整个数据访问层的阶段。是否有替代方案或行业标准来实现这一目标?

【问题讨论】:

  • 最有效的方法是为每个阅读器和对象编写单独的代码。但我怀疑这不是您要寻找的答案。
  • 如果没有更多细节和特定场景,很难说什么......
  • 嗨@Sebastian,我们已经在我们的可重用代码库中提取了这个通用映射器,并将其用于我们所有的数据访问方法。基本上,使用 EntLib 的 ExecuteReader 方法调用数据库,该方法返回一个 IDataReader。然后,我们将来自 Reader 的信息水合到我们的对象中。 Paddy,我明白你的意思,但是,我们需要一个通用的方法来做这件事,以便我们可以重用。

标签: c# performance entity-framework data-access-layer idatareader


【解决方案1】:

您为什么不对映射器进行手动编码?这是最快的方法,并且您在项目生命周期中只创建一次映射器,对吗?与您所得到的相比,在生命周期为 50 年的项目中所花费的时间可以忽略不计(易于纠正错误,无需解释 OR/M 错误或其“简单性”)。

public interface IEntityMapper
{
   object Map(IDataRecord record);
}

public void UserMapper  : IEntityMapper
{
    public void Map(IDataRecord record)
    {
        var user = new User();
        user.FirstName = record["firstName"]
    }
}

如果您有大量记录,则需要使用number 索引器来防止名称查找:

public void UserMapper  : IEntityMapper
{
    public void Map(IDataRecord record)
    {
        var user = new User();

        //requires that you specify columns in your SELECT query
        //to not break the mapper in future versions.
        user.FirstName = record[0]
    }
}

在您的存储库中,您可以这样做:

public class UserRepository
{
    private readonly IDbConnection _connection;
    private UserMapper _mapper = new UserMapper();

    public IReadOnlyList<User> GetAllUsers()
    {
        using (var cmd = _connection.CreateCommand())
        {
            cmd.CommandText = "SELECT Id, UserName FROM Users";
            using (var reader = cmd.ExecuteReader())
            {
                var users = new List<User>();
                while (reader.Read())
                {
                   var user = _mapper.Map(reader);
                   users.Add(user);
                }
                return users;
            }
        }
    }
}

您甚至可以将所有内容移至扩展方法:

public static class CommandExtensions
{
    public static IReadOnlyCollection<T> ToList<T>(this IDbCommand cmd, IDataMapper mapper)
    {
        using (var reader = cmd.ExecuteReader())
        {
            var items = new List<T>();
            while (reader.Read())
            {
               var item= _mapper.Map(reader);
               items.Add(item);
            }
            return item;
        }
    }
}

为您提供更轻量级的存储库:

public class UserRepository
{
    private readonly IDbConnection _connection;
    private UserMapper _mapper = new UserMapper();

    public IReadOnlyList<User> GetAllUsers()
    {
        using (var cmd = _connection.CreateCommand())
        {
            cmd.CommandText = "SELECT Id, UserName FROM Users";
            return cmd.ToList<User>(_mapper);
        }
    }
}

深拷贝

关于深拷贝,我尽量避免使用复杂实体(除非子实体实际上是值对象)。最好使用 ID 来引用其他实体,否则很难在代码中的某个位置保留对实体的责任。

当涉及到映射时,我可能会引入一个约定,在映射接口中使用另一种方法映射子对象:

public interface IEntityMapper
{
   object Map(IDataRecord record);
   object Map(IDataRecord record, string prefix);
}

前缀可以用作约定,表示使用连接从多个表中获取信息。

public void UserMapper  : IEntityMapper
{
    private AddressMapper _childMapper = new AddressMapper();

    public void Map(IDataRecord record)
    {
        var user = new User();
        user.FirstName = record["FirstName"]
        user.Address = _childMapper.Map(record, "Address_");
    }
}

.. 这将使您的存储库方法如下所示:

public class UserRepository
{
    private readonly IDbConnection _connection;
    private UserMapper _mapper = new UserMapper();

    public IReadOnlyList<User> GetAllUsers()
    {
        using (var cmd = _connection.CreateCommand())
        {
            cmd.CommandText = @"SELECT Id, UserName, Address.City as Address_City 
                                FROM Users 
                                JOIN Address ON (Address.Id = Users.AddressId)";
            return cmd.ToList<User>(_mapper);
        }
    }
}

我写过关于 ADO.NET 和(我的)最佳实践的文章:http://blog.gauffin.org/2013/01/04/ado-net-the-right-way/

【讨论】:

  • 谢谢@jguffin,这绝对是我可以使用的。由于我们将它用于我们的可重用库,并且还有一个使用 DSL 的自定义生成器工具,我们或许可以创建 T4 模板来生成映射器类以及我们的数据传输对象。
  • 1.如果您实际上没有在代码中使用它,为什么要创建 IEntityMapper 接口? 2.如何将存储库中的映射器与写入集成(例如 CreateUserInDb(User user){ .. } 方法)?
  • 如果我映射了一个名为“ContactName”的字段,而我的查询没有返回任何“ContactName”,那么会发生什么?错误 ?如何解决这个问题?
猜你喜欢
  • 1970-01-01
  • 2011-07-02
  • 1970-01-01
  • 1970-01-01
  • 2011-08-04
  • 1970-01-01
  • 2016-01-19
  • 1970-01-01
  • 2016-03-11
相关资源
最近更新 更多