C# 通过存储过程将数据列表返回给dapper

C# 通过存储过程将数据列表返回给dapper,c#,sql,.net,sql-server,dapper,C#,Sql,.net,Sql Server,Dapper,我试图通过存储过程使用Dapper返回数据 我的DTO类与下面类似(为了简洁起见删除了一些属性) 上面的查询可能会返回许多行、CarID和Manufacturer以及为简洁起见删除的其他属性。Dapper将按预期将这些映射回DTO 但是,问题是如何返回存储过程中的CarOptions列表-是否可以使用另一个查询,或者是否应该以某种方式将其分离出来?例如,如果我返回了CarID 1和CarID 2,CarOption表中可能有6行CarID 1,CarOption表中可能有4行CarID 2,理想

我试图通过存储过程使用Dapper返回数据

我的DTO类与下面类似(为了简洁起见删除了一些属性)

上面的查询可能会返回许多行、CarID和Manufacturer以及为简洁起见删除的其他属性。Dapper将按预期将这些映射回DTO


但是,问题是如何返回存储过程中的CarOptions列表-是否可以使用另一个查询,或者是否应该以某种方式将其分离出来?例如,如果我返回了CarID 1和CarID 2,CarOption表中可能有6行CarID 1,CarOption表中可能有4行CarID 2,理想情况下,如果可能的话,我希望它们都通过Dapper返回到CarOptions集合?

是的……这是可能的。使用dapper解决“一对多”方案有几种方法:

方法1-返回两个查询,在DAL中组合 DAL

var multi = db.QueryMultiple(getCarDataSp , new { CustomerID = customerId, Year = year },
                                commandType: CommandType.StoredProcedure));

var cars = multi.Read<CarDTO>();
var options = multi.Read<CarOptionDTO>();

//wire the options to the cars
foreach(var car in cars){
    var carOptions = options.Where(w=>w.Car.CarID == car.CarID);        //I would override Equals in general so you can write w.Car.Equals(car)...do this on a common DataModel class
    car.Options = carOptions.ToList();
}
var tuples = db.Query<CarDTO, CarOptionDTO,Tuple<CarDTO,CarOptionDTO>>(getCarDataSp , new { CustomerID = customerId, Year = year },
(car,opt)=> Tuple.Create(car,opt),                       commandType: CommandType.StoredProcedure);

//group tuples by car
var cars = tuple.GroupBy(gb=>gb.Item1.CarID)                    //again, overriding equals makes it so you can just to GroupBy(gb=>gb.Item1)
            .Select(s=>{
            var car = s.First().Item1;
            var carOptions = s.Select(t=>t.Item2).ToList()

            return car;
            });
DAL

var multi = db.QueryMultiple(getCarDataSp , new { CustomerID = customerId, Year = year },
                                commandType: CommandType.StoredProcedure));

var cars = multi.Read<CarDTO>();
var options = multi.Read<CarOptionDTO>();

//wire the options to the cars
foreach(var car in cars){
    var carOptions = options.Where(w=>w.Car.CarID == car.CarID);        //I would override Equals in general so you can write w.Car.Equals(car)...do this on a common DataModel class
    car.Options = carOptions.ToList();
}
var tuples = db.Query<CarDTO, CarOptionDTO,Tuple<CarDTO,CarOptionDTO>>(getCarDataSp , new { CustomerID = customerId, Year = year },
(car,opt)=> Tuple.Create(car,opt),                       commandType: CommandType.StoredProcedure);

//group tuples by car
var cars = tuple.GroupBy(gb=>gb.Item1.CarID)                    //again, overriding equals makes it so you can just to GroupBy(gb=>gb.Item1)
            .Select(s=>{
            var car = s.First().Item1;
            var carOptions = s.Select(t=>t.Item2).ToList()

            return car;
            });
应用BaseDTO帮助实现平等

一旦你有了BaseDTO,并连接好你的ID,你就可以简单地说:cars.Where(w=>w.Equals(car)),dictionary[car](如果在那里),if(car.Equals(otherCar)),或者results.GroupBy(gb=>gb.car)

基于公共类的
{
内部int ID{get;set;}
/// 
//如果OBJ是同一类型,具有相同的ID,我们会认为它是相等的。
/// 
公共覆盖布尔等于(对象对象对象)
{
if(obj==null | | this.GetType()!=obj.GetType())
{
返回false;
}
返回此.GetType().GetHashCode()==obj.GetType().GetHashCode()&&
this.ID==(BaseDTO)obj.ID;
}
/// 
///如果重写equals,则应重写gethashcode。
/// http://stackoverflow.com/questions/263400/what-is-the-best-algorithm-for-an-overridden-system-object-gethashcode#263416
/// 
公共覆盖int GetHashCode()
{
未经检查
{
int hash=17;
hash=hash*23+this.GetType().GetHashCode();
hash=hash*23+this.ID;
返回散列;
}
}
}
公共类CarDTO:BaseDTO
{
公共国际加勒比
{
获取{返回this.ID;}
设置{this.ID=value;}
}
公共字符串制造商{get;set;}
公共列表选项{get;set;}
}

