2017年2月24日 星期五

【Entity Framework】Using WHERE IN Clause in Stored Procedure by Entity Framework

這題的問題在 EF ObjectContext 的 YourDbEntities 幫你匯進來的方法參數部分是根據宣告內容哈死的,比方說
  1. Create Proc usp_SelectSomething
  2. @Column1 int,
  3. @Column2 varchar(255)
  4. As
  5. Select * From Table
  6. Where Column1 = @Column1 AND Column2 = @Column2
  7. Go
這樣的話在C# EF 取就會是
  1. public ObjectResult sp_SelectSomething(global::System.Int32 column1, global::System.String column2)
  2. {
  3. ...
  4. }
  5. _MyDbEntitie.usp_SelectSomething(column1, column2);
但是這樣就沒辦法應對 WHERE col IN(...) 的應用,因為無法動態產生不定數量的參數。

最簡單的方法是用這篇裡面寫到的作弊方法,這樣
  1. ALTER PROCEDURE [dbo].[usp_CheatedSelectProductByIds]
  2. @productIds nvarchar(max)
  3. AS
  4. BEGIN
  5. SET NOCOUNT ON
  6. DECLARE @cmd nvarchar(max)
  7. SET @cmd = N'SELECT * FROM Product WHERE id IN (' + @productIds + ')'
  8. EXEC sp_executesql @cmd
  9. END
  1. string cmdStr = "usp_CheatedSelectProductByIds @productIds";
  2. MyLabEntities entities = new MyLabEntities();
  3. var products = entities.Database.SqlQuery<Product>(
  4. cmdStr, new SqlParameter("productIds", input)).ToList();
但是如果你想用
  1. var products2 = entities.usp_CheatedSelectProductByIds(input).ToList();
這樣的方式,就必須先參考這篇,設定回傳集合為複雜型別,而且因為是用 sp_executesql 去串字串 cmd,EF 不會幫你把複雜型別建出來,你要自己先去手動建出來才有得選,參考
當然,如果你不用 sp_executesql
  1. ALTER PROC [dbo].[usp_SelectProductByIds]
  2. @productIds varchar(max)
  3. As
  4. SELECT * FROM Product
  5. WHERE id IN (@productIds)
EF 就會自己幫你把複雜型別建出來;但同時你也會發現不管是entities.Database.SqlQuery() 或 entities.usp_SelectProductByIds() 都會查不到東西或噴掉。 其實上面用 sp_executesql 硬串命令是有 inject 風險的,SO ref

所以比較妥當的方法是用你傳入的 CSV 規格字串做一個 temp table,然後以那個 temp table 做子查詢給你的 WHERE IN,參考
  1. CREATE PROC [dbo].[usp_StableSelectProductByIds]
  2. @productIds nvarchar(max)
  3. As
  4.  
  5. DECLARE @Delimeter char(1)
  6. SET @Delimeter = ','
  7.  
  8. DECLARE @tmpTable TABLE(Id nvarchar(50))
  9. DECLARE @Id nvarchar(50)
  10. DECLARE @StartPos int, @Length int
  11. WHILE LEN(@productIds) > 0
  12. BEGIN
  13. SET @StartPos = CHARINDEX(@Delimeter, @productIds)
  14. IF @StartPos < 0 SET @StartPos = 0
  15. SET @Length = LEN(@productIds) - @StartPos - 1
  16. IF @Length < 0 SET @Length = 0
  17. IF @StartPos > 0
  18. BEGIN
  19. SET @Id = SUBSTRING(@productIds, 1, @StartPos - 1)
  20. SET @productIds = SUBSTRING(@productIds, @StartPos + 1, LEN(@productIds) - @StartPos)
  21. END
  22. ELSE
  23. BEGIN
  24. SET @Id = @productIds
  25. SET @productIds = ''
  26. END
  27. INSERT @tmpTable (Id) VALUES(@Id)
  28. END
  29.  
  30. SELECT * FROM Product
  31. WHERE id IN (SELECT Id FROM @tmpTable)


沒有留言:

張貼留言