我的调查包含问题以及哪些用户可以/将参与调查。
喜欢这样
public virtual ICollection<User> ParticipatingUsers { get; set; }
public virtual ICollection<Question> SpecificQuestions { get; set; }
然而,由于ajaxy解决方案,我首先创建问题,然后简单地使用调查数据发送我创建的问题的ID。所以我需要做的就是更改问题的sortingIndex,然后在我的调查中添加对它的引用。
对于用户而言,他们属于公司实体,我只想从调查中引用它们而不是拥有它们。
但是目前我在我的操作方法(.net mvc)中获得问题和用户的所有ID,所以我现在加载所有问题和用户,并在将调查发送到存储库之前将它们附加到我的调查实体。
但是当我的Repository在dbset上调用Add时,它会克隆用户和问题数据,而不是简单地引用现有数据。
我迷路了,我通过添加[Foreignkey]解决了正常导航属性的这个确切问题,但我不知道这对ICollection有什么作用
为了完整性
这是我接收数据的行动方法
[HttpPost]
[FlexAuthorize(Roles = "RootAdmin")]
public ActionResult SaveSurvey(EditSurveyViewModel editModel)
{
if (!ModelState.IsValid)
{
//We dont bother to send this in so we need to fetch the list again
editModel.CompanyList = _companyRepository.GetAll();
List<string> deletionList = new List<string>();
//We clear out all questions from the state as we have custom logic to rerender them with the correct values
foreach (var modelstateItem in ModelState)
{
if (modelstateItem.Key.StartsWith("Questions"))
{
deletionList.Add(modelstateItem.Key);
}
}
foreach (string key in deletionList)
{
ModelState.Remove(key);
}
return View("EditSurvey", editModel);
}
List<Question> questionlist = new List<Question>();
int sort = 1;
Question q;
//We have questions sent in from the ui/client
if (editModel.Questions != null)
{
//Go trough each questions sent in
foreach (var question in editModel.Questions)
{
//if it's a page break, just assign our new question the sent in one and set sort index
if (question.IsPageBreak)
{
q = question;
q.SortIndex = sort;
}
else
{
//It's a question and all questions are already created with ajax from the client
//So we simply find the question and then set sort index and tie it to our survey
q = _questionRepository.GetById(question.Id);
q.SortIndex = sort;
}
questionlist.Add(q);
sort++;
}
}
//assign the new sorted questions to our Survey
editModel.Item.SpecificQuestions = questionlist;
List<User> userlist = new List<User>();
foreach (int id in editModel.SelectedUsers)
{
userlist.Add(_userRepository.GetById(id));
}
editModel.Item.ParticipatingUsers = userlist.ToList();
_surveyRepository.SaveSurveyBindAndSortQuestionsLinkUsers(editModel.Item);
return RedirectToAction("Index");
}
以下是该方法在
中发送的视图模型public class EditSurveyViewModel
{
public Survey Item { get; set; }
public IEnumerable<Question> Questions { get; set; }
public bool FullyEditable { get; set; }
public IEnumerable<Company> CompanyList { get; set; }
public IEnumerable<int> SelectedUsers { get; set; }
}
最后这里是repo方法(到目前为止我只实现了插入,而不是更新)
public void SaveSurveyBindAndSortQuestionsLinkUsers(Survey item)
{
if (item.Id == 0)
{
Add(item);
}
ActiveContext.SaveChanges();
}
更新/修改
Moho:你当然是对的,我觉得我很惭愧,我正在测试一些东西,忘记重置方法,然后在这里粘贴它。 我已经更新了上面的操作方法。Slauma:很抱歉没有细节,这里有更多。
我的所有存储库都是这样的
public class EFSurveyRepository : Repository<Survey>, ISurveyRepository
因此,他们继承了通用存储库并实现了一个接口 通用存储库(我们在上面的代码中使用的部分,看起来像这样)
public abstract class Repository<T> : IRepository<T> where T : class
{
public EFDbContext ActiveContext { get; private set; }
private readonly IDbSet<T> dbset;
public Repository()
{
this.ActiveContext = new EFDbContext("SurveyConnection");
dbset = ActiveContext.Set<T>();
}
public virtual void Add(T entity)
{
dbset.Add(entity);
}
public virtual T GetById(int id)
{
return dbset.Find(id);
}
我在数据库中注意到我的User表(对于User实体)现在包含一个我不希望它具有的Survey_Id字段。我想要一个多对多,许多调查可以链接到许多用户(相同的用户),但用户应该在实体方面仍然只属于公司的部门。
此外,现在当我运行代码时(在我更正了我的操作方法之后),我收到以下错误:
An entity object cannot be referenced by multiple instances of IEntityChangeTracker.
没有InnerException,只是当我尝试添加新调查时。
答案 0 :(得分:1)
您构建questionList
,将其设置为editModel.Item.SpecificQuestions
,然后通过将同一属性设置为editModel.Questions.ToList()
来覆盖引用,该属性来自您的视图模型(即:未通过您的加载数据库上下文,如questionList
的问题对象),因此似乎是对数据库上下文的新问题,
editModel.Item.SpecificQuestions = questionlist;
// what is this? why?
editModel.Item.SpecificQuestions = editModel.Questions.ToList();
问题更新后编辑:
不要使用questionList
并分配Survey
的questions属性,只需直接使用该属性即可。
此外,您是否意识到,如果您正在重复使用数据库中的Question
条记录多个Survey
,那么您将更新问题本身的排序顺序,而不仅仅是Survey
Question
1}?每次您保存重新使用问题的新调查时,我都会更改其他调查的问题排序。看起来您需要一个将Survey
映射到{{1}}的关系实体,您还可以在其中存储排序顺序,以便每个调查都可以重复使用问题实体,而不会弄乱现有的调查问题排序。
答案 1 :(得分:1)
问题是您每个存储库使用单独的上下文:
public Repository()
{
this.ActiveContext = new EFDbContext("SurveyConnection");
//...
}
在您的POST操作中,您有四个存储库:_companyRepository
,_questionRepository
,_userRepository
和_surveyRepository
。这意味着您正在使用四种不同的上下文,即从不同的上下文加载数据,在附加到不同上下文的实体之间创建关系,并将数据保存在另一个上下文中。
这就是数据库中实体重复的原因,“ IEntityChangeTracker的多个实例”异常,并且将成为您将来可能遇到的许多其他问题的来源。
您必须重构体系结构,以便在每个存储库中仅使用一个相同的上下文实例(“工作单元”),例如将其注入构造函数而不是创建新的:
private readonly EFDbContext _activeContext;
private readonly IDbSet<T> _dbset;
public Repository(EFDbContext activeContext)
{
_activeContext = activeContext;
_dbset = activeContext.Set<T>();
}