太好了,我试试看。乍一看,我喜欢选项2…在代码中保留尽可能多的逻辑,并保持存储的过程“更干净”。选项2的缺点是查询的大小会更大…如果一辆车有50个选项,则该列数据会重复50次(每个选项一次)。这不是一个大问题,但无论如何,这是有道理的。它可能有多达70个选项……不太可能有超过10个选项,但可能有多达70个选项。除了SP中的逻辑有点太多之外,选项1还有什么缺点吗?我想在SP中使用逻辑的好处是,如果需要修复,可以直接部署。我认为您所做的是查询逻辑,而不是业务逻辑。查询逻辑(过滤、检索)在存储过程中是完全可以接受的。顺便问一下,这一定是一场狂欢吗?如果您直接传入sql(也可以使用资源文件来帮助管理sql文件),那么维护起来就容易多了。我们转换了数百个进程,非常满意。好吧……第一种方法的另一个技巧是,只需在一个临时表中进行一个查询,该临时表将您的carID和筛选帐户(WHERE子句)存储在一个位置。然后,做两个非常干净的选择加入龋齿你刚刚放在一个临时。基本上:1.)插入@t select CarID,其中@cust=xxx…2.)从Cars c选择*内部连接@t t ON t.CarID=c.CarID 3.)从CarOptions co内部连接@t ON t.CarID=co.CarID选择*。
ALTER PROCEDURE [dbo].[GetCarData]
    @CustomerID int, 
    @Year int
AS
BEGIN
    SET NOCOUNT ON;

    --return cars
    SELECT c.*
        from [dbo].Car c
    INNER JOIN [dbo].Customer cust ON c.CarID = cust.CarID
    WHERE cust.CustID = @CustomerID AND cust.Year = @Year

    --return options
    SELECT opt.*
        from [dbo].Car c
    INNER JOIN [dbo].Customer cust ON c.CarID = cust.CarID
    INNER JOIN dbo.CarOptions opt ON op.CarID = c.CarID
    WHERE cust.CustID = @CustomerID AND cust.Year = @Year

END
var multi = db.QueryMultiple(getCarDataSp , new { CustomerID = customerId, Year = year },
                                commandType: CommandType.StoredProcedure));

var cars = multi.Read<CarDTO>();
var options = multi.Read<CarOptionDTO>();

//wire the options to the cars
foreach(var car in cars){
    var carOptions = options.Where(w=>w.Car.CarID == car.CarID);        //I would override Equals in general so you can write w.Car.Equals(car)...do this on a common DataModel class
    car.Options = carOptions.ToList();
}
ALTER PROCEDURE [dbo].[GetCarData]
    @CustomerID int, 
    @Year int
AS
BEGIN
    SET NOCOUNT ON;


    SELECT c.*,  opt.*
     from [dbo].Car c
    INNER JOIN [dbo].Customer cust ON c.CarID = cust.CarID
    LEFT OUTER JOIN dbo.CarOptions opt ON op.CarID = c.CarID
    WHERE cust.CustID = @CustomerID AND cust.Year = @Year

END
var tuples = db.Query<CarDTO, CarOptionDTO,Tuple<CarDTO,CarOptionDTO>>(getCarDataSp , new { CustomerID = customerId, Year = year },
(car,opt)=> Tuple.Create(car,opt),                       commandType: CommandType.StoredProcedure);

//group tuples by car
var cars = tuple.GroupBy(gb=>gb.Item1.CarID)                    //again, overriding equals makes it so you can just to GroupBy(gb=>gb.Item1)
            .Select(s=>{
            var car = s.First().Item1;
            var carOptions = s.Select(t=>t.Item2).ToList()

            return car;
            });
ALTER PROCEDURE [dbo].[GetCarData]
    @CustomerID int, 
    @Year int
AS
BEGIN
    SET NOCOUNT ON;

    declare @t table(CarID int);

    --filter cars (only deal with parameters here)
    INSERT INTO @t(CarID)
    SELECT c.CarID
    FROM dbo.Car c
        INNER JOIN [dbo].Customer cust ON c.CarID = cust.CarID
    WHERE cust.CustID = @CustomerID AND cust.Year = @Year

    --return cars
    SELECT c.*
    FROM [dbo].Car c
        INNER JOIN @t t ON t.CarID = c.CarID

    --return options
    SELECT opt.*
    FROM dbo.CarOptions opt
        INNER JOIN @t t ON t.CarID = opt.CarID

END
public class BaseDTO
{
    internal int ID { get; set; }

    /// <summary>
    /// If the obj is the same type with the same id we'll consider it equal.
    /// </summary>
    public override bool Equals(object obj)
    {
        if(obj == null || this.GetType() != obj.GetType())
        {
            return false;
        }

        return this.GetType().GetHashCode() == obj.GetType().GetHashCode() &&
                this.ID == (BaseDTO)obj.ID;
    }

    /// <summary>
    /// If you override equals, you should override gethashcode.  
    /// http://stackoverflow.com/questions/263400/what-is-the-best-algorithm-for-an-overridden-system-object-gethashcode#263416
    /// </summary>
    public override int GetHashCode()
    {
        unchecked
        {
            int hash = 17;

            hash = hash * 23 + this.GetType().GetHashCode();
            hash = hash * 23 + this.ID;

            return hash;
        }
    }
}

public class CarDTO : BaseDTO
{
    public int CarID
    {
        get { return this.ID; }
        set { this.ID = value; }
    }
    public string Manufacturer { get; set; }
    public List<CarOptionDTO> CarOptions { get; set; }
}