Mapping a collection of entities in fluent nHibernate - nhibernate

I'm developing a question/answer based application
I have a Post table in the db along the lines of:
ID
Title
Body
DateCreated
AuthorID
AuthorName (in case user not registered)
AutherEmail (as above)
PostType (enum - 1 if question, 2 if reply)
Show (bit field)
Then, there is "PostBase" - which is an abstract class (this has properties common to both Question and Answer - listed below)
I then have an internal "Post" class, which descends from PostBase, and therefore has all the properties listed above.
There is then have a Question class, and an Answer class.
Both use Post as a base class.
Here are the classes:
public abstract class PostBase
{
{
get { return postCreatorEmail; }
//todo: add email address validation here?
set { postCreatorEmail = value; }
} private IList<Attachment> attachments = new List<Attachment>();
public PostBase()
{
//init logic here
}
public virtual DateTime DateCreated { get; set; }
public virtual string ID { get; set; }
public virtual string Body { get; set; }
public virtual DateTime DateLastModified { get; set; }
public virtual string PostCreatorName { get; set; }
public virtual string PostCreatorEmail { get; set; }
public virtual int PostCreatorID { get; set; }
public virtual PostType PostType { get; set; }
public virtual PostSource PostSource { get; set; }
public virtual bool Show { get; set; }
public IEnumerable<Attachment> Attachments { get { return attachments; } }
}
Post
internal class Post : PostBase
{
public virtual List<string> Tags { get; set; }
public virtual string Title { get; set; }
public virtual string ParentPostID { get; set; }
}
Question:
public class Question : PostBase
{
public Question()
{
tags = new List<string>();
}
public string Title { get; set; }
public List<string> Tags { get { return tags; } }
public int NumberOfReplies { get; set; }
}
Answer:
public class Answer : PostBase
{
public string QuestionID { get; set; }
}
I use automapper to map to / from Answer and Post or Question and Post
What I'm trying to do, is create the following class:
public class QuestionWithAnswers
{
public Question Question { get; set; }
public IEnumerable<Answer> Answers { get; set; }
}
Which basically has a "Question" - remember this is just a Post in the db, with a ParentPostID of 0, with a list of Answers (where ParentPostID is equal to Question.ID ... or along those lines)
My question is - how do I map this in fluent nHibernate?

You could use Automapper for QuestionWithAnswers, but Answers has to have an IList and not an IEnumerable:
public class QuestionWithAnswers
{
public virtual Question Question { get; set; }
public virtual IList<Answer> Answers { get; set; }
}
What I don't understand is the existence of QuestionWithAnswers, when the following (I think) should do:
public class Question : PostBase
{
public virtual string Title { get; set; }
public virtual List<string> Tags { get { return tags; } }
public virtual int NumberOfReplies { get; set; }
public virtual IList<Answer> Answers { get; set; }
}
If the question has answers, you'll get them, and if it does not, you don't.

Related

How to configure One to Many relationship in Entity Framework

I am creating API in ASP .NET Core that will retrieve posts with user Id. Post should contain text and Id of a user who posted it.
I have two models Users and Posts and I need help on how to configure this relationship
I want one User to have many posts
Currently my user model contains
public class User
{
public int Id { get; set; }
public string Username { get; set; }
public List<Post> Posts { get; set; }
}
And my Post model
public class Post
{
public int Id { get; set; }
public string Text { get; set; }
}
What is the best way to do this ?
One to many relationships ( User to have many posts).
public class User{
public int Id { get; set; }
public string Username { get; set; }
public List<Post> Posts { get; set; }
}
public class Post
{
public int Id { get; set; }
public string Text { get; set; }
//Navigation
public int UserId { get; set; }
public User User{ get; set; }
}
this is your Model Class:
public class User
{
public int Id { get; set; }
public string Username { get; set; }
public Virtual List<Post> Posts { get; set; }
}
public class Post
{
public int Id { get; set; }
public string Text { get; set; }
public int UserId { get; set; }
public Virtual User User { get; set; }
}
and in your DbContext:
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
// configures one-to-many relationship
modelBuilder.Entity<User>().HasMany(x=>x.Posts).WithRequired(x=>x.User)
.HasForeignKey<int>(s => s.UserId);
}

How can I display the number of posts?

