На странице mvc asp.net не отображаются свойства связанных объектов - PullRequest
0 голосов
/ 26 октября 2011

У меня следующая простая структура: заявитель Possition ApplicantPosition и ApplicantPositionHistory

3-й класс имеет одну ссылку с заявителем и одну с позицией. В 4-й таблице есть одна ссылка с ApplicantPosition

На странице razon, которую я делаю, чтобы показать историю кандидата на должность, я хочу показать имя кандидата, например,

У меня есть это в html, однако оно показывает меня пустым, оно показывает мне информацию только для полей, которые находятся в одном и том же объекте, например комментарии и datemodified.

@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 { закрытый контекст HRContext = новый 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]».

Ответы [ 2 ]

2 голосов
/ 26 октября 2011

Как реализован метод Find вашего ApplicantPositionHistoryRepository? Если это что-то вроде

return entities.ApplicantPosition.SingleOrDefault(expression)

тогда вы должны включить нетерпеливую загрузку , например:

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

Кстати, именно поэтому мне лично не нравятся эти «общие» репозитории - существуют всегда ситуации, когда вам нужно что-то загружать, а также ситуации, когда это просто напрасная трата ресурсов. В «неуниверсальном» хранилище вы просто создадите два метода

GetApplicantPositionWithApplicant (int id)

GetApplicantPosition (int id)

а также ваше выражение (которое фактически является логикой запроса, если вы об этом думаете) остается в модели (хранилище) вместо контроллера.

РЕДАКТИРОВАТЬ (отвечая на комментарии)

О «боли» универсальных репозиториев. Универсальный репозиторий в целом является хорошей идеей, только если вы создаете какое-то действительно БОЛЬШОЕ приложение, в котором вы хотите разделить вещи на несколько уровней, обычно - слой доступа к данным (универсальные репозитории), бизнес-уровень (бизнес-логика, рабочие процессы, расширенная проверка и т. д.), а затем уровень управления и представления (контроллер + представление в MVC). В этом сценарии универсальные репозитории инкапсулируют только тривиальную логику CRUD и используются только бизнес-уровнем, а не контроллерами.

Если у вас нет каких-то тяжелых вещей (нетривиальных рабочих процессов, проверки, требующих внешних служб и т. Д.), То у вас нет реальной необходимости в бизнес-уровне, и вы можете «объединить» его на уровне доступа к данным (или если хотите, объедините доступ к данным в бизнес-слой, они станут одним телом одной душой:)

Итак, если у вас нет какого-то реального бизнес-уровня, вы можете получить только некоторую бизнес-логику в контроллере, потому что ваш общий репозиторий не очень подходит для этого. Неуниверсальные репозитории могут содержать вашу простую бизнес-обработку, а также вы можете внедрить пользовательский CRUD для различных сценариев - очень хороший пример - стремление загружать вещи только тогда, когда вы знаете, что они понадобятся).

Так что решение о переписывании действительно зависит от вас и зависит от вашей архитектуры и потребностей.

О вашем исключении:

С SingleOrDefault ваша история переменных содержит только один объект ApplicationPositionHistory, и ваше представление ожидает перечисление объектов ApplicationPositionHistory. Инкапсулируйте этот поиск данных в вызове хранилища с ясным возвращаемым значением, вы предотвратите ошибки этого типа.

0 голосов
/ 26 октября 2011

Можете ли вы показать код, где вы получаете список элементов из базы данных?

Вы пытались отладить этот код?Было ли все, что вы хотите показать на странице, загруженной из базы данных (потому что EF не загружает связанные сущности по умолчанию)?

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