DbSet에 개체 추가 후 save시 InvalidCastException 발생합니다

sql 직접 실행, DbSet.FromSqlRaw(), SqlConnection.Execute()는 문제 없지만, SaveChanges할 땐 예외가 발생합니다.

  1. sql문
INSERT INTO dbo.SubCategory (Name, MainCategoryName) VALUES ('subName1', 'mainName')
  1. DbSet.Add() 호출
context.SubCategories.Add(new SubCategory()
{
    MainCategoryName = "mainName",
    Name = "subName2"
});
context.SaveChanges();

소스 코드 보면서 확인하고 있는데 스택의 sqlDataReader.GetInt32(int i)이 코드의 184줄에서 예외 발생, dbDataReader에서 값을 구하고 int로 cast하는 중 예외가 발생하는 것으로 보이는데, 이걸 외부에서 해결 가능할까요?

메시지:
Microsoft.EntityFrameworkCore.DbUpdateException : An error occurred while saving the entity changes. See the inner exception for details.
---- System.InvalidCastException : Unable to cast object of type ‘System.String’ to type ‘System.Int32’.
스택 추적:
AffectedCountModificationCommandBatch.Consume(RelationalDataReader reader)
ReaderModificationCommandBatch.Execute(IRelationalConnection connection)
BatchExecutor.Execute(IEnumerable1 commandBatches, IRelationalConnection connection) RelationalDatabase.SaveChanges(IList1 entries)
StateManager.SaveChanges(IList1 entriesToSave) StateManager.SaveChanges(StateManager stateManager, Boolean acceptAllChangesOnSuccess) <>c.<SaveChanges>b__104_0(DbContext _, ValueTuple2 t)
SqlServerExecutionStrategy.Execute[TState,TResult](TState state, Func3 operation, Func3 verifySucceeded)
StateManager.SaveChanges(Boolean acceptAllChangesOnSuccess)
DbContext.SaveChanges(Boolean acceptAllChangesOnSuccess)
DbContext.SaveChanges()
MockDb.ctor() 줄 72
TestReviewService.ReviewService_GetReviewInfo() 줄 80
----- Inner Stack Trace -----
SqlBuffer.get_Int32()
SqlDataReader.GetInt32(Int32 i)
lambda_method417(Closure , DbDataReader )
AffectedCountModificationCommandBatch.ConsumeResultSetWithPropagation(Int32 commandIndex, RelationalDataReader reader)
AffectedCountModificationCommandBatch.Consume(RelationalDataReader reader)

좋아요 2

현재 엔터티 구조와 데이터베이스 구조가 맞지 않는것 같습니다. 확인해보시겠어요? 환경정보를 공유 주시면 좀 더 정확한 도움이 될 것 같습니다.

좋아요 3

DB는 local sql server입니다
Model

public sealed class SubCategory
{
    public string Name { get; set; }
    public string MainCategoryName { get; set; }
    public int ReviewCount { get; set; }
    public MainCategory MainCategory { get; set; }
    public List<ReviewItem> ReviewItems { get; set; }
}

Entity

modelBuilder.Entity<SubCategory>(build =>
{
    build.ToTable("SubCategory");
    build.HasKey(s => s.Name);
    build.HasOne(s => s.MainCategory).WithMany(m => m.SubCategories).HasForeignKey(m => m.MainCategoryName).IsRequired();
    build.Property(s => s.Name).HasMaxLength(30).HasColumnType("nvarchar(30)");
    build.Property(s => s.ReviewCount).HasDefaultValue(0).IsRequired();
});

Table

CREATE TABLE [dbo].[SubCategory] (
    [Name]             NVARCHAR (30) NOT NULL,
    [MainCategoryName] NVARCHAR (30) NOT NULL,
    [ReviewCount]      INT           DEFAULT ((0)) NOT NULL,
    CONSTRAINT [PK_SubCategory] PRIMARY KEY CLUSTERED ([Name] ASC),
    CONSTRAINT [FK_SubCategory_MainCategory_MainCategoryName] FOREIGN KEY ([MainCategoryName]) REFERENCES [dbo].[MainCategory] ([Name]) ON DELETE CASCADE
);

몇시간동안 뚫어져라 봐도 원인을 모르겠네요;;

좋아요 2

아래 코드처럼 작성했을 때 문제없이 동작하는 것을 보니 MainCategoryReviewItem 타입에 원인이 있을 수도 있겠네요.
저 두 모델에서 데이터 타입이 올바른지 확인이 필요합니다.

/*
 * https://forum.dotnetdev.kr/t/dbset-save-invalidcastexception/3996
 */

using var context = new TestContext();

context.Database.EnsureCreated();
context.Database.Migrate();

context.SubCategories.Add(new SubCategory()
{
	MainCategoryName = "mainName",
	Name = "subName2"
});

context.SaveChanges();

public class TestContext : DbContext
{
	public DbSet<SubCategory> SubCategories { get; set; } = default!;

	public string DbPath { get; }

	public TestContext()
	{
		var folder = Environment.SpecialFolder.UserProfile;
		var path = Environment.GetFolderPath(folder);

		DbPath = Path.Join(path, "Downloads", "categories.db");
	}

	protected override void OnConfiguring(DbContextOptionsBuilder options)
		=> options.UseSqlite($"Data Source={DbPath}");

	protected override void OnModelCreating(ModelBuilder modelBuilder)
	{
		modelBuilder.Entity<SubCategory>(build =>
		{
			build.ToTable("SubCategory");
			build.HasKey(s => s.Name);
			//build.HasOne(s => s.MainCategory).WithMany(m => m.SubCategories).HasForeignKey(m => m.MainCategoryName).IsRequired();
			build.Property(s => s.Name).HasMaxLength(30).HasColumnType("nvarchar(30)");
			build.Property(s => s.ReviewCount).HasDefaultValue(0).IsRequired();
		});
	}
}

public sealed class SubCategory
{
	public string Name { get; set; }
	public string MainCategoryName { get; set; }
	public int ReviewCount { get; set; }
	// public MainCategory MainCategory { get; set; }
	// public List<ReviewItem> ReviewItems { get; set; }
}
좋아요 6

insert trigger 지우니까 작동했습니다.
게다가 원래 트리거가 작동을 안했네요;;
일단 해결 방법은 알았는데 이유는 여전히 모르는 상태…
더 알아봐야 할 것 같습니다…

CREATE OR ALTER TRIGGER [dbo].[TR_SubCategory_AfterInsert_DML] 
   ON  [dbo].[SubCategory] 
   AFTER INSERT
AS 
BEGIN
	SET NOCOUNT ON;
	DECLARE @name nvarchar(30) SELECT MainCategoryName FROM inserted
	UPDATE dbo.MainCategory SET SubCategoryCount += 1 WHERE [Name] = @name
END
GO
좋아요 4