实体框架 6 和 Web API 中的多对多更新控制器
本文关键字:更新 控制器 API 框架 Web 实体 | 更新日期: 2023-09-27 18:34:14
我无法在 Web API 中获取更新控制器来更新同一调用期间调用的对象和引用的对象。
我有两个类:
public class Foo
{
public int Id { get; set; }
[Required]
[MaxLength(250)]
public string Title { get; set; }
...
public virtual ICollection<Bar> Bars { get; set; }
}
public class Bar
{
public int Id { get; set }
public string Name { get; set; }
...
public virtual ICollection<Foo> Foos { get; set; }
}
和一个控制器
public class BarController : ApiController
{
private DbContext db = new DbContext();
[HttpGet]
public IQueryable<Bar> getBar(int id)
{
Bar bar = await db.Bars.FindAsync(id);
if(bar == null)
{
return NotFound();
}
return OK(bar);
}
[ResponseType(typeof(Bar))]
public async Task<IHttpActionResult> PostBar(Bar bar)
{
if (!ModelState.IsValid)
{
return BadRequest(ModelState);
}
db.Bar.Add(bar);
foreach(var foo in bar.Foos)
{
db.Foos.Attach(foo);
}
await db.SaveChangesAsync();
return CreatedAtRoute("DefaultApi", new { id = bar.Id }, bar);
}
[ResponseType(typeof(void))]
public async Task(IHttpActionResult) PutBar(int id, Bar bar)
{
if (!ModelState.IsValid)
{
return BadRequest(ModelState);
}
if (id != ticket.Id)
{
return BadRequest();
}
db.Entry(bar).State = EntityState.Modified;
await db.SaveChangesAsync();
return StatusCode(HttpStatusCode.NoContent);
}
...
}
实体框架创建一个正确称为 FooBar 的链接表(在此示例中(,其外键关系分别与 Foo 和 Bar 相关。
">get"调用解析引用,并返回一个包含柱线对象的其余部分的"foos"数组。
"post"调用也可以正确解析,尽管没有foreach
部分,但它会在链接之前创建新的 foo 对象。
但是,"put"调用仅更新 Bar 对象,但不更新指向 Foo 对象的链接。
将 put 控制器更新为:
[ResponseType(typeof(void))]
public async Task<IHttpActionResult> PutBar(int id, Bar bar)
{
...
Bar _bar = db.Bars.Find(id);
List<Foo> addedFoos = bar.Foos.Except(_bar.Foos,
new KeyEqualityComparer<Bar, int>(x => x.Id)).ToList();
List<Foo> removedFoos = _bar.Foos.Except(bar.Foos,
new KeyEqualityComparer<Bar, int>(x => x.Id)).ToList();
removedFoos.ForEach(c => _bar.Foos.Remove(c));
addedFoos.ForEach(c =>
{
if (db.Entry(c).State == EntityState.Detached)
{
db.Foos.Attach(c);
}
_bar.Foos.Add(c);
});
await db.SaveChangesAsync();
return StatusCode(HttpStatusCode.NoContent);
}
将允许更新控制器更新 Bar 的 Foo 集合,但不允许更新 Bar 的其余部分。
不幸的是,尝试同时使用这两个调用总是会抛出"附加类型的实体"错误,因为每个柱线项将一个 foo 项加载到上下文中,反之亦然。
尝试类似以下内容:
db.Entry(_bar).CurrentValues.SetValues(bar);
db.Entry(_bar).Collection(b => b.Foos).Load();
db.Entry(_bar).Collection(b => b.Foos).CurrentValue = bar.Foos;
导致每次更新时 Foo 和 Bar 的副本数量呈指数级增长。
有什么办法吗?令我感到奇怪的是,实体框架会生成正确的数据库结构,但使其无法工作。
尝试此操作,如果您有问题,请告诉我
Bar _bar = db.Bars.Find(id);
var removed =_bar.Foos.Except(bar.Foos);
var added = bar.Foos.Except(_bar.Foos);
var toRemoveTracked =_bar.Foos.Where(foo => removed.Any(r => r.FooId == foo.FooId))
.ToList();
added.ToList().ForEach(_bar.Foos.Add);
toRemoveTracked.ForEach((fooo) => {_bar.Foos.Remove(fooo);});
await db.SaveChangesAsync();
还要从ICollection<Foo>
属性中删除延迟加载(通过删除前面的 virtual 关键字来完成ICollection
(