Rushino,
I’ll address your code sample, and go over a few tips, as well as point out some of the flaws for future considerations toward using NHibernate.
First, most persistence engines today use proxy classes to work with your entities. Entity Framework is no exception. While basic operations do not require all properties to be marked virtual, in order to take better advantage of change-tracking, you should do this.
Second, the DbSet class exposes a method called Create<T>() so you should incorporate this into your base repository in order to provide a built-in factory method.
Third, exposing a collection as anything but IEnumerable opens the possibility for arbitrary Add’s and Remove’s. Unfortunately, EF-CTP cannot yet map public properties to private members. It is impossible to hold the collection internally, exposing it as IEnumerable only. NHibernate can. For now, you can help secure this a bit by using ISet<T> and HashSet<T>. This disallows adding the same element more than once.
Fourth, the way in which you are adding tags is a bit detrimental to your system. Consider the two operations: post1.AddTag(“coding”) and post2.AddTag(“coding”). If two posts declare a tag with the same name, the result wil be two records in the database with the same value. This is because you are using a OneToMany relationship. You might want to consider using a ManyToMany relationship so that existing tags can be reused without bloating the table.
Fifth, with the way that EF works, using AddThis() AddThat() methods is little overkill, since we cannot explicitly deny direct Add() and Remove() on the collections. I know this is a good DDD practice, but with EF it’s pointless.
Sixth, drop the use of the [Required] attribute. If you really need to guarentee a value in a given field, make it protected set and add a setter method to the entity. This was you can protect the field exactly as you wish, including throwing custom exceptions.
Your classes and mapping should probably look more like the following. I have tested these and they work. For simplicities sake, I left out the User entity:
Category.cs
public class Category
{
// key property
public virtual long CategoryId { get; protected set; }
// scalar properties
public virtual string Name { get; set; }
}
Post.cs
public class Post
{
// key property
public virtual long PostId { get; protected set; }
// scalar properties
public virtual DateTime PostedOn { get; set; }
public virtual string Subject { get; set; }
public virtual string Body { get; set; }
// navigational properties
public virtual Category Category { get; set; }
public virtual ISet<Tag> Tags { get; set; }
// class constructors
public Post() { Tags = new HashSet<Tag>(); }
}
Tag.cs
public class Tag
{
// key property
public virtual long TagId { get; protected set; }
// scalar properties
public virtual string Name { get; set; }
// navigational properties
public virtual ISet<Post> Posts { get; set; }
// class constructors
public Tag() { Posts = new HashSet<Post>(); }
}
CategoryMap.cs
public class CategoryMap : EntityTypeConfiguration<Category>
{
public CategoryMap()
{
// map key property
HasKey(category => category.CategoryId);
// map scalars properties
Property(category => category.Name)
.HasColumnType("varchar")
.HasMaxLength(256)
.IsRequired();
}
}
PostMap.cs
public class PostMap : EntityTypeConfiguration<Post>
{
public PostMap()
{
// map key property
HasKey(post => post.PostId);
// map scalars properties
Property(post => post.PostedOn)
.IsRequired();
Property(post => post.Subject)
.HasColumnType("varchar")
.HasMaxLength(256)
.IsRequired();
Property(post => post.Body)
.HasColumnType("text")
.IsRequired();
// map navigational properties
HasRequired(post => post.Category);
HasMany(post => post.Tags)
.WithMany(tag => tag.Posts);
}
}
TagMap.cs
public class TagMap : EntityTypeConfiguration<Tag>
{
public TagMap()
{
// map key property
HasKey(tag => tag.TagId);
// map scalars properties
Property(tag => tag.Name)
.HasColumnType("varchar")
.HasMaxLength(256)
.IsRequired();
// map navigational properties
HasMany(tag => tag.Posts)
.WithMany(post => post.Tags);
}
}
DomainContext.cs
public class DomainContext : DbContext
{
public DbSet<Category> Categories { get; set; }
public DbSet<Post> Posts { get; set; }
public DbSet<Tag> Tags { get; set; }
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.Configurations.Add(new CategoryMap());
modelBuilder.Configurations.Add(new PostMap());
modelBuilder.Configurations.Add(new TagMap());
base.OnModelCreating(modelBuilder);
}
}