ASP.NET MVC C#linq查询将2个Ienumberable <data>结果集连接在一起

时间:2017-05-23 05:23:58

标签: sql asp.net-mvc linq join

我有一个返回40条记录的查询:

IEnumerable<SimData> simData = (from pov in db.PredictedObservedValues
                            join pod in db.PredictedObservedDetails on pov.PredictedObservedDetailsID equals pod.ID
                            join s in db.Simulations on pov.SimulationsID equals s.ID
                            where pov.PredictedObservedDetailsID == predictedObservedId && pov.ValueName == valueName
                            select new SimulationPredictedObserved()
                            {
                                TableName = pod.TableName,
                                SimulationName = s.Name,
                                ValueName = pov.ValueName,
                                PredictedValue = Math.Round((double)pov.PredictedValue, 3),
                                ObservedValue = Math.Round((double)pov.ObservedValue, 3),
                                Difference = (double?)Math.Round((double)pov.PredictedValue - (double)pov.ObservedValue, 3)
                            })
                            .Distinct();

我需要运行两次,每次传递一个不同的predictObservedID值。两个集合的valueName相同。

然后我使用以下内容加入了这两组:

IEnumerable<CombinedSimData> combinedData = (from PO1 in simData
                                         join PO2 in simData2
                                         on new {PO1.TableName, PO1.SimulationName, PO1.ValueName}
                                         equals new {PO2.TableName, PO2.SimulationName, PO2.ValueName}
                                         select new CurrentAndAcceptedDetail
                                         {
                                             TableName = PO1.TableName,
                                             SimulationName = PO1.SimulationName,
                                             ValueName = PO1.ValueName,
                                             CurrentPredictedValue = (double?)Math.Round((double)PO1.PredictedValue, 3),
                                             CurrentObservedValue = ((PO1.ObservedValue.HasValue) ? (double?)Math.Round((double)PO1.ObservedValue, 3) : 0), 
                                             CurrentDifference = ((PO1.PredictedValue.HasValue && PO1.ObservedValue.HasValue) ? (double?)Math.Round((double)PO1.PredictedValue - (double)PO1.ObservedValue, 3) : 0), 
                                             AcceptedPredictedValue = (double?)Math.Round((double)PO2.PredictedValue, 3),
                                             AcceptedObservedValue = ((PO2.ObservedValue.HasValue) ? (double?)Math.Round((double)PO2.ObservedValue, 3) : 0), 
                                             AcceptedDifference = ((PO2.PredictedValue.HasValue && PO2.ObservedValue.HasValue) ? (double?)Math.Round((double)PO2.PredictedValue - (double)PO2.ObservedValue, 3) : 0)
                                         })
                                         .Distinct();

每个单独的查询返回40条记录,但是当我将这两组连接在一起时,我得到了912条记录。用于连接的连接键对于每个查询中的每个项都是唯一的,并且存在于两组数据中。我期待的只有40条记录。

任何人都可以解释为什么这不起作用(即返回40条记录),以及我如何解决它?

非常感谢任何协助。

1 个答案:

答案 0 :(得分:0)

我深入研究了这个问题,我编写了一个基本的c#测试项目,并用一些基本数据模拟了db上下文。

我注意到的第一件事是对Distinct的调用。因为查询结果是CurrentAndAcceptedDetail,所以不能保证一个好的&#34; distinct&#34;如果它是在物体实现后运行的。 distinct将检查对象的引用,而不一定是它的属性。 这是关于它的article。关键是,无论你做什么不同,都需要实现IEquatable,以便它可以与另一个进行比较,以确定它是否与众不同。

下一点是它实际获取的数据。 因为联接是双向的,如果有2行&#34; a&#34;,&#34; b&#34;,&#34; c&#34;在simData和2行&#34; a,&#34; b&#34;,&#34; c&#34;在simData2中,它实际上会创建4行,因为simData.row1与simData2.row1和simData2.Row2都匹配。 simData.row2和simData2.row1以及simData2.row2也是如此。

结合在一起,你的不同的simData在返回的所有字段上,而不仅仅是你加入的3个字段。所以假设我们有以下数据:

SIMDATA

|"a"|"b"|"c"|1|2|3|
|"a"|"b"|"c"|4|5|6|

SimData2

|"a"|"b"|"c"|5|6|7|
|"a"|"b"|"c"|2|3|4|

这会产生4个结果。

