(我不知道是不是线程安全问题实际上是相关的,所以我会编辑标题为合适。)与实体框架线程安全
.NET 4.6,MVC 5.我跑我管理的网站上的任务抓取了几个CSV文件并遍历它们,根据需要操作内部数据(文件是只读的)。此任务每20分钟运行一次。
任务完全跑了好几天,但昨天它停止处理数据。它在预定时间运行,但每个CSV文件的数据处理都单独失败,并显示错误The relationship between the two objects cannot be defined because they are attached to different ObjectContext objects.
重新启动应用程序将问题解决了几个小时,然后又开始出错。
我不知道这可能是相关,但我的直觉反应是线程安全的。我不完全熟悉如何在.NET/MVC中处理线程,但粗略搜索显示EF不是线程安全的。我的任务是否有可能试图在自己的顶部运行,并且由于此问题而遇到麻烦?这看起来很奇怪,因为在我看来,单独的任务实例应该有自己的上下文。也许更可能的是服务如何执行数据库操作。
我无法重现,因为它的本质问题;所有尝试在调试时在本地重现它都会显示按预期运行的任务。
什么引起这些错误,我怎么能阻止他们?
我已经包括了第一个CSV文件导入的代码;每个部分的例外情况都是分开的。作为背景,User
有一个属性ICollection<PlannerCode>
,称为PlannerCodes
。这个项目是基于nopCommerce,但这个任务是完全自定义的,所以我相信它适用于nopCommerce而不是以这种方式标记它。
堆栈跟踪:
System.InvalidOperationException: The relationship between the two objects cannot be defined because they are attached to different ObjectContext objects.
at System.Data.Entity.Core.Objects.DataClasses.RelatedEnd.ValidateContextsAreCompatible(RelatedEnd targetRelatedEnd)
at System.Data.Entity.Core.Objects.DataClasses.RelatedEnd.Add(IEntityWrapper wrappedTarget, Boolean applyConstraints, Boolean addRelationshipAsUnchanged, Boolean relationshipAlreadyExists, Boolean allowModifyingOtherEndOfRelationship, Boolean forceForeignKeyChanges)
at System.Data.Entity.Core.Objects.ObjectStateManager.PerformAdd(IEntityWrapper wrappedOwner, RelatedEnd relatedEnd, IEntityWrapper entityToAdd, Boolean isForeignKeyChange)
at System.Data.Entity.Core.Objects.ObjectStateManager.PerformAdd(IList`1 entries)
at System.Data.Entity.Core.Objects.ObjectStateManager.DetectChanges()
at System.Data.Entity.Core.Objects.ObjectContext.DetectChanges()
at System.Data.Entity.Internal.InternalContext.DetectChanges(Boolean force)
at System.Data.Entity.Internal.InternalContext.GetStateEntries(Func`2 predicate)
at System.Data.Entity.Internal.InternalContext.GetStateEntries()
at System.Data.Entity.Infrastructure.DbChangeTracker.Entries()
at System.Data.Entity.DbContext.GetValidationErrors()
at System.Data.Entity.Internal.InternalContext.SaveChanges()
at System.Data.Entity.Internal.LazyInternalContext.SaveChanges()
at System.Data.Entity.DbContext.SaveChanges()
at Nop.Data.EfRepository`1.Update(T entity) in C:\Users\username\Documents\projectname\Libraries\Nop.Data\EfRepository.cs:line 120
at Nop.Services.Users.UserService.UpdateUser(User user) in C:\Users\username\Documents\projectname\Libraries\Nop.Services\Users\UserService.cs:line 477
at Nop.Services.WorkItems.ImportTask.Execute() in C:\Users\username\Documents\projectname\Libraries\Nop.Services\WorkItems\ImportTask.cs:line 165
ImportTask
代码:
if (File.Exists(plannerFile))
{
try
{
// planners corresponds to the CSV file
// foreach is the correct way of iterating through the lines
foreach (var p in planners)
{
var user = _userService.GetUserByEmail(p.Email);
var pcode = _codeService.GetPCodeByString(p.Code);
// check if pcode already exists. if it doesn't, insert it.
if (pcode == null)
{
pcode = new PlannerCode { P = p.Code };
_codeService.InsertPCode(pcode);
}
// if no user found or the user is already associated with the pcode, move on
if (user == null || user.PlannerCodes.Contains(pcode))
continue;
// add the pcode to the user's PlannerCodes
user.PlannerCodes.Add(pcode);
// update the user to save changes to PlannerCodes
_userService.UpdateUser(user);
}
}
catch (Exception ex)
{
// log exception info, ex.ToString()
}
}
UserService
如下。 CodeService
基本上是相同的,但相关存储库类型已更改。
readonly IRepository<User> _userRepository;
public UserService(IRepository<User> userRepository)
{
_userRepository = userRepository;
}
public virtual User GetUserByEmail(string email)
{
if (string.IsNullOrWhiteSpace(email))
return null;
return _userRepository.Table.FirstOrDefault(u => u.Email == email);
}
public virtual void UpdateUser(User user)
{
if (user == null)
throw new ArgumentNullException(nameof(user));
_userRepository.Update(user);
}
EfRepository
:
public virtual void Update(T entity)
{
// exceptions are caught but snipped from this example
_context.SaveChanges();
}
服务注入ImportTask
与此代码:
IUserService _userService = EngineContext.Current.Resolve<IUserService>();
相关:http://stackoverflow.com/questions/15274539/the-relationship-between-the-two-objects-cannot-be-defined-because-they-are-atta –
IRepository和IRepository 使用不同背景? –
@ArturoMenchaca我正在通过Autofac文档阅读,我真的不太确定这是如何工作的。我相信相关的代码在'DependencyRegistrar.cs'(source [here](https://github.com/nopSolutions/nopCommerce/blob/develop/src/Presentation/Nop.Web.Framework/DependencyRegistrar.cs))中,它将DbContext设置为'InstancePerLifetimeScope',但我试图更好地理解。 – vaindil