Add() 方法为 Code-First 实体框架中的链接模型添加重复行

2023-12-29

以下是将贷款请求添加到数据库的操作:

[HttpPost]
public ActionResult Add(Models.ViewModels.Loans.LoanEditorViewModel loanEditorViewModel)
{
    if (!ModelState.IsValid)
        return View(loanEditorViewModel);

    var loanViewModel = loanEditorViewModel.LoanViewModel;

    loanViewModel.LoanProduct = LoanProductService.GetLoanProductById(loanViewModel.LoanProductId); // <-- don't want to add to this table in database
    loanViewModel.Borrower = BorrowerService.GetBorrowerById(loanViewModel.BorrowerId); //<-- don't want to add to this table in database

    Models.Loans.Loan loan = AutoMapper.Mapper.Map<Models.Loans.Loan>(loanEditorViewModel.LoanViewModel);
    loanService.AddNewLoan(loan);
    return RedirectToAction("Index");
}

以下是AddNewLoan() method:

public int AddNewLoan(Models.Loans.Loan loan)
{
    loan.LoanStatus = Models.Loans.LoanStatus.PENDING;
    _LoanService.Insert(loan);

    return 0;
}

这是代码Insert()

public virtual void Insert(TEntity entity)
{
    if (entity == null)
        throw new ArgumentNullException(nameof(entity));

    try
    {
        entity.DateCreated = entity.DateUpdated = DateTime.Now;
        entity.CreatedBy = entity.UpdatedBy = GetCurrentUser();

        Entities.Add(entity);
        context.SaveChanges();
    }
    catch (DbUpdateException exception)
    {
        throw new Exception(GetFullErrorTextAndRollbackEntityChanges(exception), exception);
    }
}

它正在成功添加一行Loans表,但它也添加行LoanProduct and Borrower正如我在第一个代码注释中所示的表。

我检查了多次调用此操作的可能性并且Insert方法,但它们被调用一次。

UPDATE

我面临类似的问题,但功能问题相反:实体未使用代码优先方法进行更新 https://stackoverflow.com/questions/56302611/entity-not-updating-using-code-first-approach

我认为这两者对于变更跟踪有着相同的原因。但一个是添加,另一个是不更新。


下面的代码看起来有点奇怪:

var loanViewModel = loanEditorViewModel.LoanViewModel;

loanViewModel.LoanProduct = LoanProductService.GetLoanProductById(loanViewModel.LoanProductId); // <-- don't want to add to this table in database
loanViewModel.Borrower = BorrowerService.GetBorrowerById(loanViewModel.BorrowerId); //<-- don't want to add to this table in database

Models.Loans.Loan loan = AutoMapper.Mapper.Map<Models.Loans.Loan>(loanEditorViewModel.LoanViewModel);

您正在视图模型上设置实体引用,然后调用 automapper。 ViewModel 不应保存实体引用,并且自动映射器应有效地忽略任何引用的实体,而仅映射正在创建的实体结构。 Automapper 将根据传入的数据创建新实例。

相反,类似这样的事情应该按预期工作:

// Assuming these will throw if not found? Otherwise assert that these were returned.
var loanProduct = LoanProductService.GetLoanProductById(loanViewModel.LoanProductId);
var borrower = BorrowerService.GetBorrowerById(loanViewModel.BorrowerId);

Models.Loans.Loan loan = AutoMapper.Mapper.Map<Models.Loans.Loan>(loanEditorViewModel.LoanViewModel);
loan.LoanProduct = loanProduct;
loan.Borrower = borrower;

Edit:

接下来要检查的是您的服务是否使用完全相同的 DbContext 引用。您是否将依赖注入与 IoC 容器(例如 Autofac 或 Unity)一起使用?如果是这样,请确保将 DbContext 设置为注册为“每个请求实例”或类似的生命周期范围。如果服务有效地新建了一个新的 DbContext,那么 LoanService DbContext 将不知道由另一个服务的 DbContext 获取的产品和借款人的实例。

如果您没有使用 DI 库,那么您应该考虑添加一个。否则,您将需要更新服务以在每次调用时接受单个 DbContext,或者利用工作单元模式(例如 Mehdime 的 DbContextScope)来促进服务从工作单元解析其 DbContext。

例如确保相同的 DbContext:

using (var context = new MyDbContext())
{
    var loanProduct = LoanProductService.GetLoanProductById(context, loanViewModel.LoanProductId);
    var borrower = BorrowerService.GetBorrowerById(context, loanViewModel.BorrowerId);

    Models.Loans.Loan loan = AutoMapper.Mapper.Map<Models.Loans.Loan>(loanEditorViewModel.LoanViewModel);
    loan.LoanProduct = loanProduct;
    loan.Borrower = borrower;

    LoanService.AddNewLoan(context, loan);
}    

