包含原因InvalidCastException

时间:2016-12-25 11:27:53

标签: c# asp.net-core entity-framework-core

我在Entity Framework Core 1.1中遇到此查询的问题:

var leaves = _context.Proposal.OfType<ProposalLeave>()
                .Include(l => l.Creator).ThenInclude(e => e.User)
                .Include(l=>l.LeaveType) // this include causes the exception
                .Include(l => l.ProposalLeaveStatuses).ThenInclude(l => l.ProposalLeaveStatus)
                .ToList();

包含LeaveType会引发此错误:

  

InvalidCastException:无法转换类型为&#39; System.DateTime&#39;的对象   输入&#39; System.Int32&#39;。

     

Microsoft.EntityFrameworkCore.ChangeTracking.Internal.SimpleNonNullableDependentKeyValueFactory.TryCreateFromBuffer(Valu fdc eBuffer valueBuffer,out TKey key)

我的模特:

public class Proposal
{
    [Key]
    public int ProposalId { get; set; }

    [Required]
    public DateTime DateCreated { get; set; }

    [Required]
    public int CreatorId { get; set; }

    [ForeignKey("CreatorId")]
    public Employee Creator { get; set; }
}

public class ProposalLeave : Proposal
{
    [Required]
    public DateTime LeaveStart { get; set; }

    [Required]
    public DateTime LeaveEnd { get; set; }

    [Required]
    public int ProposalLeaveTypeId { get; set; }

    [ForeignKey("ProposalLeaveTypeId")]
    public virtual ProposalLeaveType LeaveType { get; set; }

    public virtual ICollection<ProposalLeaveStatuses> ProposalLeaveStatuses { get; set; }
}

public class ProposalLeaveType
{
    [Key]
    public int LeaveTypeId { get; set; }

    [Required, StringLength(255)]
    public string Name { get; set; }

    [Required, Column(TypeName = "text")]
    public string Description { get; set; }

    public ICollection<ProposalLeave> ProposalLeaves { get; set; }
}

我的DbContext的一部分:

public class AppDbContext : IdentityDbContext<User, Role, int>
{

    public DbSet<Proposal> Proposal { get; set; }
    public DbSet<ProposalLeaveStatus> ProposalLeaveStatus { get; set; }
    public DbSet<ProposalLeaveStatuses> ProposalLeaveStatuses { get; set; }
    public DbSet<ProposalLeaveType> ProposalLeaveType { get; set; }


    public AppDbContext(DbContextOptions<AppDbContext> options)
        : base(options)
    {
    }

    protected override void OnModelCreating(ModelBuilder modelBuilder)
    {
        base.OnModelCreating(modelBuilder);
        // proposal hierarchy
        modelBuilder.Entity<Proposal>()
            .HasDiscriminator<string>("proposal_type")
            .HasValue<Proposal>("proposal_base")
            .HasValue<ProposalCustom>("proposal_custom")
            .HasValue<ProposalLeave>("proposal_leave");

        // proposal statuses many to many
        modelBuilder.Entity<ProposalLeaveStatuses>()
            .HasOne(pt => pt.Proposal)
            .WithMany(p => p.ProposalLeaveStatuses)
            .HasForeignKey(pt => pt.ProposalId)
            .OnDelete(DeleteBehavior.Restrict);

        modelBuilder.Entity<ProposalLeaveStatuses>()
            .HasOne(pt => pt.ProposalLeaveStatus)
            .WithMany(t => t.ProposalLeaveStatuses)
            .HasForeignKey(pt => pt.ProposalLeaveStatusId);

        //Substantially, there are nothing about Proposal Leave Types, cause its relation one-to-many modeled using data annotations.
    }
}

我检查了这些模型数百次,我不知道什么是错的。它的奇怪之处我也有ProposalCustom:与ProposalCustomType竞争的Proposal模型与ProposalLeave:Proposal to ProposalLeaveType相同,它工作正常......

我发现如果我添加该行:

 var leaveTypes = _context.ProposalLeaveType.Include(plt => plt.ProposalLeaves).ToList();

在我的查询之前,它甚至可以与.Include(l=>l.LeaveType)一起使用,没有任何例外...

我在EF github上发现了一些引用InvalidCastException的bug问题,但所有这些问题都在EF Core 1.1中被标记为已修复

