Entity Framework - 仅获取列表中动态指定的特定列
Entity Framework - only get specific columns specified dynamically from list
我希望有一些灵活性,并提供一些接口来动态指定应包含在最终 select 中的列列表。
例如这个 table
public class Person
{
[DatabaseGeneratedAttribute(DatabaseGeneratedOption.Identity), Key()]
public int Id { get; set; }
[Required]
public string FirstName { get; set; }
[Required]
public string LastName { get; set; }
public string Address { get; set; }
...
}
我想要一个案例
persons.Select(p => new { FirstName = p.FirstName, LastName = p.LastName }).ToList();
但在另一个我想要
persons.Select(p => new { FirstName = p.FirstName, LastName = p.LastName, Address = p.Address }).ToList();
在第三种情况下,我需要其他东西...所以,我认为最好有一些灵活的机制,允许指定要提取的列列表。
有什么办法可以做到这一点吗?
我阅读了一些有关 LINQ 表达式的内容,我觉得这是正确的方法,但还不明白如何实现它...
如果包含 NuGet 包 System.Linq.Dynamic 就非常简单了:
var columns = new [] { "FirstName", "LastName" };
var selectText = "new (" + string.Join(", ", columns) + ")";
var result = Users.AsQueryable().Select(selectText);
或者为了类型安全
var columns = new Expression<Func<User, object>> [] { x => x.FirstName, x => x.Surname };
var selectText = "new (" + string.Join(", ", columns.Select(x => ((MemberExpression)x.Body).Member.Name)) + ")";
var result = Users.AsQueryable().Select(selectText);
这是将表达式转换为字符串以用于将它们返回的库,因此它不是最优化的方法,但与该数据库查询相比,成本可能微不足道。
您可以通过构建表达式来完成,但库会为您完成这一切。
基本上这是您的正确做法。您只能通过将 classes 用作数据的 'Models' 来使其更舒适 table 并输入安全。基本上,创建另一个 Person class,其中包含 table 的所有字段或仅包含您在 'person' 中需要的字段。这样做而不是仅仅创建无名对象并将它们写入列表可以为您提供更大的灵活性,因为您可以添加其他方法来转换此 class.
中的数据
您还可以根据要初始化的字段向此 class 添加不同的构造函数。像这样:
var modelPersons = persons.Select(p => new ModelPerson(p.FirstName, p.LastName)).ToList();
如果您为每个需要的初始化都编写一个构造函数,那么在查询时您将拥有可重用的短代码。当然,你仍然可以使用这样的对象初始化器:
var modelPersons = persons.Select(p => new ModelPerson {
FirstName = p.FirstName
LastName = p.LastName
}).ToList();
这与您发布的一样长,但最好使用一个您可以自己定义的对象,添加方法,格式化数据等。但总的来说,您使用的是正确的方法。
扩展 Mant101 的回答。您可以通过对 SO 问题使用 extern alias. See also this answer 来解决重复命名空间问题。
我希望有一些灵活性,并提供一些接口来动态指定应包含在最终 select 中的列列表。
例如这个 table
public class Person
{
[DatabaseGeneratedAttribute(DatabaseGeneratedOption.Identity), Key()]
public int Id { get; set; }
[Required]
public string FirstName { get; set; }
[Required]
public string LastName { get; set; }
public string Address { get; set; }
...
}
我想要一个案例
persons.Select(p => new { FirstName = p.FirstName, LastName = p.LastName }).ToList();
但在另一个我想要
persons.Select(p => new { FirstName = p.FirstName, LastName = p.LastName, Address = p.Address }).ToList();
在第三种情况下,我需要其他东西...所以,我认为最好有一些灵活的机制,允许指定要提取的列列表。
有什么办法可以做到这一点吗?
我阅读了一些有关 LINQ 表达式的内容,我觉得这是正确的方法,但还不明白如何实现它...
如果包含 NuGet 包 System.Linq.Dynamic 就非常简单了:
var columns = new [] { "FirstName", "LastName" };
var selectText = "new (" + string.Join(", ", columns) + ")";
var result = Users.AsQueryable().Select(selectText);
或者为了类型安全
var columns = new Expression<Func<User, object>> [] { x => x.FirstName, x => x.Surname };
var selectText = "new (" + string.Join(", ", columns.Select(x => ((MemberExpression)x.Body).Member.Name)) + ")";
var result = Users.AsQueryable().Select(selectText);
这是将表达式转换为字符串以用于将它们返回的库,因此它不是最优化的方法,但与该数据库查询相比,成本可能微不足道。
您可以通过构建表达式来完成,但库会为您完成这一切。
基本上这是您的正确做法。您只能通过将 classes 用作数据的 'Models' 来使其更舒适 table 并输入安全。基本上,创建另一个 Person class,其中包含 table 的所有字段或仅包含您在 'person' 中需要的字段。这样做而不是仅仅创建无名对象并将它们写入列表可以为您提供更大的灵活性,因为您可以添加其他方法来转换此 class.
中的数据您还可以根据要初始化的字段向此 class 添加不同的构造函数。像这样:
var modelPersons = persons.Select(p => new ModelPerson(p.FirstName, p.LastName)).ToList();
如果您为每个需要的初始化都编写一个构造函数,那么在查询时您将拥有可重用的短代码。当然,你仍然可以使用这样的对象初始化器:
var modelPersons = persons.Select(p => new ModelPerson {
FirstName = p.FirstName
LastName = p.LastName
}).ToList();
这与您发布的一样长,但最好使用一个您可以自己定义的对象,添加方法,格式化数据等。但总的来说,您使用的是正确的方法。
扩展 Mant101 的回答。您可以通过对 SO 问题使用 extern alias. See also this answer 来解决重复命名空间问题。