Ted*_*Ted 5 mysql entity-framework-core asp.net-core
似乎 EF Core 正在执行 INSERT 而不是 UPDATE,因此 MySQL 抱怨重复键。但是,我在 DbSet 上使用 Update 方法,并且实体确实设置了主键。这会导致 MySql 中出现 DUPLICATE ENTRY 错误。
运行 VS 2019、EF Core 3.1.1 和 ASP.NET Core 3.1
模型(我不使用 Fluent 配置关系,只是约定):
public class Vehicle
{
public long Id {get; set; } // PRIMARY KEY, AUTO INCREMENT
public string RegistrationNumber { get; set; }
public VehicleSession Session { get; set; }
}
public class VehicleSession
{
public long VehicleId { get; set; }
public Vehicle Vehicle { get; set; }
public string DeviceUuid { get; set; } // PRIMARY KEY
public string AuthenticationToken { get; set; }
public string OSName { get; set; }
public string OSVersion { get; set; }
public string DeviceModel { get; set; }
}
Run Code Online (Sandbox Code Playgroud)
数据库:
表 'vehiclesessions',其中 PRIMARY KEY 是 DeviceUuid 并且具有键 'device2':
控制器:
请求来自某处。DeviceUuid 是从 HTTP 标头中获取的。
public async Task<ActionResult<VehicleLoginResponse>> Login(VehicleLogin login)
{
Request.Headers.TryGetValue(BaseConstants.DEVICE_UUID, out StringValues deviceUuid);
Vehicle vehicle = await vehicleService.Authenticate(login.Username, login.Password); // returns a Vehicle by asking dbContet: dbContext.Vehicles.FirstOrDefault(v => v.Username.Equals(username));
VehicleSession vs = await vehicleService.CreateSession(vehicle, login, deviceUuid);
// ...
}
Run Code Online (Sandbox Code Playgroud)
服务:
该服务创建一个新的 VehicleSession 对象,将其分配给 Vehicle 属性,并在 Vehicle 上执行 .Update,以便新会话与其一起保存。
public async Task<VehicleSession> CreateSession(Vehicle vehicle, VehicleLogin vehicleLogin, string deviceUuid)
{
VehicleSession vs = new VehicleSession()
{
Vehicle = vehicle,
AuthenticationToken = someTokenFetchedFromSomewhere,
DeviceModel = vehicleLogin.DeviceModel,
DeviceUuid = deviceUuid, // is 'device2'
OSName = vehicleLogin.OSName,
OSVersion = vehicleLogin.OSVersion
};
vehicle.Session = vs;
dbContext.Vehicles.Update(vehicle);
await dbContext.SaveChangesAsync();
return vs;
}
Run Code Online (Sandbox Code Playgroud)
如果我替换new VehicleSession和赋值并不重要,只需编辑一个已经存在的 Vehicle.Session,同样的错误:
public async Task<VehicleSession> CreateSession(Vehicle vehicle, VehicleLogin vehicleLogin, string deviceUuid)
{
if (vehicle.Session == null)
vehicle.Session = new VehicleSession(); // never executes this line
vehicle.Session.AuthenticationToken = someTokenFetchedFromSomewhere;
vehicle.Session.DeviceModel = vehicleLogin.DeviceModel;
vehicle.Session.DeviceUuid = deviceUuid;
vehicle.Session.OSName = vehicleLogin.OSName;
vehicle.Session.OSVersion = vehicleLogin.OSVersion;
await dbContext.SaveChangesAsync();
return vehicle.Session;
}
Run Code Online (Sandbox Code Playgroud)
这样做时,我收到一条错误消息:
Microsoft.EntityFrameworkCore.DbUpdateException:更新条目时出错。有关详细信息,请参阅内部异常。---> MySql.Data.MySqlClient.MySqlException (0x80004005): 键 'vehiclesessions.PRIMARY' 的重复条目 'device2' ---> MySql.Data.MySqlClient.MySqlException (0x80004005): 键 'device2' 的重复条目Vehiclesessions.PRIMARY'
产生的 SQL:
Failed executing DbCommand (125ms) [Parameters=[@p0='?' (Size = 95), @p1='?' (Size = 95), @p2='?' (Size = 4000), @p3='?' (Size = 4000), @p4='?' (Size = 4000), @p5='?' (DbType = Int64)], CommandType='Text', CommandTimeout='30']
INSERT INTO `VehicleSessions` (`DeviceUuid`, `AuthenticationToken`, `DeviceModel`, `OSName`, `OSVersion`, `VehicleId`)
VALUES (@p0, @p1, @p2, @p3, @p4, @p5);
fail: Microsoft.EntityFrameworkCore.Update[10000]
An exception occurred in the database while saving changes for context type 'blabla'.
Microsoft.EntityFrameworkCore.DbUpdateException: An error occurred while updating the entries. See the inner exception for details.
---> MySql.Data.MySqlClient.MySqlException (0x80004005): Duplicate entry 'device2' for key 'vehiclesessions.PRIMARY'
---> MySql.Data.MySqlClient.MySqlException (0x80004005): Duplicate entry 'device2' for key 'vehiclesessions.PRIMARY'
Run Code Online (Sandbox Code Playgroud)
为什么我收到这个错误?即使 Vehicle 对象是从 dbContext 获取的,传递过来并设置了 PRIMARY id ,它似乎完成了 INSERT 而不是 UPDATE。
所以,我想我可能已经发现了这个问题。
如上所述,我首先进行了搜索以检索车辆,如下所示:
Vehicle vehicle = dbContext.Vehicles.FirstOrDefault(v => v.Username.Equals(username));
在这种情况下,Vehicle.Session(如果存在)未填充。后来,我按照上面的代码进行了更新,但如上所述失败了。
但是,如果我将获取代码更改为:
Vehicle entityVehicle = dbContext.Vehicles
.Include(x => x.Session)// <-- NEW!
.FirstOrDefault(v => v.Username.Equals(username));
Run Code Online (Sandbox Code Playgroud)
然后就可以了。
如果我分配 .Session anew VehicleSession(...)或者更改现有对象中的属性,这并不重要。dbContext.Vehicles.Update(vehicle);如果我使用before也没关系,不需要await dbContext.SaveChangesAsync();调用。.Update(...)
唯一有区别的是使用.Include(...).
我自己的理论是:
当从数据库获取对象时,EF Core“跟踪器”开始跟踪。属性Session为 NULL。当Session稍后被填充时,跟踪器检测到它是NULL,但现在不是NULL,因此,它认为需要 INSERT。
然后,如果您填充Sessionon fetch,跟踪器会看到它在那里,并且应该更新。
| 归档时间: |
|
| 查看次数: |
3920 次 |
| 最近记录: |