在LINQ中调用类构造函数

时间:2019-07-01 11:27:16

标签: c# entity-framework linq

我一直在尝试一些技巧,但仍然找不到我满意的东西。

如果我做类似的事情;

        IQueryable<TestClass> tests = new MyDbContext()
            .LdbRecords
            .Select(r => new TestClass() { Id = r.RecordId, Name = r.RecordName });
        Console.WriteLine(tests.Count());

EF执行相对明智的查询;

SELECT 
    [GroupBy1].[A1] AS [C1]
FROM ( SELECT 
    COUNT(1) AS [A1]
    FROM [dbo].[ldb_record] AS [Extent1]
)  AS [GroupBy1]

我试图摆脱使用无参数构造函数的情况。主要目标是-当我们添加新属性时,不必费心去更新每个新的类实例。取而代之的是,我们修改构造函数,这将破坏更新该类的所有代码段,直到添加了其他参数(确保我们不会忘记在任何地方添加新的属性映射)。如果有更好的方法可以做到这一点,那么我就不知所措!

构造函数如下;

        public TestClass(int id, string name)
        {
            Id = id;
            Name = name;
        }

到目前为止,我发现了每种技术;例如

        IEnumerable<TestClass> tests = new MyDbContext()
            .LdbRecords
            .AsEnumerable()
            .Select(r => new TestClass(r.RecordId, r.RecordName));
        Console.WriteLine(tests.Count());

对SQL查询的生成有不利影响;

SELECT 
  [Extent1].[record_id] AS [record_id], 
  [Extent1].[record_name] AS [record_name], 
  [Extent1].[record_note] AS [record_note]
FROM [dbo].[ldb_record] AS [Extent1]

是否有一种我可以使用的技术(也许使用委托,表达式或函数?)可以使我使用我的TestClass构造函数而无需枚举?

我很高兴在TestClass中有一个私有的无参数构造函数,函数/委托等可以调用该构造函数?

谢谢

3 个答案:

答案 0 :(得分:0)

我想出了

    IQueryable<TestClass> tests = new MyDbContext()
        .LdbRecords
        .Select(TestClass.ConvertPoco);
    Console.WriteLine(tests.Count());

    public class TestClass
    {
        public int Id { get; set; }
        public string Name { get; set; }

        private TestClass()
        {

        }

        public TestClass(int id, string name)
        {
            Id = id;
            Name = name;
        }

        public static Expression<Func<LdbRecord, TestClass>> ConvertPoco
        {
            get
            {
                return p => new TestClass() { Id = p.RecordId, Name = p.RecordName };
            }
        }
    }

与无参数构造函数技术得出的查询结果完全相同;

SELECT 
    [GroupBy1].[A1] AS [C1]
FROM ( SELECT 
    COUNT(1) AS [A1]
    FROM [dbo].[ldb_record] AS [Extent1]
)  AS [GroupBy1]

对此有何进展?

答案 1 :(得分:0)

您产生的SQL是不同的,因为它们返回的结果是不同的。在第一个示例中,(最后)您要求数据库提供的只是一个计数,Linq为您提供了一个计数。您永远不会使用实体本身,因此Linq永远不会查询它们。

在第二个示例中,您通过AsEnumerable调用来请求实体,指示Linq从数据库上下文切换到内存上下文。因此,当您要求计数时,Linq必须先从数据库 中获取记录,然后再对它们进行计数。

如果您在第一个示例中实际使用实体,则数据库查询将非常相似(可能不包括未使用的列,该列可以忽略不计)。如果您只需要计数,则无需使用 any 构造函数。

一个简单的测试,看看枚举第一个示例会生成什么查询,将输出更改为:

Console.WriteLine(tests.AsEnumerable().Count());

答案 2 :(得分:0)

您要解决的问题似乎已经解决了。

假设:

 var tests = new MyDbContext()
        .LdbRecords         
        .Select(r => new LdbRecord());

您将获得一个LdbRecord的IQueryable,并通过调用.ToList()这些数据库模型的列表以及其所有属性来获取。

然后您应该使用诸如AutoMapper之类的东西。接下来的逻辑将是这样的:

 var results = Mapper.Map<TestClass>(tests.ToList());

您可以将AutoMapper配置为与属性名称匹配,但也可以将属性配置为忽略,或与1到1匹配。

var mapper = config.CreateMapper<LdbRecord, TestClass>();
mapper.ForMember(dest => dest.TestId, opt => opt.MapFrom(src => src.Id));

这可确保您的代码编写一次(无需遍历代码库即可将参数更改为构造函数。

此外,您可以强制AutoMapper为未映射的属性引发异常。如果仅靠AutoMapper来实现TestClass.Id和LdbRecords.Id还不够好,则该应用程序将无法构建/运行(在这里单元测试也是理想的选择)。