|"a"|"b"|"c"|1|2|3|"a"|"b"|"c"|5|6|7|
|"a"|"b"|"c"|1|2|3|"a"|"b"|"c"|2|3|4|
|"a"|"b"|"c"|4|5|6|"a"|"b"|"c"|5|6|7|
|"a"|"b"|"c"|4|5|6|"a"|"b"|"c"|2|3|4|
如果没有您想要实现的实际数据,可能需要一些group by语句来获取最大值,或者在simData中找到最新记录,这有点难以进一步帮助。为了回答这个问题,我们需要更多的背景信息来实现您的目标。

与此同时,以下是我测试你的linq查询的代码。 (先决条件:单元测试项目中Moq和EntityFramework的Nuget包)

using System;
using System.Collections;
using System.Collections.Generic;
using System.Data.Entity;
using System.Linq;
using Microsoft.VisualStudio.TestTools.UnitTesting;
using Moq;

namespace StackOverflow.Tests
{
    [TestClass]
    public class Question44126393
    {
        public class PredictedObservedValue
        {
            public int ID { get; set; }
            public int PredictedObservedDetailsID { get; set; }
            public int SimulationsID { get; set; }
            public string ValueName { get; set; }
            public decimal PredictedValue { get; set; }
            public decimal ObservedValue { get; set; }
        }

        public class PredictedObservedDetail
        {
            public int ID { get; set; }
            public string TableName { get; set; }
        }
        public class Simulation
        {
            public int ID { get; set; }
            public string Name { get; set; }
        }

        public class SimulationPredictedObserved
        {
            public string TableName { get; set; }
            public string SimulationName { get; set; }
            public string ValueName { get; set; }
            public double? PredictedValue { get; set; }
            public double? ObservedValue { get; set; }
            public double? Difference { get; set; }
        }

        public class CurrentAndAcceptedDetail : IEquatable<CurrentAndAcceptedDetail>
        {
            public string TableName { get; set; }
            public string SimulationName { get; set; }
            public string ValueName { get; set; }
            public double? CurrentPredictedValue { get; set; }
            public double? CurrentObservedValue { get; set; }
            public double? CurrentDifference { get; set; }
            public double? AcceptedPredictedValue { get; set; }
            public double? AcceptedObservedValue { get; set; }
            public double? AcceptedDifference { get; set; }

            public bool Equals(CurrentAndAcceptedDetail other)
            {
                return other != null &&
                    TableName == other.TableName &&
                    SimulationName == other.SimulationName &&
                    ValueName == other.ValueName &&
                    Nullable.Equals(CurrentPredictedValue, other.CurrentPredictedValue) &&
                    Nullable.Equals(CurrentObservedValue, other.CurrentObservedValue) &&
                    Nullable.Equals(CurrentDifference, other.CurrentDifference) &&
                    Nullable.Equals(AcceptedPredictedValue, other.AcceptedPredictedValue) &&
                    Nullable.Equals(AcceptedObservedValue, other.AcceptedObservedValue) &&
                    Nullable.Equals(AcceptedDifference, other.AcceptedDifference);
            }

            public override int GetHashCode()
            {
                var texts = TableName + SimulationName + ValueName;

                // Calculate the hash code for the object.
                return texts.GetHashCode();

            }
        }

        public class BaseDatacontext : DbContext
        {
            public virtual DbSet<PredictedObservedValue> PredictedObservedValues { get; set; }
            public virtual DbSet<PredictedObservedDetail> PredictedObservedDetails { get; set; }
            public virtual DbSet<Simulation> Simulations { get; set; }
        }

