这是使用Dapper的正确方式还是我做错了?

这是使用Dapper的正确方式还是我做错了?

问题描述:

我想摆脱实体框架,因为我不得不在我们的解决方案中支持SQL服务器数据库以外的HANA数据库。这是使用Dapper的正确方式还是我做错了?

我正在做一些研究与短纤维,所以我创建了一个虚拟场景的快速测试环境。

我有以下波苏斯类似于我的数据库架构(我有更多的,但我仅限于显示这些为简单起见):

public class Adopter 
{ 
    public int Id { get; set; } 
    public string FirstName { get; set; } 
    public string LastName { get; set; } 
    public string Address { get; set; } 
    public string Address2 { get; set; } 
    public string City { get; set; } 
    public State State { get; set; } 
    public int StateId { get; set; } 
    public string Zip { get; set; } 
    public string Email { get; set; } 
    public string Phone { get; set; } 
    public string Fax { get; set; } 
    public IEnumerable<Pet> Pets { get; set; } 
} 

public class State 
{ 
    public int Id { get; set; } 
    public string Name { get; set; } 
    public string Abreviation { get; set; } 
} 

public class Pet 
{ 
    public int Id { get; set; } 
    public string IdTag { get; set; } 
    public string Name { get; set; } 
    public DateTime AdmitionDate { get; set; } 
    public Status Status { get; set; } 
    public int StatusId { get; set; } 
    public string Notes { get; set; } 
    public DateTime AdoptionDate { get; set; } 
    public bool IsAdopted { get; set; } 
    public int? AdopterId { get; set; } 
    public int Age { get; set; } 
    public decimal Weight { get; set; } 
    public string Color { get; set; } 
    public Breed Breed { get; set; } 
    public int BreedId { get; set; } 
    public Gender Gender { get; set; } 
    public int GenderId { get; set; } 
    public IEnumerable<PetImage> PetImages { get; set; } 
} 


public class Status 
{ 
    public int Id { get; set; } 
    public string Name { get; set; } 
    public string Description { get; set; } 
} 

public class Gender 
{ 
    public int Id { get; set; } 
    public string Name { get; set; } 
} 

我正在使用的存储库下面来回报所有列表使用者:

 using (SqlConnection connection = new SqlConnection(_connectionString)) 
     { 
      var adopters = connection.Query<Adopter>("SELECT a.* FROM Adopters a"); 
      foreach (var adopter in adopters) 
      { 
       adopter.State = connection.QueryFirst<State>("Select s.* FROM States s WHERE s.Id = @Id", new { Id = adopter.StateId }); 
       adopter.Pets = connection.Query<Pet>("Select p.* FROM Pets p WHERE p.AdopterId = @Id", new { Id = adopter.Id }); 
       foreach (var pet in adopter.Pets) 
       { 
        pet.Status = connection.QueryFirst<Status>("Select s.* FROM Status s WHERE s.Id = @Id", new { Id = pet.StatusId }); 
        pet.Gender = connection.QueryFirst<Gender>("Select g.* FROM Genders g WHERE g.Id = @Id", new { Id = pet.GenderId }); 
       } 
      } 

      return adopters; 
     } 

正如你所看到的,我单独检索基于前一个每个POCO的数据,做在代码中手动加入。

这是做这件事的正确方法吗?或者我应该做一个有多个连接的大查询并以某种方式通过简洁和LINQ来映射结果?

+2

这会导致所需的对象,所以它在技术上是正确的......但这仍然是一个糟糕的做法。此代码对数据库执行多次调用(取决于循环的次数),因此最好使用1个查询一次检索所有内容。 – RandomStranger

+0

您可以阅读本文=> https://www.tritac.com/developers-blog/dapper-net-by-example/“具有单个子对象(multimap)的对象列表” – pridemkA

+0

如果存在像在Dapper中执行所有查询并返回多个记录集的DataReader NextResult,我会为此付出代价。 –

一个可能的改进你的实际的解决方案是通过使用QueryMultiple扩展这样的:

using (SqlConnection connection = new SqlConnection(_connectionString)) 
{ 
    string query = @"SELECT * FROM Adopters; 
        SELECT * FROM States; 
        SELECT * FROM Pets; 
        SELECT * FROM Status; 
        SELECT * FROM Genders;"; 

    using (var multi = connection.QueryMultiple(query, null)) 
    { 
     var adopters = multi.Read<Adopter>(); 
     var states = multi.Read<State>(); 
     var pets = multi.Read<Pet>(); 
     var statuses = multi.Read<Status>(); 
     var genders = multi.Read<Gender>(); 

     foreach (Adopter adp in adopters) 
     { 
      adp.State = states.FirstOrDefault(x => x.Id == adp.StateID); 
      adp.Pets = pets.Where(x => x.IsAdopted && 
            x.AdopterID.HasValue && 
            x.AdopterID.Value == adp.AdopterID) 
            .ToList(); 
      foreach(Pet pet in adp.Pets) 
      { 
       pet.Status = statuses.FirstOrDefault(x => x.Id == pet.StatusID); 
       pet.Gender = genders.FirstOrDefault(x => x.Id == pet.GenderID); 

      } 
     } 
    } 
} 

这样做的好处是,你到达数据库只是一个时间,然后再处理内存中的所有。

但是,如果你有一个非常大的数据来检索(并从远程位置),这可能是性能问题和内存瓶颈。更好地仔细观察这种方法,并尽可能尝试某种异步处理和/或分页。

+0

谢谢你,它看起来比我有,我就开始用它进行试验更好,分页将最有可能成为最。 –

+0

在很大程度上取决于你的数据的大小。也许你可以简单地呈现在您让用户通过其名称选择采纳者,然后运行该代码添加WHERE条件过滤的接口。 (或显示使用者的页面,并选择一个对的选择/点击动作) – Steve

+0

另外,在您了解了该链接的点在简单非常有用的连接情况下,也许有可能使用多个连接来创建它的一个延伸,但最终结果将与上面的代码具有相同的复杂性 – Steve

我不喜欢是消极的,但是......不要这样做!不要这样想。你想转储EF,但是你想通过模拟EF来走进陷阱。在你的应用程序和你的数据库之间的桥梁,不是每一次都可以建立一次,为了每一个可以想象的目的。具体而言,你不应该带回整个表格,当然也不应该循环遍历每一行并发出更多的查询。你可能会感到不公正的批评,你只是在测试工具!如果是这样,也许告诉我们你正在检查的工具的哪个方面,然后我们将重点介绍。

小巧玲珑或QueryFirst大大简化运行的查询,并且消耗的结果,所以带回来,你需要的东西,只是当你需要它。然后,对于具体的工作,稍微去规范化。为什么在您的查询中没有加入?关系数据库管理系统非常棒,并且非常擅长进行连接。如果你在数据库之外加入数据,疯狂是唯一的词,即使Linq为你提供了一个超级(类似sql)的语法。 1个表对应1个班级这一不假设的假设是很多问题的开始。