如果您确定所有服务都提供相同的 DbContext 实例,那么您的 Entities.Add() 方法中可能会发生一些奇怪的情况。老实说,您的解决方案看起来对像 CRUD 创建和关联操作这样简单的事情有太多抽象。这看起来像是一个不从最简单的解决方案开始就对 DRY 进行过早代码优化的情况。代码可以更简单地只限定 DbContext 的范围、获取适用的实体、创建新实例、关联、添加到 DbSet 和 SaveChanges。抽象出对基本操作(例如通过 ID 获取引用)的调用没有任何好处。

public ActionResult Add(Models.ViewModels.Loans.LoanEditorViewModel loanEditorViewModel)
{
    if (!ModelState.IsValid)
        return View(loanEditorViewModel);

    var loanViewModel = loanEditorViewModel.LoanViewModel;
    using (var context = new AppContext())
    {
       var loanProduct = context.LoanProducts.Single(x => x.LoanProductId == 
loanViewModel.LoanProductId);
       var borrower = context.Borrowers.Single(x => x.BorrowerId == loanViewModel.BorrowerId);
       var loan = AutoMapper.Mapper.Map<Loan>(loanEditorViewModel.LoanViewModel);
       loan.LoanProduct = loanProduct;
       loan.Borrower = borrower;
       context.SaveChanges();
    }
    return RedirectToAction("Index");
}

撒上一些异常处理,就完成了并除尘。没有分层的服务抽象。从那里,您可以通过使用 Autofac 等 IoC 容器来管理上下文和/或引入存储库/服务层 /w UoW 模式,从而使操作可测试。上述内容将作为该行动的最低可行解决方案。任何抽象等都应该在之后应用。在涂抹油之前用铅笔勾勒出草图。 :)

使用 Mehdime 的 DbContextScope 它看起来像:

public ActionResult Add(Models.ViewModels.Loans.LoanEditorViewModel loanEditorViewModel)
{
    if (!ModelState.IsValid)
        return View(loanEditorViewModel);

    var loanViewModel = loanEditorViewModel.LoanViewModel;
    using (var contextScope = ContextScopeFactory.Create())
    {
       var loanProduct = LoanRepository.GetLoanProductById( loanViewModel.LoanProductId).Single();
       var borrower = LoanRepository.GetBorrowerById(loanViewModel.BorrowerId);
       var loan = LoanRepository.CreateLoan(loanViewModel, loanProduct, borrower).Single();
       contextScope.SaveChanges();
    }
    return RedirectToAction("Index");
}

就我而言,我利用存储库模式,该模式使用 DbContextScopeLocator 来解析它的 ContextScope 以获取 DbContext。 Repo 管理数据的获取并确保为实体的创建提供创建完整且有效的实体所需的所有必需数据。我选择每个控制器一个存储库,而不是像每个实体的通用模式或存储库/服务之类的东西,因为在我看来,这可以更好地管理单一责任原则,因为代码只有一个更改的理由(它服务于控制器,而不是在许多控制器之间共享)具有潜在不同关注点的控制者)。单元测试可以模拟存储库以提供预期的数据状态。回购获取方法返回IQueryable以便消费者逻辑可以确定它想要如何消费数据。

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