@Update 在这里,您可以下载重现该问题的VS2015项目: Project。 设置:

  1. 安装.Net Core 1.1。 sdk和运行时
  2. 创建空数据库并替换appsettings.json
  3. 中的连接字符串
  4. 在程序包管理器控制台中运行Update-Database
  5. 运行项目。

1 个答案:

答案 0 :(得分:1)

不幸的是,我所能做的就是确认这个问题是由EF Core错误引起的。

我能够将repro修改为以下内容:

型号:

public class Proposal
{
    [Key]
    public int ProposalId { get; set; }

    [Required, Column(TypeName = "text")]
    public string Substantiation { get; set; }

    public DateTime DateCreated { get; set; }
}

public class ProposalCustom : Proposal
{
    [Required, StringLength(255)]
    public string Name { get; set; }

    public int ProposalTypeId { get; set; }

    [ForeignKey("ProposalTypeId")]
    public virtual ProposalCustomType ProposalType { get; set; }
}

public class ProposalCustomType
{
    [Key]
    public int ProposalTypeId { get; set; }

    [Required, StringLength(255)]
    public string Name { get; set; }

    [Required, Column(TypeName = "text")]
    public string Description { get; set; }

    public ICollection<ProposalCustom> ProposalCustoms { get; set; }
}

public class ProposalLeave : Proposal
{
    public DateTime LeaveStart { get; set; }

    public DateTime LeaveEnd { get; set; }

    public int ProposalLeaveTypeId { get; set; }

    [ForeignKey("ProposalLeaveTypeId")]
    public virtual ProposalLeaveType LeaveType { get; set; }
}

public class ProposalLeaveType
{
    [Key]
    public int LeaveTypeId { get; set; }

    [Required, StringLength(255)]
    public string Name { get; set; }

    [Required, Column(TypeName = "text")]
    public string Description { get; set; }

    public ICollection<ProposalLeave> ProposalLeaves { get; set; }
}

的DbContext:

public DbSet<Proposal> Proposal { get; set; }
public DbSet<ProposalCustomType> ProposalCustomType { get; set; }
public DbSet<ProposalLeaveType> ProposalLeaveType { get; set; }

配置:

modelBuilder.Entity<Proposal>()
    .HasDiscriminator<string>("proposal_type")
    .HasValue<Proposal>("proposal_base")
    .HasValue<ProposalCustom>("proposal_custom")
    .HasValue<ProposalLeave>("proposal_leave");

数据:

db.Proposal.Add(new ProposalLeave
{
    Substantiation = "S1",
    DateCreated = DateTime.Today,
    LeaveStart = DateTime.Today,
    LeaveEnd = DateTime.Today,
    LeaveType = new ProposalLeaveType { Name = "PLT1", Description = "PLT1" }
});
db.Proposal.Add(new ProposalCustom
{
    Substantiation = "S2",
    Name = "PC1",
    ProposalType = new ProposalCustomType { Name = "PCT1", Description = "PCT1" }
});
db.SaveChanges();

查询:

var proposalCustoms = db.Proposal.OfType<ProposalCustom>()
    .Include(l => l.ProposalType)
    .ToList();

var proposalLeaves = db.Proposal.OfType<ProposalLeave>()
    .Include(l => l.LeaveType)
    .ToList();

虽然ProposalCustomProposalLeave看起来很相似,但第一个查询始终有效,第二个查询不起作用(它在某些情况下有效)你将在下面看到)。

为什么我确定这是一个错误。首先,因为查询和/或模型/配置没有任何问题。看起来它是由几个因素的组合引起的,从TPH开始,派生类属性的数量和类型以及(hmm)派生实体的类名(字母顺序?)。

例如,如果没有LeaveEnd属性,则第二个查询有效。最有趣的部分(取决于当然的观点)是,如果您将ProposalCustom课程重命名为ProposalXCustom,或ProposalLeave重命名为ProposalALeave,换句话说,请将问题重新命名为实体类名首先按字母顺序排列(我知道这听起来很疯狂),没有任何数据库结构/映射更改,查询有效!

我建议您将其报告给EF Core存储库。 Substantiation列和Required / Column属性似乎不会影响该行为。另请注意,第一个查询始终有效,执行顺序无关紧要。