c# - 如何在 LINQ 中加入未知数量的列表

标签 c# sql-server linq

我有三个不同类型的列表:

List<Customer> customerList = new List<Customer>();
List<Product> productList = new List<Product>();
List<Vehicle> vehicleList = new List<Vehicle>();

我也有这个列表

List<string> stringList = {"AND","OR"};

由于 stringList 的第一个元素是 AND 我想与 customerListproductList 进行内部连接。然后我想右加入 vehicleList 结果如下:

from cust in customerList 
join prod in productList on cust.ProductId equals prod.Id
join veh in vehicleList on prod.VehicleId equals veh.Id into v
from veh in v.DefaultIfEmpty()
select new {customerName = cust.Name, customerVehicle=veh.VehicleName}

我想以自动化的方式进行,假设我有 N 个列表和 N-1ANDOR,我怎样才能加入他们?此外,可以有许多相同类型的列表。这样的事情甚至可能吗?如果不是,我该怎么做才能使它更接近我的需要?提前致谢。

编辑: 我将列表及其类型保存在这样的字典中:

var listDict = new Dictionary<Type, object>();

所以我可以在必要时在这个字典中迭代。

最佳答案

更新 5-15-17:

只是为了回顾一下,我提议的是一个我们想要的例子:

  1. 传入一个包含 N 个 Table 对象的列表。
  2. 传入一个包含 N-1 个连接子句的列表,说明如何连接它们。 EG:您有 2 个表需要一个连接,3 个表需要 2 个,依此类推。
  3. 我们希望传递一个谓词以在链中向上或向下以缩小范围。

我的建议是在 SQL 中完成所有这些操作,然后将一个它可以解析的 xml 对象传递给 SQL。然而,为了让不处理 XML 序列化变得更简单一些,让我们坚持使用本质上是一个或多个要传递的值的字符串。假设我们有一个像这样的结构:

/*
CREATE TABLE Customer ( Id INT IDENTITY, CustomerName VARCHAR(64), ProductId INT)
INSERT INTO Customer VALUES ('Acme', 1),('Widgets', 2)
CREATE TABLE Product (Id INT IDENTITY, ProductName VARCHAR(64), VehicleId INT)
Insert Into Product Values ('Shirt', 1),('Pants', 2)
CREATE TABLE VEHICLE (Id INT IDENTITY, VehicleName VARCHAR(64))
INSERT INTO dbo.VEHICLE VALUES ('Car'),('Truck')

CREATE TABLE Joins (Id INT IDENTITY, OriginTable VARCHAR(32), DestinationTable VARCHAR(32), JoinClause VARCHAR(32))
INSERT INTO Joins VALUES ('Customer', 'Product', 'ProductId = Id'),('Product', 'Vehicle', 'VehicleId = Id')

--Data as is if I joined all three tables
CustomerId  CustomerName    ProductId   ProductName VehicleId   VehicleName
1   Acme    1   Shirt   1   Car
2   Widgets 2   Pants   2   Truck
*/

这个结构非常简单,一切都是一对一的关键关系,而不是它可以有一些其他标识符。使事情正常进行的关键是维护一个描述这些表如何关联的表。我称这个表连接。现在我可以像这样创建一个动态过程:

CREATE PROC pDynamicFind
  (
    @Tables varchar(256)
  , @Joins VARCHAR(256)
  , @Predicate VARCHAR(256)
  )
AS
BEGIN
  SET NOCOUNT ON;

    DECLARE @SQL NVARCHAR(MAX) = 
'With x as 
    (
    SELECT
    a.Id
  , {nameColumns}
  From {joins}
  Where {predicate}
  )
SELECT *
From x
  UNPIVOT (Value FOR TableName In ({nameColumns})) AS unpt
