我有一个返回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条记录),以及我如何解决它?
非常感谢任何协助。
答案 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();
}
}
}