Linq在select语句中调用方法非常慢

时间:2017-02-15 12:01:13

标签: c# linq

我试图找到是否有办法从我的linq select语句中调用一个方法来构建一个不会显着减慢它的对象列表。这背后的原因是我还想在尝试仅获取其中一个对象时调用相同的方法,并且不希望必须维护这两个版本(即,如果我有另一个字段添加到对象或想要渲染其中一个不同的字段我不必在多个地方更改它。)

在下面的示例中,TEST 1的运行速度比TEST 2快100倍:

// Start timer
var timer = new Stopwatch();
timer.Start();

var test = (from job in dc.Jobs
        where !job.archived
        select new JobExtended()
        {
            JobId = job.jobId,
            NodeId = job.nodeId,
            JobName = job.name != string.Empty ? job.name : "TBC",
            Salary = job.salary,
            RecruiterId = job.fkRecruiterId,
            RecruiterNodeId = job.JobRecruiter != null ? job.JobRecruiter.recruiterNodeId : null,
            RecruiterName = job.JobRecruiter != null ? job.JobRecruiter.name : string.Empty,
            LocationId = job.fkLocationId,
            Location = job.refJobLocation != null ? job.refJobLocation.jobLocation : "",
            ContractTypeId = job.fkContractTypeId,
            ContractType = job.refJobContractType != null ? job.refJobContractType.contractType : "",
            CategoryId = job.fkCategoryId,
            Category = job.refJobCategory != null ? job.refJobCategory.category : "",
            ClosingDate = job.closingDate,
            Featured = job.featured,
            JobOfTheWeek = job.jobOfTheWeek,
            PublishedDate = job.publishedDate,
            Url = "/jobs/" + job.name.Replace(" ", "-").Replace("&", "and").Replace("'", "") + (job.fkLocationId.HasValue ? "-in-" + job.refJobLocation.jobLocation.Replace(" ", "-").Replace("&", "and").Replace("'", "") : "") + "-jn" + job.jobId,
            CreatedOn = job.createdOnDate,
            PrintWidth = job.printWidth,
            PrintHeight = job.printHeight,
            UntilFilled = (job.untilFilled != null && job.untilFilled.Value),
            AdvertCost = job.advertCost,
            DatesToShow = job.relJobDates.Where(x => x.fkJobId == job.jobId).Select(x => x.date).ToList(),
            IsParentJob = job.relLinkedJobs != null && job.relLinkedJobs.Any(x => x.fkParentJobId == job.jobId),
            IsAlternateWeekJob = job.alternateWeek != null && job.alternateWeek.Value,
            Archived = job.archived,
            LastModifiedDate = job.lastModifiedDate,
            RecruiterContactDetails = job.recruiterContactDetails
        }).ToList();

// Stop timer
timer.Stop();

// Output info
litTest.Text = "TEST 1 in " + timer.Elapsed.TotalSeconds + " seconds<br/>";

//Start timer
timer = new Stopwatch();
timer.Start();

var test2 = (from job in dc.Jobs
            where !job.archived
            select GetJobDetails(job)).ToList();

//Stop timer
timer.Stop();

//Output info
litTest.Text += "TEST 2 in " + timer.Elapsed.TotalSeconds + " seconds<br/>";

这是TEST 2调用的方法,该方法应该创建在TEST 1中返回的相同对象:

