MVC通过导航属性修改EF数据

时间:2019-03-22 02:25:25

标签: c# asp.net asp.net-mvc entity-framework model-view-controller

我最近完成了关于udemy的MVC课程,该课程构建了一个视频租赁应用程序。他介绍了如何检出电影,但我们独自决定如何检回电影。

我有一个客户模型:

public class Customer
    {
        public int Id { get; set; }

        [Required(ErrorMessage = "Please enter customer's name.")]
        [StringLength(255)]
        public string Name { get; set; }

        public bool IsSubscribedToNewsletter { get; set; }

        [Display(Name = "Date of Birth")]
        [Min18YearsIfAMember]  
        public DateTime? Birthdate { get; set; }

        public MembershipType MembershipType { get; set; }  

        [Display(Name = "Membership Type")]
        public byte MembershipTypeId { get; set; } 
    }

电影模型:

public class Movie
    {
        public int Id { get; set; }

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

        public Genre Genre { get; set; }

        [Display(Name = "Genre")]
        [Required]
        public byte GenreId { get; set; }

        [Display(Name = "Release Date")]
        public DateTime ReleaseDate { get; set; }

        public DateTime DateAdded { get; set; }

        [Display(Name = "Number in Stock")]
        [Range(1, 20)]
        public byte NumberInStock { get; set; }

        public byte NumberAvailable { get; set; }
    }

以及包含客户和电影的租赁模型:

public class Rental
    {
        public int Id { get; set; }

        [Required]
        public Customer Customer { get; set; }

        [Display(Name = "Customer Name")]
        public int CustomerId { get; set; }

        [Required]
        public Movie Movie { get; set; }

        public DateTime DateRented { get; set; }

        public DateTime? DateReturned { get; set; }
    }

我制作了一个视图,其中显示了活动的租金,并且执行了“删除”操作,将其像电影已经上映一样将其删除,但是我不知道如何通过增加可用电影的数量(Movies.NumberAvailable)。我试图以与删除操作相同的操作来执行此操作,但是我没有运气。这是“删除”操作:

public ActionResult Delete(int id)  
        {
            Rental rental = _context.Rentals.Find(id);
            //var movie = _context.Movies.Where(m => rental.Movie.Id.Contains(m.Id));
            rental.Movie.NumberAvailable++;

            _context.Rentals.Remove(rental);
            _context.SaveChanges();

            return RedirectToAction("List");
        }

我试图仅将那部电影拉进它自己的变量中并添加1,但是rental.Movie.Id部分弹出一个错误,说它没有包含的定义。如果我按上面的方式运行它,则会在rental.Movie.NumberAvailable++;处收到一个异常,内容为“对象引用未设置为对象的实例。”

有人对我有什么想法可以解决此问题吗?是的,我是菜鸟。

2 个答案:

答案 0 :(得分:0)

尝试:

Rental rental = _context.Rentals.Where(r => r.Id == id).FirstOrDefault();
rental.Movie.NumberAvailable++;

答案 1 :(得分:0)

我可以建议采用几种方法来解决此类问题。

  1. 跟踪电影实例及其状态,并根据需要计算总数。

这将类似于Movie类,但是例如,每部电影都将具有MovieDisc的集合:

public class MovieDisc
{
   public int MovieDiscId{ get; set; }
   public bool InStock { get; set; }
   public string Barcode { get; set; }
}

基本上,在检出或扫描电影时,其条形码会标识“光盘”或实例并设置InStock。从电影方面来看:

public class Movie 
{
   // ...

   public virtual ICollection<MovieDisc> Discs {get; internal set;} = new List<MovieDisc>();
   public byte NumberInStock 
   { 
      get { return Discs.Count(x => x.InStock); }
   }

   public byte NumberAvailable 
   { 
      get { return Discs.Count(x => !x.InStock); }
   }
}

此方法的警告是,要使用这些属性,必须急切加载Discs集合,否则将导致延迟加载。

  1. 对实体采用DDD方法。

域驱动设计本质上为域中状态的更改添加了控件。您无需在域实体上使用方法或操作来验证和控制有效且完整的域更改,而不必使用单个设置器来设置值。

public class Movie
{
    public int Id { get; set; }

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

    public Genre Genre { get; private set; }

    [Display(Name = "Genre")]
    [Required]
    public byte GenreId { get; private set; }

    [Display(Name = "Release Date")]
    public DateTime ReleaseDate { get; private set; }

    public DateTime DateAdded { get; private set; }

    [Display(Name = "Number in Stock")]
    [Range(1, 20)]
    public byte NumberInStock { get; private set; }

    public byte NumberAvailable { get; private set; }

    public void RentOneOut()
    {
       if (NumberInStock <= 0)
          throw new InvalidOperation("Cannot rent out a movie that has no stock.");
       if (NumberAvailable <= 0)
          throw new InvalidOperation("All movie copies are out.");

       NumberAvailable -= 1;
    }

    public void ReturnOneIn()
    {
       if (NumberInStock <= 0)
          throw new InvalidOperation("Cannot return a movie that has no stock.");
       if (NumberAvailable >= NumberInStock)
          throw new InvalidOperation("All movie copies are already in. Stocktake needed.");

       NumberAvailable += 1;
    }
}

请注意,所有设置员都是私人的。 (或者,如果要启用单元测试,则为内部),目的是将针对域实体的有效操作表示为一种方法。这样可以确保整体上执行多次检查和更新,以便可以一起更新多个属性,而不用冒着使实体处于不完整状态的风险。

这对于您的租用场景可能更实用:

public function ReturnRental(Rental rental)
{
   if (rental == null)
      throw new ArgumentNullException("rental");

   rental.Return();
}

// In Rental:
public class Rental
{
   // ...  private setters, like in Movie.

   public void Return()
   {
      Movie.ReturnOneIn();
      DateReturned = DateTime.Today;
   }
}

您想要处理由于任何原因返回失败的情况。 (数据状态不同步)

希望能给您一些想法。