2017-03-31 42 views
2

我想通過存儲過程返回採用精緻小巧的數據通過存儲過程返回數據的列表,以短小精悍

我的DTO類是類似以下(除去簡潔一些屬性)

public class CarDTO 
{ 
    public int CarID { get; set; } 
    public string Manufacturer { get; set; } 
    public List<CarOptionDTO> CarOptions { get; set; } 
} 

所以基本上在數據庫中我有一個CarOption表,它有一個CarID列 - 例如Car可以有很多選項。

在一分鐘我的DAL層調用如下:

private string getCarDataSp = "[dbo].[GetCarData]"; 

    public IEnumerable<CarDTO> GetCarData(int customerId, int year) 
    { 
     return Get(db => db.Query<CarDTO>(getCarDataSp , new { CustomerID = customerId, Year = year }, 
           commandType: CommandType.StoredProcedure)); 
    } 

我獲取功能的實現是在我BaseRepository類爲:

public T Get<T>(Func<IDbConnection, T> query) 
    { 
     using (IDbConnection db = new SqlConnection(ConfigurationManager.ConnectionStrings["myDB"].ConnectionString)) 
     { 
      return query.Invoke(db); 
     } 
    } 

採用小巧玲瓏的,我可以從返回是否有可能我的存儲過程也是CarOptions?

我在一分鐘存儲的過程是如下:

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

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

END 

上述查詢可以返回許多行和CarID和製造商和其他屬性我爲簡潔移除。 Dapper會按預期將它們映射回DTO。

但是,它是如何返回存儲過程中的CarOptions的列表 - 是否有可能與另一個查詢或應該分離出來?例如,如果我返回CarID 1和CarID 2,CarOption表中可能有6行CarID 1和4行與CarID 2的CarOption表中,理想情況下,我希望將它們全部返回到CarOptions集合如果可能,通過Dapper?

回答

1

是的......這是可能的。有一對夫婦的解決 「一到多」 的場景與短小精悍的方式:

方法1 - 返回兩個查詢,結合在DAL

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 

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(); 
} 

方法2 - 返回一個查詢,拆分爲DAL

Proc

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 

DAL

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; 
      }); 

增強

在查詢

這使得所有的濾波由參數成一個單一的查詢中使用一個臨時表。隨後的查詢是通過ID進行的簡單選擇。

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 

應用一個BaseDTO平等

幫助一旦你的BaseDTO,來包裝你的ID,你可以簡單地說的東西,如:cars.Where(W => w.Equals(車)),詞典[汽車](如果它在那裏),如果(car.Equals(otherCar)),或results.GroupBy(GB => gb.Car)...

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; } 
} 
+0

很好,我會給這個去吧。從第一眼我就喜歡選項2 ...儘量保持代碼中的邏輯並保持存儲過程'更清潔' –

+1

選項2的缺點是查詢大小會更加豐富...如果汽車有50個選項該列數據重複50次(每個選項一次)。不是一個大問題,但無論如何。 – BlackjacketMack

+0

好點。它可能有多達70個選項......不大可能有10個以上的選項,但可能有多達70個。除了有一點選項1之外,你可以看到有什麼缺點。除SP之外還有很多邏輯。我猜想SP中的邏輯具有如果需要修復時可以直接部署的優勢 –