更新 Entity Framework 多对多导航
Update Entity Framework Many to Many Navigation
我有这个实体:
public class Project : ModelBase
{
private string _title = Resources.NewProject, _description;
/// <summary>
/// <c>Create</c> creates a new project with empty parameter
/// </summary>
/// <returns>A project</returns>
public static Project Create() => new();
/// <summary>
/// The projects's title
/// </summary>
[JsonPropertyName("title")]
public string Title { get => _title; set => SetProperty(ref _title, value); }
/// <summary>
/// The projects's description
/// </summary>
[JsonPropertyName("description")]
public string Description { get => _description; set => SetProperty(ref _description, value); }
/// <summary>
/// The collection of developers assigned to the project
/// </summary>
[JsonPropertyName("developers")]
public ICollection<Developer> Developers { get; set; } = new HashSet<Developer>();
/// <summary>
/// The collection of incidents assigned to the project
/// </summary>
[JsonPropertyName("incidents")]
public ICollection<Incident> Incidents { get; set; } = new HashSet<Incident>();
}
我正在使用此代码更新实体:
public async Task<bool> UpdateAsync(IncidentManager.Common.Models.Project project)
{
await _semaphore.WaitAsync();
try
{
using var context = new IncidentManagerContext(_connectionString);
context.Entry(await context.Projects.FirstOrDefaultAsync(x => x.Id == project.Id)).CurrentValues.SetValues(project);
bool saveFailed;
do
{
saveFailed = false;
try
{
await context.SaveChangesAsync();
}
catch (DbUpdateConcurrencyException ex)
{
saveFailed = true;
var entry = ex.Entries.Single();
entry.OriginalValues.SetValues(entry.GetDatabaseValues());
}
} while (saveFailed);
}
catch (Exception) { return false; }
finally { _semaphore.Release(); }
return true;
}
Project to Developer 是多对多的关系。如果将开发人员添加到项目中,我的更新方法不会更新导航属性。我知道“SetValues”不适用于导航属性。我无法使用 .Net6 和 EF Core 6 为我的案例找到任何可行的解决方案。任何人都可以帮助更新导航属性吗?
经过几天的研究,我终于想出了以下解决方案。任何改进的想法表示赞赏。他们的关键是使用来自上下文的跟踪实体,而不是来自更新源的未跟踪实体:
public async Task<bool> UpdateAsync(IncidentManager.Common.Models.Project project)
{
await _semaphore.WaitAsync();
try
{
//Create a new context
using var context = new IncidentManagerContext(_connectionString);
//Get the existing entry
var existing = context.Entry(await context.Projects.Where(x => x.Id == project.Id).Include(x => x.Developers).FirstOrDefaultAsync());
//Update the existing entry's non-navigational properties
existing.CurrentValues.SetValues(project);
//Add new relations if any
foreach (var incomingDeveloper in project.Developers)
{
bool add = true;
foreach (var existingDeveloper in existing.Entity.Developers)
{
if (incomingDeveloper.Id == existingDeveloper.Id)
{
add = false;
break;
}
}
//Add from context source, not from incoming
if (add) existing.Entity.Developers.Add(await context.Developers.FindAsync(incomingDeveloper.Id));
}
//Remove relations if any
foreach (var existingDeveloper in existing.Entity.Developers)
{
bool remove = true;
foreach (var incomingDeveloper in project.Developers)
{
if (incomingDeveloper.Id == existingDeveloper.Id)
{
remove = false;
break;
}
}
//Remove from context source, not from incoming
if (remove) existing.Entity.Developers.Remove(await context.Developers.FindAsync(existingDeveloper.Id));
}
bool saveFailed;
do
{
saveFailed = false;
try
{
await context.SaveChangesAsync();
}
catch (DbUpdateConcurrencyException ex)
{
saveFailed = true;
var entry = ex.Entries.Single();
entry.OriginalValues.SetValues(entry.GetDatabaseValues());
}
} while (saveFailed);
}
catch (Exception) { return false; }
finally { _semaphore.Release(); }
return true;
}
我有这个实体:
public class Project : ModelBase
{
private string _title = Resources.NewProject, _description;
/// <summary>
/// <c>Create</c> creates a new project with empty parameter
/// </summary>
/// <returns>A project</returns>
public static Project Create() => new();
/// <summary>
/// The projects's title
/// </summary>
[JsonPropertyName("title")]
public string Title { get => _title; set => SetProperty(ref _title, value); }
/// <summary>
/// The projects's description
/// </summary>
[JsonPropertyName("description")]
public string Description { get => _description; set => SetProperty(ref _description, value); }
/// <summary>
/// The collection of developers assigned to the project
/// </summary>
[JsonPropertyName("developers")]
public ICollection<Developer> Developers { get; set; } = new HashSet<Developer>();
/// <summary>
/// The collection of incidents assigned to the project
/// </summary>
[JsonPropertyName("incidents")]
public ICollection<Incident> Incidents { get; set; } = new HashSet<Incident>();
}
我正在使用此代码更新实体:
public async Task<bool> UpdateAsync(IncidentManager.Common.Models.Project project)
{
await _semaphore.WaitAsync();
try
{
using var context = new IncidentManagerContext(_connectionString);
context.Entry(await context.Projects.FirstOrDefaultAsync(x => x.Id == project.Id)).CurrentValues.SetValues(project);
bool saveFailed;
do
{
saveFailed = false;
try
{
await context.SaveChangesAsync();
}
catch (DbUpdateConcurrencyException ex)
{
saveFailed = true;
var entry = ex.Entries.Single();
entry.OriginalValues.SetValues(entry.GetDatabaseValues());
}
} while (saveFailed);
}
catch (Exception) { return false; }
finally { _semaphore.Release(); }
return true;
}
Project to Developer 是多对多的关系。如果将开发人员添加到项目中,我的更新方法不会更新导航属性。我知道“SetValues”不适用于导航属性。我无法使用 .Net6 和 EF Core 6 为我的案例找到任何可行的解决方案。任何人都可以帮助更新导航属性吗?
经过几天的研究,我终于想出了以下解决方案。任何改进的想法表示赞赏。他们的关键是使用来自上下文的跟踪实体,而不是来自更新源的未跟踪实体:
public async Task<bool> UpdateAsync(IncidentManager.Common.Models.Project project)
{
await _semaphore.WaitAsync();
try
{
//Create a new context
using var context = new IncidentManagerContext(_connectionString);
//Get the existing entry
var existing = context.Entry(await context.Projects.Where(x => x.Id == project.Id).Include(x => x.Developers).FirstOrDefaultAsync());
//Update the existing entry's non-navigational properties
existing.CurrentValues.SetValues(project);
//Add new relations if any
foreach (var incomingDeveloper in project.Developers)
{
bool add = true;
foreach (var existingDeveloper in existing.Entity.Developers)
{
if (incomingDeveloper.Id == existingDeveloper.Id)
{
add = false;
break;
}
}
//Add from context source, not from incoming
if (add) existing.Entity.Developers.Add(await context.Developers.FindAsync(incomingDeveloper.Id));
}
//Remove relations if any
foreach (var existingDeveloper in existing.Entity.Developers)
{
bool remove = true;
foreach (var incomingDeveloper in project.Developers)
{
if (incomingDeveloper.Id == existingDeveloper.Id)
{
remove = false;
break;
}
}
//Remove from context source, not from incoming
if (remove) existing.Entity.Developers.Remove(await context.Developers.FindAsync(existingDeveloper.Id));
}
bool saveFailed;
do
{
saveFailed = false;
try
{
await context.SaveChangesAsync();
}
catch (DbUpdateConcurrencyException ex)
{
saveFailed = true;
var entry = ex.Entries.Single();
entry.OriginalValues.SetValues(entry.GetDatabaseValues());
}
} while (saveFailed);
}
catch (Exception) { return false; }
finally { _semaphore.Release(); }
return true;
}