【问题标题】:Entity Framework Code First Update Does Not Update Foreign Key实体框架代码第一次更新不更新外键
【发布时间】:2011-10-12 01:06:19
【问题描述】:

我使用的是 EF 4.1 Code First。我有一个用这样的属性定义的实体:

public class Publication 
{
    // other stuff
    public virtual MailoutTemplate Template { get; set; }
}

我已经使用流畅的样式配置了这个外键,如下所示:

    modelBuilder.Entity<Publication>()
        .HasOptional(p => p.Template)
        .WithMany()
        .Map(p => p.MapKey("MailoutTemplateID"));

我有一个 MVC 表单处理程序,其中包含一些代码,如下所示:

public void Handle(PublicationEditViewModel publicationEditViewModel)
        {
            Publication publication = Mapper.Map<PublicationEditViewModel, Publication>(publicationEditViewModel);
            publication.Template = _mailoutTemplateRepository.Get(publicationEditViewModel.Template.Id);
            if (publication.Id == 0)
            {
                _publicationRepository.Add(publication);
            }
            else
            {
                _publicationRepository.Update(publication);
            }
            _unitOfWork.Commit();
        }

在这种情况下,我们正在更新现有的 Publication 实体,因此我们将通过 else 路径。当 _unitOfWork.Commit() 触发时,会向我在 SQL Profiler 和 Intellitrace 中看到的数据库发送一个 UPDATE,但它不包括更新中的 MailoutTemplateID。

让它真正更新模板的诀窍是什么?

存储库代码:

public virtual void Update(TEntity entity)
{
    _dataContext.Entry(entity).State = EntityState.Modified;
}

public virtual TEntity Get(int id)
{
    return _dbSet.Find(id);
}

工作单元代码:

public void Commit()
{
    _dbContext.SaveChanges();
}

【问题讨论】:

  • 如果重要的话,MailoutTemplateID 不会在此开始时设置。
  • 尝试将行publication.Template = ...移动到方法底部...Commit之前。更改检测随后有望识别模板已更改,并且必须将 ID 发送到数据库。将状态设置为 ModifiedMailoutTemplateID 列没有影响,因为它不是模型中的标量属性。

标签: entity-framework-4.1 ef-code-first


【解决方案1】:

取决于您的存储库代码。 :) 如果您在上下文跟踪 Publication 时设置了 Publication.Template,我希望它可以工作。当您断开连接然后附加(在您有导航属性但没有显式 FK 属性的情况下)时,我猜测上下文没有足够的信息来计算调用 SaveChanges 时的详细信息。我会做一些实验。 1) 进行集成测试,在其中查询 pub 并将其附加到上下文,然后添加模板,然后保存。 2)在 Publicaction 类上粘贴一个 MailOutTemplateId 属性,看看它是否有效。不建议将#2作为解决方案,只是作为一种探索行为的方式。我很想做这个实验,但我还需要做一些其他的工作。;)

【讨论】:

  • 我用 Repo 和 UoW 代码更新了问题。问题是我没有在发布上执行 Get(),而是简单地从视图模型映射它?所有其他字段都正确更新 - 只有这个 FK ID 不是。
【解决方案2】:

我找到了让它工作的方法。我最初不想执行 Get() (除了额外的 DB 命中)的原因是,我无法使用 AutoMapper 魔术来获取值:

Publication publication = Mapper.Map<PublicationEditViewModel, Publication>(publicationEditViewModel);

但是,我找到了另一种不使用返回值的方法来做同样的事情,所以我像这样更新了我的方法,这很有效:

    public void Handle(PublicationEditViewModel publicationEditViewModel)
        {
            Publication publication = _publicationRepository.Get(publicationEditViewModel.Id);
            _mappingEngine.Map(publicationEditViewModel, publication);
//            publication = Mapper.Map<PublicationEditViewModel, Publication>(publicationEditViewModel);
            publication.Template = _mailoutTemplateRepository.Get(publicationEditViewModel.Template.Id);
            if (publication.Id == 0)
            {
                _publicationRepository.Add(publication);
            }
            else
            {
                _publicationRepository.Update(publication);
            }
            _unitOfWork.Commit();
        }

我现在将 IMappingEngine 注入到类中,并通过 StructureMap 将其连接起来,如下所示:

    For<IMappingEngine>().Use(() => Mapper.Engine);

有关更多信息,请查看Jimmy's AutoMapper and IOC post

【讨论】:

  • 嗯,当您立即知道它可以解决您的问题时,为什么还要等待 2 天才能将自己的答案标记为答案?就像我真的会记得在两天后回到这个?在接下来的 2 天内,还有多少其他潜在的回答者会在这方面浪费时间?
  • 在这种情况下,您可以完全删除您的 Update 方法(else 块)。 _mappingEngine.Map... 将设置已加载(=附加)publication 的属性。当您调用SaveChanges 时,一条 UPDATE 语句将被发送到仅包含实际更改的属性的数据库。 (将状态设置为 Modified 将导致 所有 属性的更新 - 无论它们是否真的改变了。)
猜你喜欢
  • 2015-12-20
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2012-11-10
相关资源
最近更新 更多