是否可以动态限制从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();
}
}
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();
}
}
我不确定如何更改此方法的主体以将生成的SQL语句更改为仅返回我关心的列值,所有其他值都可以为NULL。
答案 0 :(得分:4)
这样的事情应该有效:
List<string> columns = new List<string>();
columns.Add("EmployeeID");
columns.Add("HireDate");
columns.Add("City");
在列表中添加列^。
var result = Class.ReturnList(columns);
将List传递给方法^。
public static List<Entity> ReturnList(List<string> VisibleColumns)
{
StringBuilder SqlStatement = new StringBuilder();
SqlStatement.Append("Select ");
for (int i = 0; i < VisibleColumns.Count; i++)
{
if (i == VisibleColumns.Count - 1)
{
SqlStatement.Append(VisibleColumns[i]);
}
else
{
SqlStatement.Append(VisibleColumns[i]);
SqlStatement.Append(",");
}
}
SqlStatement.Append(" FROM Entity");
using (var ctx = new DataClasses1DataContext())
{
var result = ctx.ExecuteQuery<Entity>(SqlStatement.ToString());
return result.ToList();
}
}
这基本上只会生成一个SELECT
语句,其中包含您使用VisibleColumns
列表传入的所有字段。
在这种情况下,将由VisibleColumns
列表中的字符串生成的SQL语句为:
Select EmployeeID, HireDate, City From Employee
(注意:我使用Northwind数据库来尝试这个,因此使用EmployeeID等列名。显然你应该用你自己的名称替换它们。)
答案 1 :(得分: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();
}
}
额外选择步骤是必要的,因为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();
}
}
会有很多代码,并且需要维护,但它应该有效 如果您要使用此路由,我建议您构建一个代码生成器,使用LINQ上下文中的反射构建此代码。
答案 2 :(得分:0)
尝试这样的事情
using (var context = new CensusEntities())
{
var q = from e in context.Entities
select e.myfield1,e.myfield2;
return q.Tolist();
}
生成的查询应该更轻,以及下面的所有数据转换。 但是如果你真的需要构建动态输入,我认为应该涉及一些动态的sql。所以 1.构建dinymic sql并获取数据表 2.使用数据表进行动态对象转换,如下所示 How can I convert a DataTable into a Dynamic object? 顺便说一下,我认为你应该考虑使用第一块代码。