Entity Framework Core 2.1. Правильный способ обновить объект множеством подобъектов - PullRequest
1 голос
/ 16 апреля 2020

Я занимаюсь разработкой ASP. NET Core + EF Core приложения.

Я немного (очень много) борюсь с методом обновления контроллера, который управляет наиболее сложным объект моего проекта.

Объект выглядит примерно так:

public class CIApplication : ConfigurationItem
{
    public String InventoryCollectionQuery { get; set; }
    public EnumCCMCIType CCMCIType { get; set; }

    //Collection of DeploymentScenario
    public virtual ICollection<DeploymentScenario> DeploymentScenarios { get; set; }

    //Collection of SoftwareMeteringRules
    public virtual ICollection<SoftwareMeteringRule> SoftwareMeteringRules { get; set; }

    //Collection of AppLocalPresenceCondition
    public virtual ICollection<AppLocalPresenceCondition> AppLocalPresenceConditions { get; set; }
}

И некоторые из объектов в коллекциях также могут содержать другие коллекции и так далее, и так далее ...

На самом деле мои методы обновления работают (что ниже), но это настоящая боль в поддержании, когда я делаю некоторые изменения в моей модели. Поскольку я разрабатывал это довольно давно, go мне интересно, есть ли лучший подход для этого. И, что лучше, для меня это намного проще :) иногда я даже задаюсь вопросом, не лучше ли удалить объект и воссоздать его.

 var cIApplicationInDB = _context.CIApplications
            .Include(c => c.Translations)
            .Include(c => c.DeploymentScenarios).ThenInclude(d => d.InstallSteps)
            .Include(c => c.DeploymentScenarios).ThenInclude(d => d.InstallSteps).ThenInclude(s => s.Translations)
            .Include(c => c.DeploymentScenarios).ThenInclude(d => d.InstallSteps).ThenInclude(s => s.InputVariables).ThenInclude(x => x.Translations)
            .Include(c => c.DeploymentScenarios).ThenInclude(d => d.InstallSteps).ThenInclude(s => s.OutPutVariables).ThenInclude(x => x.Translations)
            .Include(c => c.DeploymentScenarios).ThenInclude(d => d.UninstallSteps)
            .Include(c => c.DeploymentScenarios).ThenInclude(d => d.UninstallSteps).ThenInclude(s => s.Translations)
            .Include(c => c.DeploymentScenarios).ThenInclude(d => d.UninstallSteps).ThenInclude(s => s.InputVariables).ThenInclude(x => x.Translations)
            .Include(c => c.DeploymentScenarios).ThenInclude(d => d.UninstallSteps).ThenInclude(s => s.OutPutVariables).ThenInclude(x => x.Translations)
            .Include(c => c.DeploymentScenarios).ThenInclude(d => d.Translations)
            .Include(c => c.DeploymentScenarios).ThenInclude(d => d.DetectionMethods)
            .Include(c => c.DeploymentScenarios).ThenInclude(d => d.InstallationBehaviorRules)
            .Include(c => c.SoftwareMeteringRules)
            .Include(c => c.Catalogs)
            .Include(c => c.Categories)
            .Include(c => c.OwnerCompany)
            .Include(c => c.AppLocalPresenceConditions)
            .SingleOrDefault(c => c.ID == id);

_context.Entry(cIApplicationInDB).CurrentValues.SetValues(cIApplication);

// Updating Translations
foreach (var upTodateTranslation in cIApplication.Translations)
{
    var existingTranslation = cIApplicationInDB.Translations.FirstOrDefault(t => t.Id == upTodateTranslation.Id);

    if (existingTranslation == null) 
    {
         cIApplicationInDB.Translations.Add(upTodateTranslation); 
    }
    else 
    {
         _context.Entry(existingTranslation).CurrentValues.SetValues(upTodateTranslation);    
    }
}

foreach (var ExistingTranslation in cIApplicationInDB.Translations)
{
    if (!cIApplication.Translations.Any(t => t.Id == ExistingTranslation.Id)) 
    {
         _context.Remove(ExistingTranslation); 
    }
}

// Updating SoftwareMeteringRules
foreach (var upTodateSoftwareMeteringRule in cIApplication.SoftwareMeteringRules)
{
    var existingSoftwareMeteringRule = cIApplicationInDB.SoftwareMeteringRules.FirstOrDefault(t => t.ID == upTodateSoftwareMeteringRule.ID);

    if (existingSoftwareMeteringRule == null) 
    {
          cIApplicationInDB.SoftwareMeteringRules.Add(upTodateSoftwareMeteringRule);
    }
    else 
    { 
         _context.Entry(existingSoftwareMeteringRule).CurrentValues.SetValues(upTodateSoftwareMeteringRule); 
    }
}

foreach (var existingSoftwareMeteringRule in cIApplicationInDB.SoftwareMeteringRules)
{
    if (!cIApplication.SoftwareMeteringRules.Any(t => t.ID == existingSoftwareMeteringRule.ID)) 
    {
         _context.Remove(existingSoftwareMeteringRule); 
    }
}

// Updating DeploymentScenarios Graph
foreach (var upToDateDS in cIApplication.DeploymentScenarios)
{
    // Look for incoming DS into the actual db CI
    var existingDeploymentScenario = cIApplicationInDB.DeploymentScenarios.FirstOrDefault(d => d.ID == upToDateDS.ID);

    // can't find DS in existing CI, it is new
    if (existingDeploymentScenario == null)
    {
        // cIApplicationInDB.DeploymentScenarios.
        cIApplicationInDB.DeploymentScenarios.Add(upToDateDS);
    }
    // Look if there are things to update in existing DS
    else
    {
         _context.Entry(existingDeploymentScenario).CurrentValues.SetValues(upToDateDS);

         // update DeploymentScenario translations
        ....
        ...doing the same way for all objects

        // and then after going through all sub collections :
        try
        {
            await _context.SaveChangesAsync();
        }
        catch (DbUpdateConcurrencyException e)
        {
            if (!CIApplicationExists(id))
            {
                return NotFound();
            }
            else
            {
            }
        }
        catch (Exception e)
        {
            throw new Exception(e.Message);
        }
    }
}
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...