【发布时间】:2020-09-04 19:12:47
【问题描述】:
我正在使用 EF Core 3.1 和内存数据库开发一些 API。
我对以下代码有一些问题。我无法为同一个表中的重复名称和不存在的外键生成异常。
public class Category
{
public Guid Id { get; set; }
public DateTime CreationDate { get; set; }
public DateTime? LastModifiedDate { get; set; }
public bool IsActive { get; set; }
public string Name { get; set; }
public Guid? ParentId { get; set; }
public virtual Category Parent { get; set; }
public virtual IList<Category> Children { get; set; }
}
public class ItemDbContext : DbContext
{
public ItemDbContext(DbContextOptions<ItemDbContext> options)
: base(options) {}
public DbSet<Category> Categories { get; set; }
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.HasDefaultSchema("s_items");
GetCategoryBuilder(modelBuilder);
base.OnModelCreating(modelBuilder);
}
private void GetCategoryBuilder(ModelBuilder modelBuilder)
{
modelBuilder.Entity<Category>(
entity =>
{
entity.Property(c => c.Id)
.IsRequired()
.HasColumnName("CATE_ID")
.HasMaxLength(40);
entity.Property(c => c.Name)
.IsRequired()
.IsFixedLength(false)
.IsUnicode()
.HasColumnName("CATE_NAME")
.HasMaxLength(50);
entity.Property(c => c.CreationDate)
.IsRequired()
.HasColumnName("CATE_CREATION_DATE")
.HasDefaultValueSql("CURRENT_TIMESTAMP")
.ValueGeneratedOnAdd();
entity.Property(c => c.LastModifiedDate)
.IsRequired(false)
.HasColumnName("CATE_UPDATE_DATE")
.HasDefaultValueSql("CURRENT_TIMESTAMP")
.ValueGeneratedOnUpdate();
entity.Property(c => c.IsActive)
.HasColumnName("CATE_ACTIVE")
.HasDefaultValue(true)
.ValueGeneratedOnAddOrUpdate();
entity.Property(c => c.ParentId)
.IsFixedLength(true)
.HasColumnName("CATE_PARENT_ID")
.HasMaxLength(40);
}
);
modelBuilder.Entity<Category>()
.HasOne(c => c.Parent)
.WithMany(c => c.Children)
.HasForeignKey(c => c.ParentId)
.HasConstraintName("FK_CATE_PARENT");
modelBuilder.Entity<Category>()
.ToTable("Categories")
.HasKey(c => c.Id)
.HasName("PK_CATE");
modelBuilder.Entity<Category>()
.HasIndex(u => u.Name)
.IsUnique(true)
.HasName("UK_CATE_NAME");
}
}
public class CategoryRepository : ICategoryRepository
{
private readonly ItemDbContext _context;
public CreateCategoryRepository(ItemDbContext context)
{
if (context == null)
{
throw new ArgumentNullException(nameof(context));
}
_context = context;
}
public Category Create(Category item)
{
if (item == null)
{
throw new ArgumentNullException(nameof(item));
}
Category result = null;
try
{
_context.Categories.Add(item);
int nbRowsImpacted = _context.SaveChanges();
if (nbRowsImpacted == 1)
{
result = item;
}
}
catch (InvalidOperationException ex)
{
var message = "The instance of entity type 'Category' cannot be tracked because another instance with the same key value for {'Id'} is already being tracked. When attaching existing entities, ensure that only one entity instance with a given key value is attached. Consider using 'DbContextOptionsBuilder.EnableSensitiveDataLogging' to see the conflicting key values.";
if (ex.Message == message)
{
throw new DBConcurrencyException("There is already exists a similar category");
}
}
catch (ArgumentException ex)
{
var message = "An item with the same key has already been added.";
if (ex.Message.StartsWith(message))
{
throw new DBConcurrencyException("There is already exists a similar category");
}
}
return result;
}
}
[TestClass]
public class CategoryRepository
{
[TestMethod]
[TestCategory("Category_Repository")]
public void Category_Create_NotExistantParentId()
{
#region Arrange
IServiceProvider provider = GetServiceProvider(
DatabaseType.InMemory,
injectCreateCategoryRepository: true);
ItemDbContext context = provider.GetRequiredService<ItemDbContext>();
IList<Product> products = SeedInMemory.GetProducts();
context.Products.AddRange(products);
context.SaveChanges();
Category category = new Category()
{
Id = CategoryId.New(),
Name = "Name",
CreationDate = DateTime.UtcNow,
IsActive = true,
ParentId = CategoryId.New()
};
ICategoryRepository repository = provider.GetRequiredService<ICategoryRepository>();
var message = "There is already exists a similar category";
#endregion
#region Assert
var result = Assert.ThrowsException<DBConcurrencyException>(() => repository.Create(category));
Assert.AreEqual(message, result.Message);
#endregion
}
[TestMethod]
[TestCategory("CreateCategory_Repository")]
public void CreateCategory_Create_DuplicateName()
{
#region Arrange
IServiceProvider provider = GetServiceProvider(
DatabaseType.InMemory,
injectCreateCategoryRepository: true);
ItemDbContext context = provider.GetRequiredService<ItemDbContext>();
IList<Product> products = SeedInMemory.GetProducts();
context.Products.AddRange(products);
context.SaveChanges();
Category category = new Category()
{
Id = CategoryId.New(),
Name = context.Categories.FirstOrDefault().Name,
CreationDate = DateTime.UtcNow,
IsActive = true
};
ICategoryRepository repository = provider.GetRequiredService<ICategoryRepository>();
var message = "There is already exists a similar category";
#endregion
#region Assert
var result = Assert.ThrowsException<DBConcurrencyException>(() => repository.Create(category));
Assert.AreEqual(message, result.Message);
#endregion
}
}
// Part for GetServiceProvider
services.AddDbContext<ItemDbContext>(
options =>
{
options.ConfigureWarnings(warnings => warnings.Ignore(CoreEventId.DetachedLazyLoadingWarning));
options.UseLazyLoadingProxies().UseInMemoryDatabase("Test");
});
对于重复的主键,我有一个ArgumentException。
两个测试用例都没有抛出异常,nbRowsImpacted变量等于1。为什么?
真诚地, 新井680
【问题讨论】:
标签: c# unit-testing .net-core code-first ef-core-3.1