计算字段的AspNetCore NullReferenceException

时间:2018-05-17 15:57:03

标签: asp.net razor asp.net-core

我有一些带有计算字段的模型可以正常显示值,但是当我尝试编辑条目或创建新条目时,我得到NullReferenceException。

型号:

public class MMSPostage
{
    public int ID { get; set; }

    [Display(Name = "Pieces")]
    public int PieceCount { get; set; }

    [DisplayFormat(DataFormatString = "{0:C3}")]
    public decimal Rate { get; set; }

    [DisplayFormat(DataFormatString = "{0:C3}")]
    [Display(Name = "Total")]
    public decimal MMSSubTotal
    {
        get
        {
            return (Rate + JobType.Cost) * PieceCount;
        }
    }
    public JobType JobType { get; set; }
}

保存时包含以下内容的编辑页面抛出NullReferenceException:

MMSPostage = await _context.MMSPostage
            .Include(m => m.JobType)
            .SingleOrDefaultAsync(m => m.ID == id);

上下文模型:

modelBuilder.Entity("SPM_Postage_Billing.Models.MMSPostage", b =>
                {
                    b.Property<int>("ID")
                        .ValueGeneratedOnAdd();

                    b.Property<int>("PieceCount");

                    b.Property<decimal>("Rate");

                    b.HasKey("ID");

                    b.HasIndex("JobTypeID");

                    b.ToTable("MMSPostage");
                });

SQL:

CONSTRAINT [FK_MMSPostage_JobType_JobTypeID] FOREIGN KEY ([JobTypeID]) REFERENCES [dbo].[JobType] ([ID]) ON DELETE SET DEFAULT

GO
CREATE NONCLUSTERED INDEX [IX_MMSPostage_JobTypeID]
    ON [dbo].[MMSPostage]([JobTypeID] ASC);

错误:

  

SPM_Postage_Billing.Models.MMSPostage.get_MMSSubTotal()in   MMSPostage.cs   +                   return(Rate + JobType.Cost)* PieceCount; Microsoft.Extensions.Internal.PropertyHelper.CallNullSafePropertyGetter(Func getter,object target)

希望我能忽略一些简单的事情。我是AspNetCore的新手,所以任何帮助都很受欢迎!

1 个答案:

答案 0 :(得分:0)

如果JobTypeMMSPostage之间的关系只是一对多,即1 MMSPostage有1 JobType而1 JobType有很多MMSPostage,然后您可以设置如下:

注意:

  • 我喜欢将持久性模型与域模型分开。所以我的应用程序中有两组模型(在视图模型旁边)。
  • 这是使用Microsoft.EntityFrameworkCore v2.0.3。对于延迟加载功能的v2.1.x,实体可能看起来不同,因为您可能需要virtual关键字。

JobTypeEntity.cs

namespace DL.SO.Persistence.EFCore.Entities
{
    public class JobTypeEntity
    {
        public int Id { get; set; }
        public string Name { get; set; }
        public double Cost { get; set; }

        public List<MMSPostageEntity> MMSPostages { get; set; }
    }
}

MMSPostageEntity.cs

namespace DL.SO.Persistence.EFCore.Entities
{
    public MMSPostageEntity
    {
        public int Id { get; set; }
        public int PieceCount { get; set; }
        public double Rate { get; set; }

        public int JobTypeId { get; set; }
        public JobTypeEntity JobType { get; set; }

        public double SubTotal
        {
            get
            {
               return (Rate + JobType.Cost) * PieceCount;
            }
        }
    }
}

然后,您需要使用Data Annotations或Fluent API将实体属性配置为SQL表列。你可以直接在OnModelCreating()上直接进行,也可以为每个实体创建配置类并在那里应用配置。

JobTypeConfiguration.cs

namespace DL.SO.Persistence.EFCore.Configurations
{
    public class JobTypeConfiguration : IEntityTypeConfiguration<JobTypeEntity>
    {
        public void Configure<EntityTypeBuilder<JobTypeEntity> builder)
        {
            builder.HasKey(x => x.Id);
            builder.Property(x => x.Name).IsRequired();
            builder.HasIndex(x => x.Name).IsUnique();
            builder.Property(x => x.Cost).IsRequired();

            builder.ToTable("JobType");
        }
    }
}

MMSPostageConfiguration.cs

namespace DL.SO.Persistence.EFCore.Configurations
{
    public class MMSPostageConfiguration : IEntityTypeConfiguration<MMSPostageEntity>
    {
        public void Configure<EntityTypeBuilder<MMSPostageEntity> builder)
        {
            builder.HasKey(x => x.Id);

            // By default, public properties with getter and setter
            // will be included!
            // Since the computed property SubTotal only has getter,
            // it should not be included by default.
            // Just in case you worry about, you can do Ignore() too!
            builder.Ignore(x => x.SubTotal);

            // This is how you configure the 1-to-many relationship!
            builder.HasOne(x => x.JobType)
                .WithMany(jt => jt.MMSPostages)
                .HasForeignKey(x => x.JobTypeId);

            builder.ToTable("MMSPostage");
        }
    }
}

AppDbContext.cs

namespace DL.SO.Persistence.EFCore
{
    public class AppDbContext : DbContext
    {
        public class AppDbContext(DbContextOptions<AppDbContext> options)
            : base(options) { }

        protected override void OnModelCreating(ModelBuilder builder)
        {
            base.OnModelCreating(builder);

            // You apply those configurations
            builder.ApplyConfiguration(new JobTypeConfiguration());
            builder.ApplyConfiguration(new MMSPostageConfiguration());
        }

        public DbSet<JobTypeEntity> JobTypes { get; set; }
        public DbSet<MMSPostageEntity> MMSPostages { get; set; }
    }
}

然后在您的编辑页面上,您应该能够从数据库中获取MMSPostageEntity,包括JobTypeEntity,并为其构建视图模型。

PostageController.cs

namespace DL.SO.Web.UI.Controllers
{
    public class PostageController : Controller
    {
        private readonly AppDbContext _dbContext;

        public PostageController(AppDbContext dbContext)
        {
           _dbContext = dbContext;
        }

        public IActionResult Edit(int id)
        {
            var postageEntity = _dbContext.MMSPostages
                    .AsNoTracking()
                    .Include(x => x.JobType)
                    .SingleOrDefault(x => x.Id == id);
            if (postageEntity == null)
            {
                return NotFound();
            }

            // Then you can construct your view model here!
            // Don't send your db entities directly to views man!
            var vm = new EditPostageViewModel
            {
                PostageId = postageEntity.Id,
                PieceCount = postageEntity.PieceCount,
                Rate = postageEntity.Rate,
                SubTotal = postageEntity.SubTotal,
                JobType = postageEntity.JobType.Name
            };

            return View(vm);
        }
    }
}

免责声明:我手工编写了所有内容,因此未经过测试,甚至可能无法编译。