I create a forum and I have a problem: how can I add the number of posts for each category? I would like to use the viewmodel
public class Category
{
public int Id { get; set; }
public string Name { get; set; }
public string Description { get; set; }
public virtual List<Post> Posts { get; set; }
}
public IActionResult Index()
{
var model = categoryService.GetAll();
return View(model);
}
You can add a property to your ViewModel that will retrieve the number of Post's for that Category.
It could look something like this:
public class Category
{
public int Id { get; set; }
public string Name { get; set; }
public string Description { get; set; }
public virtual List<Post> Posts { get; set; }
public int PostCount
{
get
{
return Posts != null ? Posts.Count : 0;
}
}
}

How to make changes in Database First code to make it working with Code First

i am having trouble in changing code from database first to code first.I was implementing this blog post http://techbrij.com/facebook-wall-posts-comments-knockout-aspnet-webapi.
First trouble is with database generated from code first.It is creating two User Profile tables in database.First is singular and another is Plurized.
Singular one (UserProfile table)is created while registering(Simple membership used).
My Post class is something like this----
public class Post
{
public Post()
{
this.PostComments = new HashSet<PostComment>();
}
[Key]
public int PostId { get; set; }
public string Message { get; set; }
public int PostedBy { get; set; }
public System.DateTime PostedDate { get; set; }
public int UserId { get; set; }
[ForeignKey("UserId")]
public virtual UserProfile UserProfile { get; set; }
public virtual ICollection<PostComment> PostComments { get; set; }
}
}
and my Post Comment class is something like this----
public class PostComment
{
[Key]
public int CommentId { get; set; }
public int PostId { get; set; }
public string Message { get; set; }
public int CommentedBy { get; set; }
public System.DateTime CommentedDate { get; set; }
public virtual Post Post { get; set; }
public virtual UserProfile UserProfile { get; set; }
}
and my UserProfile class is like this----
public class UserProfile
{
public UserProfile()
{
this.PostComments = new HashSet<PostComment>();
this.Posts = new HashSet<Post>();
}
[Key]
public int UserId { get; set; }
public string UserName { get; set; }
public string AvatarExt { get; set; }
public virtual ICollection<PostComment> PostComments { get; set; }
public virtual ICollection<Post> Posts { get; set; }
}
and My DbContext is something like this---
public class WallEntitiesDbContext : DbContext
{
public WallEntitiesDbContext():base("WallEntitiesDbContext")
{
}
public DbSet<PostComment> PostComments { get; set; }
public DbSet<Post> Posts { get; set; }
public DbSet<UserProfile> UserProfiles { get; set; }
public override int SaveChanges()
{
return base.SaveChanges();
}
}
and my connection string are like this----
<add name="DefaultConnection" connectionString="Data Source=.;Initial Catalog=DBWallPostByTechBrij;Integrated Security=SSPI;" providerName="System.Data.SqlClient" />
what changes should i made so that only one UserProfile table is created in database and WallEntitiesDbContext should be able to retreive information from that table in database.
Right now, If i remove------
DbSet<UserProfile> UserProfiles {get; set;}
then i start getting error in my wallpostController. If anyone could help me in getting this working with code First then it would be great help.One more thing,I have manually entered some sample data into database so, after login it should show posts and comments from database but it's not showing and if i tried to post something, it throws exception System.Data.Infrastructure.DbUpdateException at this line----
public override int SaveChanges()
{
return base.SaveChanges();
}
in WallEntitiesDbContext class.
Plzzz anyone have a look it.What changes should i make to make it working.
I think there is no need for two dbContext as u are implementing from the article.
So, use only one dbcontext like this----
public class UsersContext : DbContext
{
public UsersContext()
: base("DefaultConnection")
{
}
public DbSet<UserProfile> UserProfile { get; set; }
public DbSet<Post> Posts { get; set; }
public DbSet<PostComment> PostComments { get; set; }
In DbFisrt approach as in article, u may use it but in code first approach, definitly two dbcontext will create two userProfile table.
Nothing more is needed to be changed.

fluent NHibernate many to many creates additional table

I have two tables "recall" and "service" and I need many to many between them.
I use fluent NHibernate mapping but it creates additional table with name "servicetorecall"
public class Recall : BaseDomain
{
public virtual string Name { get; set; }
public virtual string PersonPosition { get; set; }
public virtual string RecallText { get; set; }
private ICollection<Service> _services = new List<Service>();
public virtual ICollection<Service> Services
{
get { return _services; }
set { _services = value; }
}
}
public class Service : BaseDomain
{
public virtual string Name { get; set; }
public virtual string Url { get; set; }
public virtual string ImgPath { get; set; }
public virtual string ShortContent { get; set; }
public virtual string Content { get; set; }
public virtual bool ServiceIsVisible { get; set; }
ICollection<Recall> _recalls = new List<Recall>();
public virtual ICollection<Recall> Recalls
{
get { return _recalls; }
set { _recalls = value; }
}
}
Mappings :
class RecallMappingOverride : IAutoMappingOverride<Recall>
{
public void Override(AutoMapping<Recall> mapping)
{
mapping.Cache.ReadWrite();
mapping.HasManyToMany(q => q.Services).Table(MappingNames.RECALLS_RELATIONS)
.ParentKeyColumn(MappingNames.RECALL_ID)
.ChildKeyColumn(MappingNames.SERVICE_ID).Inverse().Cascade.All();
}
}
public class ServiceMappingOverride : IAutoMappingOverride<Service>
{
public void Override(AutoMapping<Service> mapping)
{
mapping.Cache.ReadWrite();
mapping.HasManyToMany(q => q.Recalls).Table(MappingNames.RECALLS_RELATIONS) .ParentKeyColumn(MappingNames.SERVICE_ID).ChildKeyColumn(MappingNames.RECALL_ID)
.Inverse().Cascade.All();
}
}
I tried to change cascades but this didn't help. Also I did the same with other entities and it works correctly what type of magic is it?
How do you define "correct", what do you want to achieve?
I never heard of any clean solution for many to many relations which doesn't use pivot table.
[quick glimpse at your mappings]: only one of the "ManyToMany" should be Inverse

zero to many relationship mapping to model

I'm having a bit of an issue mapping this out to my model. I have a Question model that represents (obviously) a question, and a QuestionType that represents the types of questions possible (text, multiple choice, list, multi-line text, and so on...).
The issue i'm having right now is trying to set the options associated with each of QuestionType Model back to the Question Model. So for example, if the QuestionType was a list type, and the list contained three elements, i'm trying to join those elements back on the Question model. The problem i'm having is that not all Questions need to have the QuestionOptions variable set. For example, for just a simple text question (not shown in code).
Any suggestions on how to achieve this?
Question Model
[Table("Questions")]
public class Question {
[Key]
[DatabaseGeneratedAttribute(DatabaseGeneratedOption.Identity)]
public int QuestionId { get; set; }
[Required]
public String Question { get; set; }
public int QuestionTypeId { get; set; }
[ForeignKey("QuestionTypeId")]
public virtual QuestionType QuestionType { get; set; }
public virtual ICollection<QuestionOptions> QuestionOptions { get; set; }
}
QuestionType Model
[Table("QuestionTypes")]
public class QuestionType {
[Key]
[DatabaseGeneratedAttribute(DatabaseGeneratedOption.Identity)]
public int QuestionTypeId { get; set; }
[Required]
public String QuestionType { get; set; }
}
QuestionOptions Model
public abstract class QuestionOptions {
[Key]
[DatabaseGeneratedAttribute(DatabaseGeneratedOption.Identity)]
public int OptionId { get; set; }
public int? QuestionId { get; set; }
[ForeignKey("QuestionId")]
public virtual Question Question { get; set; }
}
[Table("questionType_List")]
public class ListQuestion : QuestionOptions {
[Required]
public String Item { get; set; }
}
QuestionContext
public class QuestionContext : DbContext {
public QuestionContext() : base("DefaultConnection") { }
protected override void OnModelCreating(DbModelBuilder modelBuilder) {
Database.SetInitializer<QuestionContext>(null);
public DbSet<Question> Questions { get; set; }
public DbSet<QuestionType> QuestionTypes { get; set; }
public DbSet<ListQuestion> ListQuestions { get; set; }
}
Personally I would have QuestionOptions for all questions, even if that was a blank entry in the table or perhaps some sort of identifier that allows you to know if its multi-line or single line text.