AspNetCore NullReferenceException на вычисляемых полях - PullRequest
0 голосов
/ 17 мая 2018

У меня есть некоторые модели с вычисляемыми полями, которые корректно работают для отображения значений, но я получаю исключение 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 () в MMSPostage.cs + return (Rate + JobType.Cost) * PieceCount; Microsoft.Extensions.Internal.PropertyHelper.CallNullSafePropertyGetter (функция получения, цель объекта)

Надеюсь, это что-то простое, что я пропускаю. Я новичок в AspNetCore, поэтому любая помощь приветствуется!

1 Ответ

0 голосов
/ 18 мая 2018

Если отношения между JobType и MMSPostage просто один ко многим, т. Е. 1 MMSPostage имеет 1 JobType, а 1 JobType имеет много MMSPostage с, то вы можете установить как показано ниже:

Примечание:

  • Мне нравится отделять модели постоянства от моделей предметной области. Таким образом, у меня есть 2 набора моделей в моем приложении (кроме моделей просмотра).
  • Используется 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;
            }
        }
    }
}

Затем вам нужно настроить свойства объекта для столбцов таблицы SQL, используя аннотации данных или свободный API. Вы можете сделать это непосредственно на 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);
        }
    }
}

Отказ от ответственности: Я написал все в качестве примеров вручную, поэтому они не проверены и могут даже не скомпилироваться.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...