Linq To SQL选择动态列

jwd*_*y13 10 c# sql linq

是否可以动态限制从LINQ to SQL查询返回的列数?

我有一个包含50多列的数据库SQL视图.我的应用程序有一个包含50多个属性的域对象,每列一个.在我的winforms项目中,我将域对象列表绑定到网格.默认情况下,只有少数列可见,但用户可以打开/关闭任何列.

用户抱怨网格加载时间太长.我捕获了LINQ生成的SQL查询,然后在SQL Server Management Studio中执行它并验证它的速度很慢.如果我改变SQL语句,删除所有不可见的列,它几乎立即运行.性能与查询中的列数之间存在直接关联.

我想知道是否有可能动态地改变从LINQ生成的SQL查询返回的列数?例如,以下是我的代码目前的样子:

public List<Entity> GetEntities()
{
    using (var context = new CensusEntities())
    {
        return (from e in context.Entities
            select e).ToList();
    }
}
Run Code Online (Sandbox Code Playgroud)

context.Entities对象是从包含超过50列的SQL视图生成的,因此当上面执行时,它生成SQL,如"SELECT Col1,Col2,Col3,... Col50 FROM Entity INNER JOIN ...".我想将方法​​签名更改为如下所示:

public List<Entity> GetEntities(string[] visibleColumns)
{
    using (var context = new CensusEntities())
    {
        return (from e in context.Entities
            select e).ToList();
    }
}
Run Code Online (Sandbox Code Playgroud)

我不确定如何更改此方法的主体以将生成的SQL语句更改为仅返回我关心的列值,其他所有可以为NULL.

Alb*_*nbo 0

动态地执行此操作并不简单,但是如果您想要检索一组有限的列组合,则可以执行如下显式选择:

public List<Entity> GetEntities()
{
    using (var context = new CensusEntities())
    {
        return (from e in context.Entities
            select new
            {
                col1 = e.col1,
                col4 = e.col4,
                col5 = e.col5,
            }
        ).ToList()
        .Select(x=>new Entity{col1 = x.col1, col4 = x.col4, col5 = x.col5}).ToList();
    }
}
Run Code Online (Sandbox Code Playgroud)

额外的选择步骤是必要的,因为 LINQ2SQL 不会为您创建部分实体。

为用户想要检索的每个常见列组合(尤其是首字母组合)创建一个方法。

但是,为了使这种动态化,您可以使用存储为匿名类中的属性的实体构建查询,并在同一匿名类的第二个属性中的另一个匿名类中收集结果属性。最后,您从收集的对象中选择实体到正确类型的对象中。

public List<Entity> GetEntities()
{
    using (var context = new CensusEntities())
    {
        var combinedResult = (from e in context.Entities
            select new {
                Entity = e,
                CollectedValues = new
                                  {
                                      // Insert default values of the correct type as placeholders
                                      col1 = 0, // or "" for string or false for bool
                                      col2 = 0, // or "" for string or false for bool
                                      // ...
                                      col49 = 0, // or "" for string or false for bool
                                      col50 = 0, // or "" for string or false for bool
                                  }
        );

        // Then copy each requested property

        // col1
        if (useCol1)
        {
            var combinedResult = (from e in combinedResult
                select new {
                    Entity = e,
                    CollectedValues = new
                                      {
                                          col1 = e.Enitity.col1, // <-- here we update with the real value
                                          col2 = e.CollectedValues.col2, // <-- here we just use any previous value
                                          // ...
                                          col49 = e.CollectedValues.col49, // <-- here we just use any previous value
                                          col50 = e.CollectedValues.col50, // <-- here we just use any previous value                                          }
            );
        }

        // col2
        if (useCol2)
        {
         // same as last time
                                          col1 = e.CollectedValues.col1, // <-- here we just use any previous value
                                          col2 = e.Enitity.col2, // <-- here we update with the real value
                                          // ...
        }

        // repeat for all columns, update the column you want to fetch

        // Just get the collected objects, discard the temporary
        // Entity property. When the query is executed here only
        // The properties we actually have used from the Entity object
        // will be fetched from the database and mapped.
        return combinedResult.Select(x => x.CollectedValues).ToList()
        .Select(x=>new Entity{col1 = x.col1, col2 = x.col2, ... col50 = x.col50}).ToList();
    }
}
Run Code Online (Sandbox Code Playgroud)

虽然会有很多代码,并且维护起来很痛苦,但它应该可以工作。
如果您打算走这条路,我建议您构建一个代码生成器,通过 LINQ 上下文的反射来构建此代码。