ASP.Net Core 2.2 добавление нового дочернего объекта в базу данных также добавляет новый пустой родительский объект - PullRequest
1 голос
/ 29 мая 2019

Я недавно обновил свой проект с ASP.Net Core 2.1 до 2.2 (и следовал руководству по миграции Microsoft), после обновления некоторых моих бритвенных страниц для добавления дочернего объекта в базу данных начал также добавлять новые пустые родительские объекты в базы данных одновременно. Как ни странно, это поведение не одинаково для всех дочерних и родительских объектов, и некоторые страницы бритвы продолжают работать нормально.

Я выкопал следующие ссылки, которые в некоторой степени связаны с моей проблемой, но не дают решения:

Эта проблема github точно описывает, что со мной происходит, но я не использую пользовательский ModelBinder.

Вот файл Create.cshtml:

<h2>Create</h2>

<h4>Branch</h4>
<hr />
<div class="row">
    <div class="col-md-4">
        <form method="post">
            <div asp-validation-summary="ModelOnly" class="text-danger"></div>
            <div class="form-group">
                <label asp-for="Branch.BranchName" class="control-label"></label>
                <input asp-for="Branch.BranchName" class="form-control" />
                <span asp-validation-for="Branch.BranchName" class="text-danger"></span>
            </div>
            <div class="form-group">
                <label asp-for="Branch.Name" class="control-label"></label>
                <input asp-for="Branch.Name" class="form-control" />
                <span asp-validation-for="Branch.Name" class="text-danger"></span>
            </div>
            <div class="form-group">
                <label asp-for="Branch.Address" class="control-label"></label>
                <input asp-for="Branch.Address" class="form-control" />
                <span asp-validation-for="Branch.Address" class="text-danger"></span>
            </div>
            <div class="form-group">
                <label asp-for="Branch.City" class="control-label"></label>
                <input asp-for="Branch.City" class="form-control" />
                <span asp-validation-for="Branch.City" class="text-danger"></span>
            </div>
            <div class="form-group">
                <label asp-for="Branch.Province" class="control-label"></label>
                <input asp-for="Branch.Province" class="form-control" />
                <span asp-validation-for="Branch.Province" class="text-danger"></span>
            </div>
            <div class="form-group">
                <label asp-for="Branch.Country" class="control-label"></label>
                <input asp-for="Branch.Country" class="form-control" />
                <span asp-validation-for="Branch.Country" class="text-danger"></span>
            </div>
            <div class="form-group">
                <label asp-for="Branch.PostalCode" class="control-label"></label>
                <input asp-for="Branch.PostalCode" class="form-control" />
                <span asp-validation-for="Branch.PostalCode" class="text-danger"></span>
            </div>
            <div class="form-group">
                <label asp-for="Branch.Status" class="control-label"></label>
                <input asp-for="Branch.Status" class="form-control" />
                <span asp-validation-for="Branch.Status" class="text-danger"></span>
            </div>
            <div class="form-group">
                <label asp-for="Branch.Phone" class="control-label"></label>
                <input asp-for="Branch.Phone" class="form-control" />
                <span asp-validation-for="Branch.Phone" class="text-danger"></span>
            </div>
            <div class="form-group">
                <label asp-for="Branch.Fax" class="control-label"></label>
                <input asp-for="Branch.Fax" class="form-control" />
                <span asp-validation-for="Branch.Fax" class="text-danger"></span>
            </div>
            <div class="form-group">
                <label asp-for="Branch.Comments" class="control-label"></label>
                <textarea asp-for="Branch.Comments" class="form-control"></textarea>
                <span asp-validation-for="Branch.Comments" class="text-danger"></span>
            </div>
            <div class="form-group">
                <label asp-for="Branch.DealerId" class="control-label"></label>
                <select asp-for="Branch.DealerId" class ="form-control" asp-items="ViewBag.DealerId"></select>
            </div>
            <div class="form-group">
                <input type="submit" value="Create" class="btn btn-default" />
            </div>
        </form>
    </div>
</div>

Вот файл Create.cshtml.cs:

public class CreateModel : PageModel
    {
        private readonly ApplicationDbContext _context;

        public CreateModel(ApplicationDbContext context)
        {
            _context = context;
        }

        public IActionResult OnGet()
        {
            Branch = new Branch();
            //Usually the DealerId would be passed in the URL, this is just for testing
            ViewData["DealerId"] = new SelectList(_context.Dealer, "DealerId", "DealerId");
            return Page();
        }

        //This is weird for debugging purposes
        private Branch _branch;

        [BindProperty]
        public Branch Branch {
            get
            {
                return _branch;
            }
            set
            {
                var t = value;
                _branch = value;
            }}

        public async Task<IActionResult> OnPostAsync()
        {
            if (!ModelState.IsValid)
            {
                return Page();
            }

            _context.Branches.Add(Branch);
            await _context.SaveChangesAsync();

            return RedirectToPage("./Index");
        }
    }

Branch.cs:

public class Branch
    {
        [Key]
        public string BranchId { get; set; }

        [Display(Name = "Branch Name")]
        public string BranchName { get; set; }

        public string Name { get; set; }
        public string Address { get; set; }
        public string City { get; set; }
        public string Province { get; set; }
        public string Country { get; set; }

        [Display(Name = "Postal Code")]
        [DataType(DataType.PostalCode)]
        public string PostalCode { get; set; }


        public string Status { get; set; }

        [DataType(DataType.PhoneNumber)]
        public string Phone { get; set; }

        [DataType(DataType.PhoneNumber)]
        public string Fax { get; set; }

        [DataType(DataType.MultilineText)]
        public string Comments { get; set; }


        public string DealerId { get; set; }

        [ForeignKey("DealerId")]
        public Dealer Dealer { get; set; }

        public List<ApplicationUser> Users { get; set; }
    }

Когда страница отправляется обратно, объект Branch должен иметь нулевое свойство Dealer (и в ASP.Net Core 2.1 оно имеет), но имеет только что созданный экземпляр объекта Dealer (с нулевыми полями) вот скриншот точка останова на установщике ветвления . Когда объект ветви добавляется в базу данных, он должен использовать DealerId, чтобы найти связанный объект и связать их (между этими классами / таблицами установлено правильное и функциональное отношение FK), но поскольку свойство навигации Dealer не является нулевым и Филиал, и Пустой Дилер добавляются в базу данных, между ними устанавливается отношение FK. Это можно исправить, установив для свойства Dealer значение null, прежде чем добавлять Branch в базу данных _context, но это явно не ожидаемое поведение, и в этом нет необходимости для некоторых других объектов базы данных с похожими отношениями.

Я рвал на себе волосы, пытаясь выяснить источник этой ошибки. Я даже зашел так далеко, что создал новый проект ASP.Net Core, скопировал необходимые модели и выстроил из них новую базу данных только для того, чтобы столкнуться с той же проблемой.

Из того, что я могу сказать, это проблема с ModelBinder, который плохо играет с этими и другими связанными классами. Binder должен установить для свойства Dealer значение null на постбэк, но по какой-то причине он вместо этого вызывает конструктор по умолчанию (а затем вызывает конструктор по умолчанию для родительского класса дилера).

...