0

我有一份调查,其中包含问题以及哪些用户可以/将参与调查。

像这样

public virtual ICollection<User> ParticipatingUsers { get; set; }
public virtual ICollection<Question> SpecificQuestions { get; set; }

但是,由于 ajaxy 解决方案,我首先创建问题,然后简单地将我创建的问题的 ID 与调查数据一起发送。所以我需要做的就是更改问题的排序索引,然后在我的调查中添加对它的引用。

当涉及到用户时,他们属于公司实体,我只想从调查中引用他们而不是拥有他们。

但是目前我在我的操作方法(.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:你当然是对的,我觉得很遗憾我在测试一些东西并忘记在粘贴之前重置方法。我已经更新了上面的操作方法。

斯劳玛:抱歉没有详细说明,这里有更多。

我所有的存储库都是这样的

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);
    }

我在数据库中注意到我的用户表(用于用户实体)现在包含一个我不希望它拥有的 Survey_Id 字段。我想要一个多对多,其中许多调查可以链接到许多用户(相同的用户),但用户应该在实体方面仍然只属于公司的一个部门。

另外,现在当我运行代码时(在我更正了我的操作方法之后),我收到以下错误:

An entity object cannot be referenced by multiple instances of IEntityChangeTracker.

没有 InnerException,只有当我尝试添加新调查时。

4

2 回答 2

1

您建立questionList,将其设置为editModel.Item.SpecificQuestions,然后通过将相同的属性设置为 来覆盖引用,该属性editModel.Questions.ToList()来自您的视图模型(即:未通过您的数据库上下文加载,如questionList的问题对象),因此似乎是您的数据库的新问题语境,

    editModel.Item.SpecificQuestions = questionlist;

    // what is this?  why?
    editModel.Item.SpecificQuestions = editModel.Questions.ToList();

问题更新后编辑:

无需使用questionList并分配给 的 questions 属性Survey,只需直接使用该属性即可。

另外,您是否意识到,如果您将Question数据库中的记录重复使用多个Surveys,您正在更新问题本身的排序顺序,而不仅仅是为了那个Survey?每次您保存一个重复使用问题的新调查时,其他调查的问题顺序都会改变。看起来您需要一个将 s 映射QuestionSurveys 的关系实体,您还可以在其中存储排序顺序,以便每个调查都可以重用问题实体,而不会弄乱现有调查的问题顺序。

于 2013-10-03T20:38:57.187 回答
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>();
}
于 2013-10-04T22:15:41.753 回答