我在一项非常简单的任务中遇到了奇怪的行为。我在SQL Server 2016上使用EF Core 2.1,我有以下代码,基本上创建了三个对象,并使用DbContext.Add()将它们插入到数据库中。
使用正确创建的实体,所有三个Add()操作都会成功返回,但是,在调用SaveChangesAsync()时, JobSchedule 永远不会插入到数据库中,我也不知道。
private async Task CreateXPTOJob(XPTOJobModel model)
{
var jobData = new XPTOJobData
{
Id = Guid.NewGuid(),
Foo = model.Foo,
Bar= model.Bar
};
Context.XPTOJobData.Add(jobData);
var jobType = await Context.JobTypes.FindByCode(EJobType.XPTO);
var jobPriority = await Context.JobPriorities.FindByCode(EJobPriority.Normal);
var jobStatus = await Context.JobStatuses.FindByCode(EJobStatus.Created);
var job = new Job
{
Id = Guid.NewGuid(),
OwnerId = UserId,
PriorityId = jobPriority.Id,
TypeId = jobType.Id,
StatusId = jobStatus.Id,
MaxRetries = 3,
XPTOJobDataId = jobData.Id
};
Context.Jobs.Add(job);
var scheduleFrequency = await Context.ScheduleFrequencies.FindByCode(EScheduleFrequency.Once);
var schedule = new JobSchedule
{
Id = Guid.NewGuid(),
Enabled = true,
FrequencyId = scheduleFrequency.Id,
JobId = jobId,
NotifyCompletion = true,
PreferredStartTime = DateTime.Now
};
Context.JobSchedules.Add(schedule);
await Context.SaveChangesAsync();
}
如果查看调试输出,我可以看到四个选择和两个插入,如下所示:
Microsoft.EntityFrameworkCore.Database.Command:Information: Executed DbCommand (3ms) [Parameters=[@__type_0='?' (DbType = Int32)], CommandType='Text', CommandTimeout='30']
SELECT TOP(2) [jobType].[Id], [jobType].[Code], [jobType].[Description], [jobType].[DisplayName], [jobType].[Name]
FROM [JobQueue].[JobTypes] AS [jobType]
WHERE [jobType].[Code] = @__type_0
Microsoft.EntityFrameworkCore.Database.Command:Information: Executed DbCommand (2ms) [Parameters=[@__priority_0='?' (DbType = Int32)], CommandType='Text', CommandTimeout='30']
SELECT TOP(2) [jobPriority].[Id], [jobPriority].[Code], [jobPriority].[Description], [jobPriority].[DisplayName], [jobPriority].[Name]
FROM [JobQueue].[JobPriorities] AS [jobPriority]
WHERE [jobPriority].[Code] = @__priority_0
Microsoft.EntityFrameworkCore.Database.Command:Information: Executed DbCommand (1ms) [Parameters=[@__status_0='?' (DbType = Int32)], CommandType='Text', CommandTimeout='30']
SELECT TOP(2) [jobStatus].[Id], [jobStatus].[Code], [jobStatus].[Description], [jobStatus].[DisplayName], [jobStatus].[Name]
FROM [JobQueue].[JobStatuses] AS [jobStatus]
WHERE [jobStatus].[Code] = @__status_0
Microsoft.EntityFrameworkCore.Database.Command:Information: Executed DbCommand (1ms) [Parameters=[@__frequency_0='?' (DbType = Int32)], CommandType='Text', CommandTimeout='30']
SELECT TOP(2) [scheduleFrequency].[Id], [scheduleFrequency].[Code], [scheduleFrequency].[Description], [scheduleFrequency].[DisplayName], [scheduleFrequency].[Name]
FROM [JobQueue].[ScheduleFrequencies] AS [scheduleFrequency]
WHERE [scheduleFrequency].[Code] = @__frequency_0
Microsoft.EntityFrameworkCore.Database.Command:Information: Executed DbCommand (1ms) [Parameters=[@p0='?' (DbType = Guid), @p1='?' (DbType = Guid), @p2='?' (DbType = Guid)], CommandType='Text', CommandTimeout='30']
SET NOCOUNT ON;
INSERT INTO [LifeCycle].[XPTOJobData] ([Id], [Foo], [Bar])
VALUES (@p0, @p1, @p2);
Microsoft.EntityFrameworkCore.Database.Command:Information: Executed DbCommand (1ms) [Parameters=[@p3='?' (DbType = Guid), @p4='?' (DbType = Guid), @p5='?' (DbType = Int32), @p6='?' (DbType = Guid), @p7='?' (DbType = Guid), @p8='?' (DbType = Guid), @p9='?' (DbType = Guid), @p10='?' (DbType = Guid)], CommandType='Text', CommandTimeout='30']
SET NOCOUNT ON;
INSERT INTO [JobQueue].[Jobs] ([Id], [YPTOJobDataId], [MaxRetries], [XPTOJobDataId], [OwnerId], [PriorityId], [StatusId], [TypeId])
VALUES (@p3, @p4, @p5, @p6, @p7, @p8, @p9, @p10);
Microsoft.AspNetCore.Mvc.Internal.ControllerActionInvoker:Information: Executed action method JobQueue.Controllers.JobsController.Post (JobQueue), returned result Microsoft.AspNetCore.Mvc.ObjectResult in 63.4302ms.
所有 FindByCode 扩展都遵循相同的逻辑:
public static Task<ScheduleFrequency> FindByCode(this IQueryable<ScheduleFrequency> queryable, EScheduleFrequency frequency)
{
return queryable.AsNoTracking().SingleAsync(scheduleFrequency => scheduleFrequency.Code == frequency);
}
为什么没有执行第三个插入的任何想法?我尝试了很多小的改动和调整,但没有成功。无论如何,谢谢你的时间和帮助!
编辑1:我正在提出更多相关代码。
的DbContext
public class MyDbContext : DbContext
{
...
public DbSet<User> Users { get; set; }
public DbSet<XPTOJobData> XPTOJobData { get; set; }
public DbSet<Job> Jobs { get; set; }
public DbSet<JobPriority> JobPriorities { get; set; }
public DbSet<JobSchedule> JobSchedules { get; set; }
public DbSet<JobStatus> JobStatuses { get; set; }
public DbSet<JobType> JobTypes { get; set; }
public DbSet<ScheduleFrequency> ScheduleFrequencies { get; set; }
...
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.ApplyConfiguration(new XPTOJobDataConfiguration());
modelBuilder.ApplyConfiguration(new JobConfiguration());
modelBuilder.ApplyConfiguration(new JobPriorityConfiguration());
modelBuilder.ApplyConfiguration(new JobScheduleConfiguration());
modelBuilder.ApplyConfiguration(new JobStatusConfiguration());
modelBuilder.ApplyConfiguration(new JobTypeConfiguration());
modelBuilder.ApplyConfiguration(new ScheduleFrequencyConfiguration());
}
}
工作
public class Job
{
// Properties
public Guid Id { get; set; }
public Guid? XPTOJobDataId { get; set; }
public Guid OwnerId { get; set; }
public Guid PriorityId { get; set; }
public Guid StatusId { get; set; }
public Guid TypeId { get; set; }
public ushort MaxRetries { get; set; }
// Navigation Properties
public XPTOJobData XPTOJobData { get; set; }
public User Owner { get; set; }
public JobPriority Priority { get; set; }
public JobStatus Status { get; set; }
public JobType Type { get; set; }
// Navigation Related Properties
public ICollection<JobSchedule> JobSchedules => _jobSchedules?.ToList();
private HashSet<JobSchedule> _jobSchedules;
public Job()
{
_jobSchedules = new HashSet<JobSchedule>();
}
}
JobPriority
public enum EJobPriority
{
Normal,
High,
Immediate
}
public class JobPriority
{
// Properties
public Guid Id { get; set; }
public EJobPriority Code { get; set; }
public string Description { get; set; }
public string DisplayName { get; set; }
public string Name { get; set; }
// Navigation Related Properties
public ICollection<Job> Jobs => _jobs?.ToList();
private HashSet<Job> _jobs;
public JobPriority()
{
_jobs = new HashSet<Job>();
}
}
JobSchedule
public class JobSchedule
{
// Properties
public Guid Id { get; set; }
public bool Enabled { get; set; }
public DateTime? EffectiveDate { get; set; }
public DateTime? ExpiryDate { get; set; }
public Guid FrequencyId { get; set; }
public Guid JobId { get; set; }
public string Name { get; set; }
public DateTime? NextRunDate { get; set; }
public bool NotifyCompletion { get; set; }
public DateTime PreferredStartTime { get; set; }
public string Recurrence { get; set; }
// Navigation Properties
public Job Job { get; set; }
public ScheduleFrequency Frequency { get; set; }
}
JobConfiguration
public class JobConfiguration : AEntityTypeConfiguration<Job>
{
protected override string TableName => "Jobs";
protected override string SchemaName => Schemas.JobQueue;
protected override void ConfigureForeignKeys(EntityTypeBuilder<Job> entity)
{
entity.HasOne(job => job.XPTOJobData)
.WithMany()
.HasConstraintName(CreateForeignKeyName("XPTOJobDataId"))
.OnDelete(DeleteBehavior.SetNull);
entity.HasOne(job => job.Owner)
.WithMany(user => user.Jobs)
.HasConstraintName(CreateForeignKeyName("OwnerId"))
.IsRequired()
.OnDelete(DeleteBehavior.Restrict);
entity.HasOne(job => job.Priority)
.WithMany(jobPriority => jobPriority.Jobs)
.HasConstraintName(CreateForeignKeyName("PriorityId"))
.IsRequired()
.OnDelete(DeleteBehavior.Restrict);
entity.HasOne(job => job.Status)
.WithMany(jobStatus => jobStatus.Jobs)
.HasConstraintName(CreateForeignKeyName("StatusId"))
.IsRequired()
.OnDelete(DeleteBehavior.Restrict);
entity.HasOne(job => job.Type)
.WithMany(jobType => jobType.Jobs)
.HasConstraintName(CreateForeignKeyName("TypeId"))
.IsRequired()
.OnDelete(DeleteBehavior.Restrict);
}
}
JobPriorityConfiguration
public class JobPriorityConfiguration : AEntityTypeConfiguration<JobPriority>
{
protected override string TableName => "JobPriorities";
protected override string SchemaName => Schemas.JobQueue;
protected override void ConfigureProperties(EntityTypeBuilder<JobPriority> entity)
{
entity.Property(jobPriority => jobPriority.Code)
.IsRequired();
entity.Property(jobPriority => jobPriority.Description)
.HasMaxLength(255)
.IsRequired();
entity.Property(jobPriority => jobPriority.DisplayName)
.HasMaxLength(50)
.IsRequired();
entity.Property(jobPriority => jobPriority.Name)
.HasMaxLength(50)
.IsRequired();
}
protected override void ConfigureIndexes(EntityTypeBuilder<JobPriority> entity)
{
entity.HasIndex(x => x.Code)
.IsUnique()
.HasName(CreateUniqueKeyName("Code"));
entity.HasIndex(x => x.Name)
.IsUnique()
.HasName(CreateUniqueKeyName("Name"));
}
}
JobScheduleConfiguration
public class JobScheduleConfiguration : AEntityTypeConfiguration<JobSchedule>
{
protected override string TableName => "JobSchedules";
protected override string SchemaName => Schemas.JobQueue;
protected override void ConfigureProperties(EntityTypeBuilder<JobSchedule> entity)
{
entity.Property(jobSchedule => jobSchedule.Name)
.HasMaxLength(255)
.IsRequired();
entity.Property(jobSchedule => jobSchedule.Recurrence)
.HasMaxLength(50);
}
protected override void ConfigureIndexes(EntityTypeBuilder<JobSchedule> entity)
{
entity.HasIndex(jobSchedule => jobSchedule.Name)
.HasName(CreateIndexName("Name"));
}
protected override void ConfigureForeignKeys(EntityTypeBuilder<JobSchedule> entity)
{
entity.HasOne(jobSchedule => jobSchedule.Job)
.WithMany(job => job.JobSchedules)
.HasConstraintName(CreateForeignKeyName("JobId"))
.IsRequired()
.OnDelete(DeleteBehavior.Cascade);
}
}
答案 0 :(得分:1)
我认为您的副作用是由此签名后的属性引起的。
public ICollection<JobSchedule> JobSchedules => _jobSchedules?.ToList();
virtual ICollection<>
的DbSet属性不再意味着同样的事情。这不会启用延迟加载导航属性。您必须在DbContext配置中启用它。
其他内容
根据帖子的内容,您可以证明EF 6可以迁移到EF Core。我认为你的问题的原因是副作用行为。 EF Core专注于常规第一种方法,而EF 6则需要详细配置。让这些惯例为你工作。
例如,从AEntityTypeConfiguration<>
派生的类(您共享的类)几乎完全重述默认约定,但外键约束的显式命名除外。 .HasConstraintName(CreateForeignKeyName("XPTOJobDataId"))
如果你能够切换到EF Core的Fkey命名方案,那么很多代码都不需要编写。至少需要编写3个类和一个接口。