很少且显然是随机的,实体框架会插入许多重复记录。谁能解释为什么会出现这种情况?这是我见过的第二个项目:
protected void btnAddQual_Click(object sender, EventArgs e)
{
QEntities ds = new QEntities();
Qualification qual = new Qualification();
qual.PersonID = ds.Persons.Where(x => x.Username == User.Identity.Name).Single().PersonID;
qual.QualificationName = txtQualAddName.Text;
qual.QualificationProvider = txtQualAddProvider.Text;
qual.QualificationYear = txtQualAddYear.Text;
qual.Inactive = false;
qual.LastUpdatedBy = User.Identity.Name;
qual.LastUpdatedOn = DateTime.Now;
ds.Qualifications.Add(qual);
ds.SaveChanges();
}
资格表:
public partial class Qualification
{
public int QualificationID { get; set; }
public int PersonID { get; set; }
public string QualificationName { get; set; }
public string QualificationProvider { get; set; }
public string QualificationYear { get; set; }
public bool Inactive { get; set; }
public string LastUpdatedBy { get; set; }
public Nullable<System.DateTime> LastUpdatedOn { get; set; }
public virtual Persons Persons { get; set; }
}
我已经看到它在一次按键点击中创建了3到32条记录,当它出现时,可以在很长一段时间内传播的时间戳(上次是28条记录,除主键外全部相同)和时间戳,在23分钟内分布不均匀)
我之前已将此归结为基于用户或浏览器的行为,但昨晚它发生在我使用该机器上。 当时我并没有注意到任何不寻常的事情,但是它不经常发生使它成为追踪的魔鬼。任何人都可以建议一个原因吗?
使用其他信息进行编辑:
这是使用.net framework 4.5.2和EF 6.1.3
编辑以解释赏金:
我刚看到这发生在以下代码中:
using(exEntities ds = new exEntities())
{
int initialStations;
int finalStations;
int shouldbestations = numStations * numSessions * numRotations * numBlock;
initialStations = ds.Stations.Count();
for(int b = 1; b <= numBlock; b++)
{
for (int se = 1; se <= numSessions; se++)
{
for (int r = 1; r <= numRotations; r++)
{
for (int st = 1; st <= numStations; st++)
{
Stations station = new Stations();
station.EID = eID;
station.Block = b;
station.Rotation = r;
station.Session = se;
station.StationNum = st;
station.LastUpdatedBy = User.Identity.Name + " (Generated)";
station.LastUpdatedOn = DateTime.Now;
station.Inactive = false;
ds.Stations.Add(station);
}
}
}
}
ds.SaveChanges();
在这个例子中,每个循环的迭代次数是: 分别为1,2,6,5。
这一次点击(同一时间戳)复制了整套记录
答案 0 :(得分:1)
在这种情况下,您需要向应用程序中添加日志记录。根据您的代码,我不认为Entity Framework会在复制您的数据,而是认为您的代码是以不适合您的方式触发的。我看到EF重复记录的原因是开发人员传递了从一个DBContext加载的实体,然后将它们与在另一个DBContext中创建的实体相关联,而无需检查DbContext并先附加它们。 EF会将其视为“新”,然后重新插入。从您提供的代码来看,我认为情况并非如此,但需要提防。
首先,特别是在处理Web应用程序时,应编写任何事件处理程序或POST / PATCH API方法,以将每个调用视为不可信。在正常使用情况下,这些方法应该可以实现您期望的效果,但是在滥用或黑客尝试下,可以在不应该使用的条件下调用它们,或者携带不应该使用的负载。例如,您可能希望仅在更新记录1234并且用户按下“保存”按钮(一次)后,才会触发记录ID为1234的事件处理程序,但是可以:
不进行任何操作,验证并记录所有内容,如果不正确,请终止会话。 (强制注销)
对于日志记录,除了标准的异常日志记录之外,我建议为生产调试版本添加带有附加编译器常量的Information跟踪,以监视这种情况被多次触发的情况之一。我个人使用Diagnostics.Trace
,然后将Log4Net之类的日志记录处理程序挂接到其中。
我建议添加如下内容:
#if DEBUG
Trace.TraceInformation(string.Format("Add Stations called. (eIS: {0})", eID));
Trace.TraceInformation(Environment.StackTrace);
#endif
然后,当您进行计数检查并发现问题时:
Trace.TraceWarning(string.Format("Add Station count discrepancy. Expected {0}, Found {1}", shouldBeStations, finalStations));
我将编译器置于条件下,因为Environment.StackTrace会产生生产系统中通常不希望的成本。您可以将“ DEBUG”替换为另一个自定义编译常数,以使部署可以检查此问题。让它在野外运行并监视您的跟踪输出(数据库,文件等),直到问题出现为止。当警告出现时,您可以返回“信息”轨迹以查看何时何地触发了代码。您还可以在加载屏幕的调用中进行类似的跟踪,在该屏幕上将触发此事件以记录ID和用户详细信息,以查看是否/如何通过代码错误或某些环境条件或黑客尝试触发了错误的eID事件
或者,对于日志记录,您还可以考虑在应用程序配置中添加一个设置,以基于日志记录级别或标志来打开和关闭日志记录事件,以开始/停止捕获方案而无需重新部署。
这类问题很难诊断和解决,例如StackOverflow,但是希望日志记录可以帮助突出显示您未考虑的问题。最坏的情况是,考虑让有EF和您的核心技术经验的人员短期访问系统,因为对整个工作情况的第二次审视也可能有助于指出潜在的原因。
一个小技巧。而不是像这样:
qual.PersonID = ds.Persons.Where(x => x.Username == User.Identity.Name).Single().PersonID;
使用:
qual.PersonID = ds.Persons.Where(x => x.Username == User.Identity.Name).Select(x => x.PersonID).Single();
第一条语句在实体尚未缓存的地方执行“ SELECT * FROM tblPersons WHERE ...”,并拉回所有只需要PersonID的列。第二个执行“从tblPersons WHERE ...中选择SELECT PersonID”