        [TestMethod]
        public void TestMethodLinq()
        {
            var fakePredictedObservedValues = new List<PredictedObservedValue>
            {
                new PredictedObservedValue
                {
                    ID = 1,
                    SimulationsID = 2,
                    PredictedObservedDetailsID = 3,
                    ValueName = "Value A",
                    PredictedValue = 5,
                    ObservedValue = 1
                },
                new PredictedObservedValue
                {
                    ID = 2,
                    SimulationsID = 2,
                    PredictedObservedDetailsID = 6,
                    ValueName = "Value A",
                    PredictedValue = 3,
                    ObservedValue = 20
                },
                new PredictedObservedValue
                {
                    ID = 3,
                    SimulationsID = 2,
                    PredictedObservedDetailsID = 3,
                    ValueName = "Value A",
                    PredictedValue = 5,
                    ObservedValue = 2
                },
                new PredictedObservedValue
                {
                    ID = 4,
                    SimulationsID = 2,
                    PredictedObservedDetailsID = 6,
                    ValueName = "Value A",
                    PredictedValue = 3,
                    ObservedValue = 19
                },
            };

            var fakePredictedObservedDetails = new List<PredictedObservedDetail>
            {
                new PredictedObservedDetail
                {
                    ID = 3,
                    TableName = "Table Z",
                },
                new PredictedObservedDetail
                {
                    ID = 6,
                    TableName = "Table Z",
                },
            };

            var fakeSimulations = new List<Simulation>
            {
                new Simulation
                {
                    ID = 2,
                    Name = "Simulation 2",
                },
                new Simulation
                {
                    ID = 7,
                    Name = "Simulation 7",
                },
            };

            var fakePredictedObservedValueSet = GetMockDbSet(fakePredictedObservedValues.AsQueryable());
            var fakePredictedObservedDetailSet = GetMockDbSet(fakePredictedObservedDetails.AsQueryable());
            var fakeSimluationsSet = GetMockDbSet(fakeSimulations.AsQueryable());

            var fakeDbContext = new Mock<BaseDatacontext>();
            fakeDbContext.Setup(db => db.PredictedObservedValues).Returns(fakePredictedObservedValueSet.Object);
            fakeDbContext.Setup(db => db.PredictedObservedDetails).Returns(fakePredictedObservedDetailSet.Object);
            fakeDbContext.Setup(db => db.Simulations).Returns(fakeSimluationsSet.Object);

            var simData = GetValueSummary(fakeDbContext.Object, 3, "Value A");
            var simData2 = GetValueSummary(fakeDbContext.Object, 6, "Value A");
            var combinedData = (from PO1 in simData
                                join PO2 in simData2
                                on new { PO1.TableName, PO1.SimulationName, PO1.ValueName }
                                equals new { PO2.TableName, PO2.SimulationName, PO2.ValueName }
                                select new CurrentAndAcceptedDetail
                                {
                                    TableName = PO1.TableName,
                                    SimulationName = PO1.SimulationName,
                                    ValueName = PO1.ValueName,
                                    CurrentPredictedValue = (double?)Math.Round((double)PO1.PredictedValue, 3),
                                    CurrentObservedValue = ((PO1.ObservedValue.HasValue) ? (double?)Math.Round((double)PO1.ObservedValue, 3) : 0),
                                    CurrentDifference = ((PO1.PredictedValue.HasValue && PO1.ObservedValue.HasValue) ? (double?)Math.Round((double)PO1.PredictedValue - (double)PO1.ObservedValue, 3) : 0),
                                    AcceptedPredictedValue = (double?)Math.Round((double)PO2.PredictedValue, 3),
                                    AcceptedObservedValue = ((PO2.ObservedValue.HasValue) ? (double?)Math.Round((double)PO2.ObservedValue, 3) : 0),
                                    AcceptedDifference = ((PO2.PredictedValue.HasValue && PO2.ObservedValue.HasValue) ? (double?)Math.Round((double)PO2.PredictedValue - (double)PO2.ObservedValue, 3) : 0)
                                })
                                .Distinct();

            Assert.AreEqual(2, combinedData.Count());
        }

        public Mock<DbSet<T>> GetMockDbSet<T>(IQueryable<T> queryable) where T : class
        {
            var mockSet = new Mock<DbSet<T>>();
            mockSet.As<IQueryable<T>>().Setup(m => m.Provider).Returns(queryable.Provider);
            mockSet.As<IQueryable<T>>().Setup(m => m.Expression).Returns(queryable.Expression);
            mockSet.As<IQueryable<T>>().Setup(m => m.ElementType).Returns(queryable.ElementType);
            mockSet.As<IQueryable<T>>().Setup(m => m.GetEnumerator()).Returns(() => queryable.GetEnumerator());

            return mockSet;
        }

        public IEnumerable<SimulationPredictedObserved> GetValueSummary(BaseDatacontext db, int predictedObservedId, string valueName)
        {
            return (from pov in db.PredictedObservedValues
                    join pod in db.PredictedObservedDetails on pov.PredictedObservedDetailsID equals pod.ID
                    join s in db.Simulations on pov.SimulationsID equals s.ID
                    where pov.PredictedObservedDetailsID == predictedObservedId && pov.ValueName == valueName
                    select new SimulationPredictedObserved()
                    {
                        TableName = pod.TableName,
                        SimulationName = s.Name,
                        ValueName = pov.ValueName,
                        PredictedValue = Math.Round((double)pov.PredictedValue, 3),
                        ObservedValue = Math.Round((double)pov.ObservedValue, 3),
                        Difference = (double?)Math.Round((double)pov.PredictedValue - (double)pov.ObservedValue, 3)
                    })
                    .Distinct();
        }
    }
}