asp.net mvc页面未显示关联对象的属性

时间:2011-10-26 09:50:51

标签: asp.net-mvc entity-framework entity-framework-4.1

我有以下简单的结构: 申请人 Possition ApplicantPosition 和ApplicantPositionHistory

第3课有一个申请人参考,一个有职位。 第4个表有一个与ApplicantPosition

的引用

在razon页面中,我正在显示申请人的每个职位的历史,我想以申请人的姓名显示

我在html中有这个,但它显示我为空,它只显示同一对象中的字段的信息,例如注释和日期修改。

@foreach (var item in Model) {
    <tr>
        <td>
            @Html.DisplayFor(modelItem => item.applicantPosition.Applicant.name)
        </td>
        <td>
            @Html.DisplayFor(modelItem => item.applicantPosition.Position.name)
        </td>
        <td>
            @Html.DisplayFor(modelItem => item.oldStatus.status)
        </td>
        <td>
            @Html.DisplayFor(modelItem => item.newStatus.status)
        </td>
        <td>
            @Html.DisplayFor(modelItem => item.comments)
        </td>
        <td>
            @Html.DisplayFor(modelItem => item.dateModified)
        </td>

我的模型是这样的:

namespace Data.Model
{

    public class Position
    {
        [DatabaseGenerated(System.ComponentModel.DataAnnotations.DatabaseGeneratedOption.Identity)]   
        public int PositionID { get; set; }

        [Required(ErrorMessage = "Position name is required.")]
        [StringLength(20, MinimumLength = 3, ErrorMessage = "Name should not be longer than 20 characters.")]
        [Display(Name = "Position name")]              
        public string name { get; set; }

        [Required(ErrorMessage = "Number of years is required")] 
        [Display(Name = "Number of years")]
        [YearsValidationAttribute(5, ErrorMessage = "{0} value must be greater than {1} years.")]        
        public int yearsExperienceRequired { get; set; }

        public virtual ICollection<ApplicantPosition> applicantPosition { get; set; }
    }

    public class Applicant
    {
        [DatabaseGenerated(System.ComponentModel.DataAnnotations.DatabaseGeneratedOption.Identity)]      
        public int ApplicantID { get; set; }

        [Required(ErrorMessage = "Name is required")] 
        [StringLength(20, MinimumLength = 3, ErrorMessage="Name should not be longer than 20 characters.")]
        [Display(Name = "First and LastName")]
        public string name { get; set; }

        [Required(ErrorMessage = "Telephone number is required")] 
        [StringLength(20, MinimumLength = 3, ErrorMessage = "Telephone should not be longer than 20 characters.")]
        [Display(Name = "Telephone Number")]
        public string telephone { get; set; }

        [Required(ErrorMessage = "Skype username is required")] 
        [StringLength(20, MinimumLength = 3, ErrorMessage = "Skype user should not be longer than 20 characters.")]
        [Display(Name = "Skype Username")]
        public string skypeuser { get; set; }

        public byte[] photo { get; set; }

        public virtual ICollection<ApplicantPosition> applicantPosition { get; set; }


    }

    public class ApplicantPosition
    {
        [Key]
        [Column("ApplicantID", Order = 0)]
        public int ApplicantID { get; set; }

        [Key]
        [Column("PositionID", Order = 1)]
        public int PositionID { get; set; }

        public virtual Position Position { get; set; }

        public virtual Applicant Applicant { get; set; }

        [Required(ErrorMessage = "Applied date is required")] 
        [DisplayFormat(DataFormatString = "{0:d}", ApplyFormatInEditMode = true)]
        [Display(Name = "Date applied")]     
        public DateTime appliedDate { get; set; }

        [Column("StatusID", Order = 0)]
        public int StatusID { get; set; }

        public Status Status { get; set; }

        //[NotMapped]
        //public int numberOfApplicantsApplied
        //{
        //    get
        //    {
        //        int query =
        //             (from ap in Position
        //              where ap.Status == (int)Status.Applied
        //              select ap
        //                  ).Count();
        //        return query;
        //    }
        //}
    }


    public class Address
    {
        [StringLength(20, MinimumLength = 3, ErrorMessage = "Country should not be longer than 20 characters.")]
        public string Country { get; set; }

        [StringLength(20, MinimumLength = 3, ErrorMessage = "City  should not be longer than 20 characters.")]
        public string City { get; set; }

        [StringLength(50, MinimumLength = 3, ErrorMessage = "Address  should not be longer than 50 characters.")]
        [Display(Name = "Address Line 1")]     
        public string AddressLine1 { get; set; }

        [Display(Name = "Address Line 2")]
        public string AddressLine2 { get; set; }   

    }



    public class ApplicationPositionHistory
    {
        [DatabaseGenerated(System.ComponentModel.DataAnnotations.DatabaseGeneratedOption.Identity)]
        public int ApplicationPositionHistoryID { get; set; }

        public ApplicantPosition applicantPosition { get; set; }

        [Column("oldStatusID")]
        public int oldStatusID { get; set; }

        [Column("newStatusID")]
        public int newStatusID { get; set; }

        [ForeignKey("oldStatusID")]
        public Status oldStatus { get; set; }

        [ForeignKey("newStatusID")]
        public Status newStatus { get; set; }

        [StringLength(500, MinimumLength = 3, ErrorMessage = "Comments  should not be longer than 500 characters.")]
        [Display(Name = "Comments")]
        public string comments { get; set; }

        [DisplayFormat(DataFormatString = "{0:d}", ApplyFormatInEditMode = true)]
        [Display(Name = "Date")]     
        public DateTime dateModified { get; set; }
    }

    public class Status
    {
        [DatabaseGenerated(System.ComponentModel.DataAnnotations.DatabaseGeneratedOption.Identity)]
        public int StatusID { get; set; }

        [StringLength(40, MinimumLength = 3, ErrorMessage = "Status  should not be longer than 20 characters.")]
        [Display(Name = "Status")]
        public string status { get; set; }

    }



}

控制器操作

public ViewResult History(int applicantId, int positionId)
        {
            var history= unitOfWork.ApplicantPositionHistoryRepository.Find(d => d.applicantPosition.ApplicantID == applicantId && d.applicantPosition.PositionID == positionId);
            return View(history);
        }

EDIT UnitofWork.cs

公共类UnitOfWork     {         private HRContext context = new HRContext();

    private BaseRepository<Position> positiontRepository;
    private BaseRepository<ApplicantPosition> applicantpositiontRepository;
    private BaseRepository<Applicant> applicantRepository;
    private BaseRepository<Status> statusRepository;
    private BaseRepository<ApplicationPositionHistory> applicantPositionHistoryRepository;


    public BaseRepository<ApplicationPositionHistory> ApplicantPositionHistoryRepository
    {
        get
        {

            if (this.applicantPositionHistoryRepository == null)
            {
                this.applicantPositionHistoryRepository = new BaseRepository<ApplicationPositionHistory>(context);
            }
            return applicantPositionHistoryRepository;
        }
    }

    public BaseRepository<Status> StatusRepository
    {
        get
        {

            if (this.statusRepository == null)
            {
                this.statusRepository = new BaseRepository<Status>(context);
            }
            return statusRepository;
        }
    }

    public BaseRepository<Applicant> ApplicantRepository
    {
        get
        {

            if (this.applicantRepository == null)
            {
                this.applicantRepository = new BaseRepository<Applicant>(context);
            }
            return applicantRepository;
        }
    }

    public BaseRepository<Position> PositionRepository
    {
        get
        {

            if (this.positiontRepository == null)
            {
                this.positiontRepository = new BaseRepository<Position>(context);
            }
            return positiontRepository;
        }
    }

    public BaseRepository<ApplicantPosition> ApplicantPositionRepository
    {
        get
        {

            if (this.applicantpositiontRepository == null)
            {
                this.applicantpositiontRepository = new BaseRepository<ApplicantPosition>(context);
            }
            return applicantpositiontRepository;
        }
    }

    public void Save()
    {
        context.SaveChanges();
    }

    private bool disposed = false;

    protected virtual void Dispose(bool disposing)
    {
        if (!this.disposed)
        {
            if (disposing)
            {
                context.Dispose();
            }
        }
        this.disposed = true;
    }

    public void Dispose()
    {
        Dispose(true);
        GC.SuppressFinalize(this);
    }
}

HRContext.cs

 public class HRContext : DbContext
    {
        public DbSet<Position> Positions { get; set; }
        public DbSet<Applicant> Applicants { get; set; }
        public DbSet<ApplicantPosition> ApplicantsPositions { get; set; }
        public DbSet<ApplicationPositionHistory> ApplicationsPositionHistory { get; set; }
        public DbSet<Status> Status { get; set; }

        protected override void OnModelCreating(DbModelBuilder modelBuilder)
        {
            modelBuilder.Entity<Position>().ToTable("Position");
            modelBuilder.Entity<Applicant>().ToTable("Applicant");
            modelBuilder.Entity<ApplicantPosition>().ToTable("ApplicantPosition");
            modelBuilder.Entity<ApplicationPositionHistory>().ToTable("ApplicationsPositionHistory");
            modelBuilder.Entity<Status>().ToTable("Status");

            modelBuilder.Entity<Position>().Property(c => c.name).IsRequired();
            modelBuilder.Entity<Applicant>().Property(c => c.name).IsRequired();
            modelBuilder.Entity<ApplicantPosition>().Property(c => c.appliedDate).IsRequired();
            modelBuilder.Entity<ApplicationPositionHistory>().Property(c => c.ApplicationPositionHistoryID).IsRequired();
            modelBuilder.Entity<Status>().Property(c => c.StatusID).IsRequired();

            modelBuilder.Conventions.Remove<OneToManyCascadeDeleteConvention>();
            base.OnModelCreating(modelBuilder);
        }
    }

BaseRepository.cs

public class BaseRepository<TEntity> : IRepository<TEntity> where TEntity : class
    {
        internal HRContext context;
        internal DbSet<TEntity> dbSet;

        public BaseRepository(HRContext context)
        {
            this.context = context;
            this.dbSet = context.Set<TEntity>();
        }

        public virtual TEntity GetByID(object id)
        {
            return dbSet.Find(id);
        }


        public virtual void Insert(TEntity entity)
        {
            dbSet.Add(entity);

        }

        public virtual void Delete(object id)
        {
            TEntity entityToDelete = dbSet.Find(id);
            Delete(entityToDelete);
        }

        public virtual void DeleteAll(List<TEntity> entities)
        {
            foreach (var entity in entities)
            {
                this.Delete(entity);
            }
        }

        public virtual List<TEntity> GetAll()
        {
            return context.Set<TEntity>().ToList();
        }

        public virtual void Delete(TEntity entityToDelete)
        {
            if (context.Entry(entityToDelete).State == EntityState.Detached)
            {
                dbSet.Attach(entityToDelete);
            }
            dbSet.Remove(entityToDelete);
        }

        public virtual void Update(TEntity entityToUpdate)
        {
            dbSet.Attach(entityToUpdate);
            context.Entry(entityToUpdate).State = EntityState.Modified;
        }

        public IQueryable<TEntity> Find(System.Linq.Expressions.Expression<Func<TEntity, bool>> predicate)
        {
            return dbSet.Where(predicate);
        }
    }

IRepository.cs

public interface IRepository<TEntity>
    {
        IQueryable<TEntity> Find(Expression<Func<TEntity, bool>> predicate);

        void Insert(TEntity entity);

        void Delete(TEntity entity);

        void DeleteAll(List<TEntity> entities);
    }

我更改了通用存储库并使用以下方法更改了控制器操作方法:

public ViewResult History(int applicantId, int positionId)
{
    //var history= unitOfWork.ApplicantPositionHistoryRepository.Find(d => d.applicantPosition.ApplicantID == applicantId && d.applicantPosition.PositionID == positionId);
     var history= db.ApplicationsPositionHistory.Include("ApplicantPosition").SingleOrDefault(d => d.applicantPosition.ApplicantID == applicantId && d.applicantPosition.PositionID == positionId);

    return View(history);
}

但是我得到了这个例外:

传递到字典中的模型项的类型为“Data.Model.ApplicationPositionHistory”,但此字典需要类型为“System.Collections.Generic.IEnumerable`1 [Data.Model.ApplicationPositionHistory]”的模型项。 / p>

2 个答案:

答案 0 :(得分:2)

如何实现ApplicantPositionHistoryRepository的Find方法?如果它像

那样
return entities.ApplicantPosition.SingleOrDefault(expression)

然后您必须启用急切加载,如下所示:

return entities.ApplicantPosition.Include("Applicant").SingleOrDefault(expression)

顺便说一下,这就是为什么我个人不喜欢这些“通用”存储库 - 有总是的情况,你需要急切加载某些东西,以及它只是浪费资源的情况。在“非通用”存储库中,您只需创建两个方法

GetApplicantPositionWithApplicant(int id)

GetApplicantPosition(int id)

以及你的表达式(如果你考虑它实际上是查询逻辑)保留在模型(存储库)而不是控制器中。

编辑(回答评论)

关于通用存储库的“痛苦”:只有在构建一些真正的BIG应用程序时,通用存储库才是一个好主意,您希望在更多层中分离内容,通常是 - 数据访问层(通用存储库),业务层(业务逻辑,工作流,高级验证等),然后是控制和表示层(控制器+ MVC中的视图)。在这种情况下,通用存储库仅封装普通的CRUD逻辑,仅由业务层使用,而不是由控制器使用。

如果您没有进行繁重的工作(非平凡的工作流程,验证需要外部服务等),那么您就不需要业务层,并且可以将其“合并”在数据访问层中(或如果你愿意,将数据访问合并到商业层,它们就会成为一体的灵魂:)

因此,如果您没有真正的业务层,那么您最终只能在控制器中使用某些业务逻辑,因为您的通用存储库并不是非常适合这种情况。非通用存储库可以包含您的简单业务处理,并且您还可以为不同的场景实现自定义CRUD - 非常好的示例是只有在您知道需要时才会急切加载事物。

所以重写的决定取决于你,取决于你的架构和需求。

关于您的例外:

使用SingleOrDefault,您的变量历史记录只包含一个ApplicationPositionHistory对象,您的视图需要枚举ApplicationPositionHistory对象。使用明确的返回值将此数据检索封装在存储库调用中,您将防止此类错误。

答案 1 :(得分:0)

您能否显示从数据库中获取项目列表的代码?

您是否尝试调试该代码?是否要在从数据库加载的页面上显示所有内容(因为默认情况下EF不加载关联实体)?