'
    DECLARE @Tbls TABLE (id INT IDENTITY, tableName VARCHAR(256), joinType VARCHAR(16))
    DECLARE @Start INT = 2
    DECLARE @alphas VARCHAR(26) = 'abcdefghijklmnopqrstuvwxyz'

    --Comma seperated into temp table (realistically most people create a function to do this so you don't have to do it over and over again)
    WHILE LEN(@Tables) > 0
    BEGIN
        IF PATINDEX('%,%', @Tables) > 0
        BEGIN
            INSERT INTO @Tbls (tableName) VALUES (RTRIM(LTRIM(SUBSTRING(@Tables, 0, PATINDEX('%,%', @Tables)))))
            SET @Tables = SUBSTRING(@Tables, LEN(SUBSTRING(@Tables, 0, PATINDEX('%,%', @Tables)) + ',') + 1, LEN(@Tables))
        END
        ELSE
        BEGIN
            INSERT INTO @Tbls (tableName) VALUES (RTRIM(LTRIM(@Tables)))
            SET @Tables = NULL
        END
    END

    --Have to iterate over this one seperately
    WHILE LEN(@Joins) > 0
    BEGIN
        IF PATINDEX('%,%', @Joins) > 0
        BEGIN
            Update @Tbls SET joinType = (RTRIM(LTRIM(SUBSTRING(@Joins, 0, PATINDEX('%,%', @Joins))))) WHERE id = @Start
            SET @Joins = SUBSTRING(@Joins, LEN(SUBSTRING(@Joins, 0, PATINDEX('%,%', @Joins)) + ',') + 1, LEN(@Joins))
            SET @Start = @Start + 1
        END
        ELSE
        BEGIN
            Update @Tbls SET joinType = (RTRIM(LTRIM(@Joins))) WHERE id = @Start
            SET @Joins = NULL
            SET @Start = @Start + 1
        END
    END

    DECLARE @Join VARCHAR(256) = ''
    DECLARE @Cols VARCHAR(256) = ''

    --Determine dynamic columns and joins
    Select 
      @Join += CASE WHEN joinType IS NULL THEN t.tableName + ' ' + SUBSTRING(@alphas, t.id, 1) 
      ELSE ' ' + joinType + ' JOIN ' + t.tableName + ' ' + SUBSTRING(@alphas, t.id, 1) + ' ON ' + SUBSTRING(@alphas, t.id-1, 1) + '.' + REPLACE(j.JoinClause, '= ', '= ' + SUBSTRING(@alphas, t.id, 1) + '.' )
      END
    , @Cols += CASE WHEN joinType IS NULL THEN t.tableName + 'Name' ELSE ' , ' + t.tableName + 'Name' END
    From @Tbls t
      LEFT JOIN Joins j ON t.tableName = j.DestinationTable

    SET @SQL = REPLACE(@SQL, '{joins}', @Join)
    SET @SQL = REPLACE(@SQL, '{nameColumns}', @Cols)
    SET @SQL = REPLACE(@SQL, '{predicate}', @Predicate)

    --PRINT @SQL
    EXEC sp_executesql @SQL
END
GO

我现在有一种媒介来查找使其成为 stub 查询的东西,可以说我可以替换 from 语句的来源、我查询的内容、我用来查询的值。我会得到这样的结果:

EXEC pDynamicFind 'Customer, Product', 'Inner', 'CustomerName = ''Acme'''
EXEC pDynamicFind 'Customer, Product, Vehicle', 'Inner, Inner', 'VehicleName = ''Car'''

现在如何在 EF 中设置它并在代码中使用它?那么您可以将过程添加到 EF 并从中获取数据作为上下文。这个问题的答案是,尽管我可能添加了很多列,但我现在实际上是在返还一个固定的对象。如果我的模式始终是 N 个表的“(表)名称”,我可以通过逆透视来规范化我的结果,然后只为我拥有的许多表获取 N 行。因此,当您获得更大的结果集时,性能可能会更差,但只要使用类似的结构,就可以进行任意数量的连接。

不过,我要说的是,SQL 最终会获取您的数据,而 Linq 导致的疯狂连接有时会超出其值(value)。但是如果你有一个小的结果集和一个小的数据库,你可能没问题。这只是一个示例,说明如何使用动态 sql 在 SQL 中获取完全不同的对象,以及在编写 proc 代码后它可以多快地执行某些操作。这只是给猫剥皮的一种方法,我相信有很多。问题是无论你采用动态连接或解决问题的方法走什么路,都需要某种类型的规范化标准、工厂模式或它说我可以有 N 个输入,无论如何总是产生相同的 X 对象的东西.我通过垂直结果集执行此操作,但如果您想要一个不同于“名称”的列,您也将不得不为此编写更多代码。但是,如果您想要描述但又想为日期字段做一个谓词,我构建它的方式就可以了。

关于c# - 如何在 LINQ 中加入未知数量的列表,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/43895621/

相关文章:

java - 适用于 Java 9 的 JDBC 和 MS SQL Server 驱动程序

c# - LINQ 提示子查询返回多于 1 行

sql - 如何在 Linq 中编写此交叉表类型查询(提供表和数据)

c# - 使用相同的 SQL 生成输出将 T-SQL 转换为 Fluent Linq C#

c# - 使用 Moq 测试接受委托(delegate)的方法

c# - 查询 Entity Framework 中的第 n 行

c# - .Net Entity Framework 循环级联路径

c# - KeyDown 未拾取 'Return' 键 C#

c# - 添加到 System.Web.Caching.Cache 的项目的默认行为是什么?

sql - 替换 SQL Server XML 中的属性名称