I have two table News and NewsComments. I followed the rules of naming
structure NewsComments
public class NewsComment : BaseComment
{
public int NewsId { get; set; }
public virtual News News { get; set; }
}
But query return exception Invalid column name "News_Id". I know what this exception created when in table not related column.
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; }
}
News
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();
}
}
Where used 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; }
}
The problem is in the relationship between News
and NewsCommentView
: One end of the relationship is the News.CommentViews
collection. But the other end is not NewsCommentView.News
as you perhaps expect. Why? Because the property News
is not declared on the NewsCommentView
class but on the base class NewsComment
. Now EF doesn't allow that an entity participates in a relationship with a navigation property which is not declared on that entity class itself but only in a base class.
So, because you don't have Fluent mapping EF defines all relationships only by conventions. What happens?
News
has a navigation property CommentViews
declared and pointing to the NewsCommentView
class.News
which is declared in the NewsCommentView
class. (There is one but it's in the base class, which doesn't count.)NewsCommentView
class.NewsCommentViews
will have a standard conventional name.NameOfEntityClass_PKPropertyName
-> News_Id
Your real name in the view is NewsId
though. So, EF queries for a column News_Id
which doesn't exist, hence the exception.
The exception is probably triggered due to lazy loading when your MVC-View accesses NewsComment.News.CommentViews
.
You can fix this problem by specifying the FK column name explicitely in Fluent API (as far as I know there no other way without 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"));
}
}
But caution: Be aware that NewsCommentView.News
is not the other end of the relationship belonging to News.CommentViews
. It means that if you have a NewsCommentView
in your News.CommentViews
collection then NewsCommentView.News
does not point back to that News
object. The other end is invisible and not exposed in the model. The mapping above just fixes the FK column name problem but doesn't change the relationships which conventions would create anyway (except maybe changing the relationship to required instead of optional).
Your SQL does not have an underscore between user and id.
Update EDMX from database (via right click menu) and check the mappings.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With