Сущность 4.1 неверное имя столбца
У меня есть две таблицы новостей и новостей. Я соблюдал правила присвоения имен
Структура
НовостиКомментарии
public class NewsComment : BaseComment
{
public int NewsId { get; set; }
public virtual News News { get; set; }
}
Но исключение запроса запроса Недопустимое имя столбца "News_Id". Я знаю, что это исключение создано, когда в столбце, не связанном с таблицей.
CREATE TABLE [dbo].[NewsComments](
[Id] [int] IDENTITY(1,1) NOT NULL,
[NewsId] [int] NOT NULL,
[Text] [varchar](max) NOT NULL,
[UserId] [int] NOT NULL,
[CommentDate] [datetime] NOT NULL,
[Ip] [varchar](40) NOT NULL, CONSTRAINT [PK_NewsComments] PRIMARY KEY CLUSTERED([Id] ASC )WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY]) ON [PRIMARY]
BaseComment
public abstract class BaseComment : BasePersistentEntity, IComment
{
public int UserId { get; set; }
public virtual BaseUser User { get; set; }
[Display(ResourceType = typeof(FrameworkResurce), Name = "CommentText")]
public string Text { get; set; }
[Display(ResourceType = typeof(FrameworkResurce), Name = "CommentDate")]
public DateTime CommentDate { get; set; }
public string Ip { get; set; }
}
Новости
public class News : BaseContent
{
[Display(ResourceType = typeof(NewsResurce), Name = "NewsImage")]
public string NewsImage { get; set; }
public virtual ICollection<NewsCommentView> CommentViews { get; set; }
}
BaseContent
public abstract class BaseContent : BasePersistentEntity
{
[Display(ResourceType = typeof(FrameworkResurce), Name = "Keywords")]
public string Keywords { get; set; }
[Display(ResourceType = typeof(FrameworkResurce), Name = "TitleTranslit")]
public string TitleTranslit { get; set; }
[Required(ErrorMessageResourceType = typeof(FrameworkResurce), ErrorMessageResourceName = "IsTextEmpty")]
[Display(ResourceType = typeof(FrameworkResurce), Name = "Title")]
public string Title { get; set; }
[Display(ResourceType = typeof(FrameworkResurce), Name = "Description")]
public string Description { get; set; }
[Display(ResourceType = typeof(FrameworkResurce), Name = "Contents")]
public string Contents { get; set; }
[Display(ResourceType = typeof(FrameworkResurce), Name = "DatePublish")]
public DateTime DatePublish { get; set; }
[Display(ResourceType = typeof(FrameworkResurce), Name = "AuthorPublish")]
public string AuthorPublish { get; set; }
[Display(ResourceType = typeof(FrameworkResurce), Name = "Author")]
public string Author { get; set; }
[Display(ResourceType = typeof(FrameworkResurce), Name = "AuthorUrl")]
public string AuthorUrl { get; set; }
[Display(ResourceType = typeof(FrameworkResurce), Name = "Views")]
public int Views { get; set; }
[Display(ResourceType = typeof(FrameworkResurce), Name = "Comments")]
public int Comments { get; set; }
[Display(ResourceType = typeof(FrameworkResurce), Name = "IsComment")]
public bool IsComment { get; set; }
[Display(ResourceType = typeof(FrameworkResurce), Name = "SumVote")]
public int SumVote { get; set; }
[Display(ResourceType = typeof(FrameworkResurce), Name = "VoteCount")]
public int VoteCount { get; set; }
[NotMapped]
[Display(ResourceType = typeof(FrameworkResurce), Name = "Rating")]
public double Rating
{
get
{
if (VoteCount > 0)
{
return Math.Round((float)SumVote/VoteCount, 2);
}
return 0;
}
}
}
Query
private IEnumerable<NewsComment> GetComments()
{
var news = NewsCommentRepository.AllIncluding(c=>c.User,c=>c.News);
return news;
}
private DataRepository<NewsComment> NewsCommentRepository
{
get { return DataRepository<NewsComment>.Repository; }
}
DataRepository
public class DataRepository<T> where T : BasePersistentEntity
{
public static DataRepository<T> Repository
{
get
{
return new DataRepository<T>();
}
}
private readonly SGNContext<T> context = new SGNContext<T>();
public IQueryable<T> All
{
get { return this.context.Table; }
}
public IQueryable<T> AllIncluding(params Expression<Func<T, object>>[] includeProperties)
{
IQueryable<T> query = this.context.Table;
return includeProperties.Aggregate(query, (current, includeProperty) => current.Include(includeProperty));
}
public T Find(int id)
{
return this.context.Table.Find(id);
}
public void InsertOrUpdate(T country)
{
if (country.Id == default(int))
{
// New entity
this.context.Table.Add(country);
Save();
}
else
{
// Existing entity
this.context.Entry(country).State = EntityState.Modified;
Save();
}
}
public void Delete(int id)
{
var country = this.context.Table.Find(id);
this.context.Table.Remove(country);
this.Save();
}
private void Save()
{
this.context.SaveChanges();
}
}
Где используется GetComments
[GridAction]
public ActionResult AjaxCommentsBinding()
{
return View(new GridModel<NewsComment>
{
Data = GetComments()
});
}
NewsCommentViews
CREATE VIEW [dbo].[NewsCommentViews]
AS
SELECT dbo.NewsComments.NewsId, dbo.NewsComments.Text, dbo.NewsComments.UserId, dbo.NewsComments.CommentDate, dbo.NewsComments.Ip,
dbo.Roles.RoleName, dbo.Users.UserName, dbo.Users.DateRegistered, dbo.NewsComments.Id, dbo.Users.Avatar
FROM dbo.NewsComments INNER JOIN
dbo.Users ON dbo.NewsComments.UserId = dbo.Users.Id INNER JOIN
dbo.Roles ON dbo.Users.RoleId = dbo.Roles.Id
NewsCommentViews
[Table("NewsCommentViews")]
public class NewsCommentView : NewsComment
{
public string RoleName { get; set; }
public string UserName { get; set; }
public DateTime DateRegistered { get; set; }
public string Avatar { get; set; }
}
Ответы
Ответ 1
Проблема заключается в отношении между News
и NewsCommentView
: один конец отношения - это коллекция News.CommentViews
. Но другой конец не NewsCommentView.News
, как вы, возможно, ожидаете. Зачем? Поскольку свойство News
не объявлено в классе NewsCommentView
, а в базовом классе NewsComment
. Теперь EF не позволяет тому, что сущность участвует в отношениях с навигационным свойством, которое не является объявлено для этого класса сущности, но только в базовом классе.
Итак, поскольку у вас нет Fluent mapping, EF определяет все отношения только по соглашениям. Что происходит?
-
News
имеет свойство навигации CommentViews
, объявленное и указывающее на класс NewsCommentView
.
- EF не находит обратное свойство типа
News
, которое объявлено в классе NewsCommentView
. (Существует один, но он в базовом классе, который не учитывается.)
- Итак, EF предполагает, что другой конец отношения не подвергается в классе
NewsCommentView
.
- Не выявлено: EF не имеет свойства навигации или свойства внешнего ключа и будет считать, что необходимые столбцы внешнего ключа в таблице базы данных/представлении
NewsCommentViews
будут иметь стандартное условное имя.
- Это условное имя
NameOfEntityClass_PKPropertyName
→ News_Id
Ваше настоящее имя в представлении NewsId
. Итак, EF-запросы для столбца News_Id
, который не существует, поэтому исключение.
Исключение, вероятно, срабатывает из-за ленивой загрузки, когда ваш MVC-View обращается к NewsComment.News.CommentViews
.
Вы можете исправить эту проблему, указав имя столбца FK явно в Fluent API (насколько я знаю, нет другого способа без Fluent mapping):
public class MyContext : DbContext
{
// ...
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
modelBuilder.Entity<News>()
.HasMany(n => n.CommentViews)
.WithRequired() // <- no param because not exposed end of relation,
// nc => nc.News would throw an exception
// because nc.News is in the base class
.Map(a => a.MapKey("NewsId"));
}
}
Но осторожность. Помните, что NewsCommentView.News
- это не другой конец отношения, принадлежащего News.CommentViews
. Это означает, что если у вас есть NewsCommentView
в вашей коллекции News.CommentViews
, тогда NewsCommentView.News
возвращает не к этому объекту News
. Другой конец невидим и не отображается в модели. Приведенное выше сопоставление просто исправляет проблему с именем столбца FK, но не изменяет отношения, которые будут созданы в любом случае (за исключением, возможно, изменения отношения к необязательному, а не необязательного).
Ответ 2
У вашего SQL-кода нет символа подчеркивания между пользователем и идентификатором.
Обновите EDMX из базы данных (через меню правой кнопки мыши) и проверьте сопоставления.