C# DbSet<;tenty>;。添加(张力)和唯一性
在将.Net与其他数据库一起使用多年后,我正在重新学习SQLServer 我正在使用Microsoft.EntityFrameworkCore 我在SQLServer中有一个1表数据库。从该表中,我使用实体框架生成了模型。该表有一个主键(自动递增)和一个唯一键(表的三列上的自然键) EF生成的模型已生成唯一键索引的代码:C# DbSet<;tenty>;。添加(张力)和唯一性,c#,.net,sql-server,entity-framework-core,C#,.net,Sql Server,Entity Framework Core,在将.Net与其他数据库一起使用多年后,我正在重新学习SQLServer 我正在使用Microsoft.EntityFrameworkCore 我在SQLServer中有一个1表数据库。从该表中,我使用实体框架生成了模型。该表有一个主键(自动递增)和一个唯一键(表的三列上的自然键) EF生成的模型已生成唯一键索引的代码: entity.HasIndex(e => new { e.Col1, e.Col2, e.Col3}) .HasName("MyT
entity.HasIndex(e => new { e.Col1, e.Col2, e.Col3})
.HasName("MyTable_UK")
.IsUnique();
我正在使用db.MyTable.Add(myRow)向数据库集添加多行;不立即调用SaveChanges
我期待db.MyTable.Add(myRow);在违反唯一索引时引发异常。它没有;它允许在数据库集中重复行。当我保存对数据库的更改时,我会得到UK违规作为一个例外
有没有办法让它在调用SaveChanges之前抛出异常?它似乎具有执行此操作所需的所有信息。您可以轻松编写查询,以检查您尝试插入的记录是否已经存在:
if (!db.MyTable.Any(e => e.Col1 == myRow.Col1 && e.Col2 == myRow.Col2 && e.Col3 == myRow.Col3))
{
db.MyTable.Add(myRow);
}
else {
// You can throw an exception here if you'd like but I usually prefer to return 'false' or some other indicator.
}
您可以轻松编写查询,以检查您尝试插入的记录是否已存在:
if (!db.MyTable.Any(e => e.Col1 == myRow.Col1 && e.Col2 == myRow.Col2 && e.Col3 == myRow.Col3))
{
db.MyTable.Add(myRow);
}
else {
// You can throw an exception here if you'd like but I usually prefer to return 'false' or some other indicator.
}
将
SaveChanges
包装到try…catch
块始终是验证数据库操作的正确且最简单的方法
if (ModelState.IsValid)
{
try
{
_context.Add(data);
await _context.SaveChangesAsync();
return RedirectToAction(nameof(Index));
}
catch (DbUpdateException ex)
{
if (ex.InnerException is SqlException innerException)
{
// handle exception here..
ModelState.AddModelError("Col1", yourmessage1);
}
else
{
ModelState.AddModelError("Col1", yourmessage2);
}
}
catch (Exception ex)
{
ModelState.AddModelError("Col1", ex.Message);
}
}
return View();
但如果你确实想让事情变得复杂,以下是正确的方法:
假设这是你的实体
public class Example
{
[Key, Column(Order = 0)]
public int Col1 { get; set; }
[Key, Column(Order = 1)]
public int Col2 { get; set; }
[Key, Column(Order = 2)]
public int Col3 { get; set; }
public string Data { get; set; }
}
public class MyDbContext : DbContext
{
public virtual DbSet<Example> Examples { get; set; }
public override int SaveChanges()
{
ValidateEntities();
return base.SaveChanges();
}
private void ValidateEntities()
{
var hasChanges = ChangeTracker.Entries()
.Any(x => (x.Entity is Example) && (x.State == EntityState.Added || x.State == EntityState.Modified));
if (!hasChanges)
{
return;
}
var addedEntries = ChangeTracker.Entries()
.Where(x => (x.Entity is Example) && x.State == EntityState.Added)
.Select(x => x.Entity as Example);
// The tricky is right here: this.Examples.Where(...), will it execute in DB or local?
var existingEntities = this.Examples.Where(x => addedEntries.Any(e => e.Col1 == x.Col1 && e.Col2 == x.Col2 && x.Col3 == e.Col3));
if (existingEntities.Any())
{
var keys = string.Join("; ", existingEntities.Select(x => $"{x.Col1}-{x.Col2}-{x.Col3}"));
throw new Exception($"{keys} already exist.");
}
var modifiedEntries = ChangeTracker.Entries()
.Where(x => (x.Entity is Brand) && x.State == EntityState.Modified);
if (modifiedEntries.Any())
{
return;
}
////TO DO: the rest code for modified entries, more complex than added part.
}
}
公共类示例
{
[键,列(顺序=0)]
公共int Col1{get;set;}
[键,列(顺序=1)]
公共int Col2{get;set;}
[键,列(顺序=2)]
公共int Col3{get;set;}
公共字符串数据{get;set;}
}
公共类MyDbContext:DbContext
{
公共虚拟数据库集示例{get;set;}
公共覆盖int SaveChanges()
{
验证属性();
返回base.SaveChanges();
}
私有void ValidateEntities()
{
var hasChanges=ChangeTracker.Entries()
.Any(x=>(x.Entity为示例)和&(x.State==EntityState.Added | | x.State==EntityState.Modified));
如果(!hasChanges)
{
返回;
}
var addedEntries=ChangeTracker.Entries()
其中(x=>(以x.Entity为例)&&x.State==EntityState.Added)
.选择(x=>x.实体作为示例);
//棘手的问题就在这里:this.Examples.Where(…),它是在DB中执行还是在本地执行?
var existingEntities=this.Examples.Where(x=>addedEntries.Any(e=>e.Col1==x.Col1&&e.Col2==x.Col2&&x.Col3==e.Col3));
if(existingEntities.Any())
{
var keys=string.Join(“;”,existingEntities.Select(x=>$”{x.Col1}-{x.Col2}-{x.Col3}”);
抛出新异常($“{keys}已存在。”);
}
var modifiedEntries=ChangeTracker.Entries()
其中(x=>(x.Entity为品牌)和&x.State==EntityState.Modified);
if(modifiedEntry.Any())
{
返回;
}
////待办事项:修改条目的rest代码,比添加的部分更复杂。
}
}
将SaveChanges
包装到try…catch
块始终是验证数据库操作的正确且最简单的方法
if (ModelState.IsValid)
{
try
{
_context.Add(data);
await _context.SaveChangesAsync();
return RedirectToAction(nameof(Index));
}
catch (DbUpdateException ex)
{
if (ex.InnerException is SqlException innerException)
{
// handle exception here..
ModelState.AddModelError("Col1", yourmessage1);
}
else
{
ModelState.AddModelError("Col1", yourmessage2);
}
}
catch (Exception ex)
{
ModelState.AddModelError("Col1", ex.Message);
}
}
return View();
但如果你确实想让事情变得复杂,以下是正确的方法:
假设这是你的实体
public class Example
{
[Key, Column(Order = 0)]
public int Col1 { get; set; }
[Key, Column(Order = 1)]
public int Col2 { get; set; }
[Key, Column(Order = 2)]
public int Col3 { get; set; }
public string Data { get; set; }
}
public class MyDbContext : DbContext
{
public virtual DbSet<Example> Examples { get; set; }
public override int SaveChanges()
{
ValidateEntities();
return base.SaveChanges();
}
private void ValidateEntities()
{
var hasChanges = ChangeTracker.Entries()
.Any(x => (x.Entity is Example) && (x.State == EntityState.Added || x.State == EntityState.Modified));
if (!hasChanges)
{
return;
}
var addedEntries = ChangeTracker.Entries()
.Where(x => (x.Entity is Example) && x.State == EntityState.Added)
.Select(x => x.Entity as Example);
// The tricky is right here: this.Examples.Where(...), will it execute in DB or local?
var existingEntities = this.Examples.Where(x => addedEntries.Any(e => e.Col1 == x.Col1 && e.Col2 == x.Col2 && x.Col3 == e.Col3));
if (existingEntities.Any())
{
var keys = string.Join("; ", existingEntities.Select(x => $"{x.Col1}-{x.Col2}-{x.Col3}"));
throw new Exception($"{keys} already exist.");
}
var modifiedEntries = ChangeTracker.Entries()
.Where(x => (x.Entity is Brand) && x.State == EntityState.Modified);
if (modifiedEntries.Any())
{
return;
}
////TO DO: the rest code for modified entries, more complex than added part.
}
}
公共类示例
{
[键,列(顺序=0)]
公共int Col1{get;set;}
[键,列(顺序=1)]
公共int Col2{get;set;}
[键,列(顺序=2)]
公共int Col3{get;set;}
公共字符串数据{get;set;}
}
公共类MyDbContext:DbContext
{
公共虚拟数据库集示例{get;set;}
公共覆盖int SaveChanges()
{
验证属性();
返回base.SaveChanges();
}
私有void ValidateEntities()
{
var hasChanges=ChangeTracker.Entries()
.Any(x=>(x.Entity为示例)和&(x.State==EntityState.Added | | x.State==EntityState.Modified));
如果(!hasChanges)
{
返回;
}
var addedEntries=ChangeTracker.Entries()
其中(x=>(以x.Entity为例)&&x.State==EntityState.Added)
.选择(x=>x.实体作为示例);
//棘手的问题就在这里:this.Examples.Where(…),它是在DB中执行还是在本地执行?
var existingEntities=this.Examples.Where(x=>addedEntries.Any(e=>e.Col1==x.Col1&&e.Col2==x.Col2&&x.Col3==e.Col3));
if(existingEntities.Any())
{
var keys=string.Join(“;”,existingEntities.Select(x=>$”{x.Col1}-{x.Col2}-{x.Col3}”);
抛出新异常($“{keys}已存在。”);
}
var modifiedEntries=ChangeTracker.Entries()
其中(x=>(x.Entity为品牌)和&x.State==EntityState.Modified);
if(modifiedEntry.Any())
{
返回;
}
////待办事项:修改条目的rest代码,比添加的部分更复杂。
}
}
感谢您快速简洁的回答!对我来说有道理。不幸的是,Any对db.MyTable不可用-我只获得AnyAsync。我对wait db.MyTable.AnyAsync进行了更改,并将我正在编写的方法改为“async”。当我运行时,程序在AnyAsync退出,不会抛出异常。是的,这是正确的答案。在我上面的评论中。任何都没有显示在intellisense中,因为我没有使用System.Linq!!真烦人。对这个解决方案的一个重要修正。db.MyTable.Any仅在接受的行中查找。如果在调用SaveChanges之前搜索数据库集,则需要使用db.MyTable.Local.Any查找已添加但未保存的行。感谢您快速简洁的回答!对我来说有道理。不幸的是,Any对db.MyTable不可用-我只获得AnyAsync。我对wait db.MyTable.AnyAsync进行了更改,并将我正在编写的方法改为“async”。当我运行时,程序在AnyAsync退出,没有异常