Add() 方法为 Code-First 实体框架中的链接模型添加重复行 的相关文章

  • SQL Server 2008 中的全文搜索一步一步

    如何开始使用SQL Server 2008 中的全文搜索 阅读这些链接 SQL SERVER 2008 创建全文目录和全文搜索 http blog sqlauthority com 2008 09 05 sql server creatin
  • 有没有比这更快的方法来查找目录和所有子目录中的所有文件?

    我正在编写一个程序 需要在目录及其所有子目录中搜索具有特定扩展名的文件 这将在本地驱动器和网络驱动器上使用 因此性能是一个问题 这是我现在使用的递归方法 private void GetFileList string fileSearchP
  • 身份未映射异常

    System Security Principal IdentityNotMappedException 无法转换部分或全部身份引用 该错误仅在应用程序注册后出现一次 当 SecurityIdentifier 无法映射时 例如 返回 Ide
  • __FUNCTION__ 宏的 C# 版本

    有人对 C FUNCTION 宏的 C 版本有好的解决方案吗 编译器似乎不喜欢它 尝试使用这个代替 System Reflection MethodBase GetCurrentMethod Name C 没有 LINE or FUNCTI
  • 使用显式创建表语句与 select into 创建表

    使用显式创建表语句和加载数据与选择数据之间是否存在性能差异 此示例仅显示 2 列 但问题是针对使用非常大的表 下面的示例也使用临时表 尽管我也想知道使用常规表的效果 我认为无论表格类型如何 它们都是相同的 临时表场景 Explicitly
  • 成员初始值设定项列表中的求值顺序是什么?

    我有一个带有一些参数的构造函数 我假设它们是按照列出的顺序初始化的 但在一种情况下 它们似乎是按相反的顺序初始化的 导致中止 当我反转参数时 程序停止中止 下面是我正在使用的语法的示例 a 之前需要初始化b 在这种情况下 你能保证这个初始化
  • asp.net mvc jquery 下拉验证

    我如何使用不显眼的 javascript 验证下拉列表 作为所需验证器的验证文本框 但它不适用于下拉列表 需要更改不显眼的 js 文件吗 或者还有其他选项来验证下拉列表吗 我想在我的 javascript 中检查 form validate
  • 我的 C# .NET 团队是否应该迁移到 Windows Presentation Foundation? [关闭]

    就目前情况而言 这个问题不太适合我们的问答形式 我们希望答案得到事实 参考资料或专业知识的支持 但这个问题可能会引发辩论 争论 民意调查或扩展讨论 如果您觉得这个问题可以改进并可能重新开放 访问帮助中心 help reopen questi
  • C#:使用 System.Text 和 System.Text.RegularExpressions 之间的区别

    在 ASP NET C 应用程序中 我注意到为了使用 Regex 和 StringBuilder 我必须将两者都放在 using System Text using System Text RegularExpressions 从简单的角度
  • 如何在不使用reinterpret_cast的情况下使用dlsym()加载函数?

    我正在尝试使用 clang tidy 来强制执行 C 核心指南 虽然它确实有很多有效点 但有一件事我无法真正解决 dlsym 返回一个void 我需要以某种方式将其转换为正确的函数指针 为此 我使用reinterpret cast 由于指南
  • for 循环 - 没有效果的语句

    由于某种原因 我收到错误 statement with no effect关于这个声明 for j idx j lt iter j increment printf from loop idx i int idx punc ctxt j 你
  • C++ Primer 5th Edition 错误 bool 值没有指定最小大小?

    bool 的最小大小不应该是 1 个字节吗 这有点学术性的东西 尽管它们会转换为数字 并且 与其他所有事物一样 它们最终将基本上由计算机内存中的数字表示 但布尔值不是数字 你的bool可以取值true 或值false 即使您确实需要至少 1
  • 为什么 C 函数不能返回数组类型?

    我是 C 语言新手 想知道 为什么 C 函数不能返回数组类型 我知道数组名是数组第一个值的地址 而数组是 C 中的二等公民 您自己已经回答了这个问题 数组是二等公民 C 按值返回 数组不能按值传递 因此不能返回它们 至于为什么数组不能按值传
  • 如何在 C++ 中使用 PI 常数

    我想在一些 C 程序中使用 PI 常数和三角函数 我得到三角函数include
  • SSBO 是更大的 UBO?

    我目前正在 OpenGL 4 3 中使用 UBO 进行渲染 以将所有常量数据存储在 GPU 上 诸如材料描述 矩阵等内容 它可以工作 但是 UBO 的小尺寸 我的实现为 64kB 迫使我多次切换缓冲区 减慢渲染速度 我正在寻找类似的方法来存
  • 删除对象时指针自动指向空

    假设我有一个对象和其他几个不同类类型的对象中的 10 个指向它的指针 如果对象被删除 这些指针必须设置为空 通常我会将对象的类与具有指向它的指针的类互连 以便它可以通知它们它正在被删除 并且它们可以将它们的指针设置为空 但这也有一个负担 即
  • 在 unix 中编译 dhrystone 时出错

    我是使用基准测试和 makefile 的新手 我已经从下面的链接下载了 Dhrystone 基准测试 我正在尝试编译它 但我遇到了奇怪的错误 我尝试解决它 但没有成功 有人可以帮助我运行 dhrystone 基准测试吗 以下是我尝试编译的两
  • 如何使用 g++ 在 c++ 20 中使用模块?

    我读了这个链接https gcc gnu org wiki cxx modules https gcc gnu org wiki cxx modules并尝试从该网站复制以下示例 我已经知道这个编译器部分支持模块系统 注 我用的是windo
  • 局部静态变量初始化是线程安全的[重复]

    这个问题在这里已经有答案了 假设我有一个包含三个静态函数的类 如下所示 include
  • 如何使复选框不可选择?

    我想知道你是怎么做的CheckBox在c 中无法选择 我认为这会是类似 SetSelectable false 之类的东西 但我似乎看不到该方法 I found CanSelect但这似乎是只读属性 您可以设置自动检查 http msdn

随机推荐