public static JobExtended GetJobDetails(Data.Job job)
{
    return new JobExtended()
    {
        JobId = job.jobId,
        NodeId = job.nodeId,
        JobName = job.name != string.Empty ? job.name : "TBC",
        Salary = job.salary,
        RecruiterId = job.fkRecruiterId,
        RecruiterNodeId = job.JobRecruiter != null ? job.JobRecruiter.recruiterNodeId : null,
        RecruiterName = job.JobRecruiter != null ? job.JobRecruiter.name : string.Empty,
        LocationId = job.fkLocationId,
        Location = job.refJobLocation != null ? job.refJobLocation.jobLocation : "",
        ContractTypeId = job.fkContractTypeId,
        ContractType = job.refJobContractType != null ? job.refJobContractType.contractType : "",
        CategoryId = job.fkCategoryId,
        Category = job.refJobCategory != null ? job.refJobCategory.category : "",
        ClosingDate = job.closingDate,
        Featured = job.featured,
        JobOfTheWeek = job.jobOfTheWeek,
        PublishedDate = job.publishedDate,
        Url = "/jobs/" + job.name.Replace(" ", "-").Replace("&", "and").Replace("'", "") + (job.fkLocationId.HasValue ? "-in-" + job.refJobLocation.jobLocation.Replace(" ", "-").Replace("&", "and").Replace("'", "") : "") + "-jn" + job.jobId,
        CreatedOn = job.createdOnDate,
        PrintWidth = job.printWidth,
        PrintHeight = job.printHeight,
        UntilFilled = (job.untilFilled != null && job.untilFilled.Value),
        AdvertCost = job.advertCost,
        DatesToShow = job.relJobDates.Where(x => x.fkJobId == job.jobId).Select(x => x.date).ToList(),
        IsParentJob = job.relLinkedJobs != null && job.relLinkedJobs.Any(x => x.fkParentJobId == job.jobId),
        IsAlternateWeekJob = job.alternateWeek != null && job.alternateWeek.Value,
        Archived = job.archived,
        LastModifiedDate = job.lastModifiedDate,
        RecruiterContactDetails = job.recruiterContactDetails
    };
}

原因是因为我希望能够调用“GetJobDetails”来返回单个作业,例如:

    public JobExtended GetJobDetails(int jobId)
    {
        using (DataContext dc = new DataContext())
        {
            return dc.Jobs.Where(x => x.jobId == jobId).Select(j => GetJobDetails(j)).FirstOrDefault();
        }
    }

这样做只允许我更新“GetJobDetails”方法,例如我决定添加一个新的变更字段如何生成“Url”值,但这样做的方式要慢得多。有没有解决方法,我已经尝试过以下似乎没有帮助的方法:

var test3 = (from job in dc.Jobs
                where !job.archived
                select job).AsEnumerable()
                .Select(GetJobDetails).ToList();

var test4 = (from job in dc.Jobs
                where !job.archived
                select GetJobDetails(job));
var test4a = test4.ToList();

2 个答案:

答案 0 :(得分:1)

TEST 1更快的原因是因为查询在服务器上执行一次并且只返回选定的字段。

var test = (from job in dc.Jobs
    where !job.archived
    select new JobExtended()
    {
        JobId = job.jobId,
        NodeId = job.nodeId,
        ...
    }).ToList();

当您在TEST 2中调用GetJobDetails时,参数j需要首先实现,然后才能将其作为参数发送到GetJobDetails。因此,有多个完整对象的调用。

return dc.Jobs.Where(x => x.jobId == jobId).Select(j => GetJobDetails(j)).FirstOrDefault();

为了达到你想要的效果,你应该使用扩展方法。这个延伸了IQueryable。

    public static IEnumerable<JobExtended> SelectJobExtended(this IQueryable<Data.Job> query)
    {
        return query
            .Select(o => new JobExtended()
            {
                JobId = job.jobId,
                NodeId = job.nodeId,
                ...
            }
    }

然后你可以打电话:

dc.Jobs.Where(x => x.jobId == jobId).SelectJobExtended().FirstOrDefault();

答案 1 :(得分:0)

我以前见过这种问题。如果我记得,我们所做的是&#34;堆积&#34;查询。

public IEnumerable<JobExtended> ConvertToJobExtended(IEnumerable<Job> jobs)
{
    return
        from job in jobs
        select new JobExtended()
        {
            MyString = job.MyInt.ToString(),
            ...
        };
}

然后您可以通过以下方式调用它:

var query = (from job in dc.Jobs
        where !job.archived
        select job;

var test2 = ConvertToJobExtended(query).ToList();

有很多替代方案可以从这里开始......我希望这是你正在寻找的正确方向。