The favorite ORM in .NET world for me it is edmx files. It gives you the database plain – and have generators for POCO (http://blogs.msdn.com/b/adonet/archive/2010/01/25/walkthrough-poco-template-for-the-entity-framework.aspx) as for EF4.1 DBContext(http://blogs.msdn.com/b/adonet/archive/2011/03/15/ef-4-1-model-amp-database-first-walkthrough.aspx).
I will use this one – because of the new ! But the template is over simplistic -so I improved . I do not want you to bother you with my experience – I want to say that, after playing with Data attributes – I decide that Fluent Configuration suits all. The template is here:
DBContext-SecondVersion.zip
For example, the default template
Also for testing I use the fact the default EF4.1 Model1.Context.tt generates this code:
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
throw new UnintentionalCodeFirstException();
}
( see playing on safe side to NOT drop the existing database?!)
Mine Model1.Context.tt generates this:
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
var prefix = System.Configuration.ConfigurationManager.AppSettings["prefixTable"]??"";
bool Continue=true;
OnBeginModelCreating(modelBuilder,prefix, ref Continue);
if(!Continue)
return;
//construct default
#region smsg_Message
modelBuilder.Entity<smsg_Message>().ToTable(prefix + "smsg_Message");
modelBuilder.Entity<smsg_Message>().HasKey(item => item.IDMessage);
#endregion
#region smsg_Message_Archive
modelBuilder.Entity<smsg_Message_Archive>().ToTable(prefix + "smsg_Message_Archive");
modelBuilder.Entity<smsg_Message_Archive>().HasKey(item => item.IDMessageArchive);
#endregion
#region smsg_MessageThread
modelBuilder.Entity<smsg_MessageThread>().ToTable(prefix + "smsg_MessageThread");
modelBuilder.Entity<smsg_MessageThread>().HasKey(item => item.IDMessageThread);
modelBuilder.Entity<smsg_MessageThread>()
.HasRequired(item => item.IDMessage_IDMessageInitial)
.WithMany(u => u.IDMessage_IDMessageInitial)
.HasForeignKey(x => x.IDMessageInitial)
.WillCascadeOnDelete(false);
modelBuilder.Entity<smsg_MessageThread>()
.HasRequired(item => item.IDMessage_IDMessageReply)
.WithMany(u => u.IDMessage_IDMessageReply)
.HasForeignKey(x => x.IDMessageReply)
.WillCascadeOnDelete(false);
modelBuilder.Entity<smsg_MessageThread>()
.HasRequired(item => item.IDMessageArchive_IDMessageInitial)
.WithMany(u => u.IDMessageArchive_IDMessageInitial)
.HasForeignKey(x => x.IDMessageInitial)
.WillCascadeOnDelete(false);
modelBuilder.Entity<smsg_MessageThread>()
.HasRequired(item => item.IDMessageArchive_IDMessageReply)
.WithMany(u => u.IDMessageArchive_IDMessageReply)
.HasForeignKey(x => x.IDMessageReply)
.WillCascadeOnDelete(false);
#endregion
OnFinishModelCreating(modelBuilder, prefix);
}
More, for giving you a smell of what the templates generates, look at this function for reading messages sent:
public IEnumerable<IUserMessage<IUser<String>,string>> RetrieveMessageSent(DateTime dt)
{
using (smsg_Message_List ml = new smsg_Message_List(ConnectionMessaging))
{
ml.LoadFromDB.AddToCustomPredicate(smsg_Message_FindDB.fexp_FromUser(this.Key));
ml.LoadFromDB.AddToCustomPredicate(smsg_Message_FindDB.fexp_DateInsertedBetweenEqDate(dt, dt));
ml.LoadFromDB.LoadFindCustomPredicate();
return ml;
}
}
The class smsg_Message_List and the AddToCustomPredicate , LoadFindCustomPredicate and the expression fexp_FromUser and fexp_DateInsertedBetweenEqDate are automatically generated by the template.
I do not think that the template is perfect – it just helps me a lot!
For the database tests, I was thinking of NUnit – but was deprecated by xUnit. It has a nice project, named SpecificationExample – and it generates data in BDD style .
For example, this code:
public void CreateUsersAndSendMessage()
{
List<SimpleUser> users = null;
"When create two users".Context(() => users = CreateUsersAndDeleteItFirst());
"it will be friends".Assert(() => FindFriend(users).ShouldNotBeNull());
"and when send message from first user to second user".Do(() => SendMessage(users));
"we will retrieve it searching message from first user ".Assert(() => RetrieveMessageSent( users[0]).ShouldEqual(1));
"we will NOT retrieve it searching message from second user".Assert(() => RetrieveMessageSent(users[1]).ShouldEqual(0));
"we will retrieve searching messages received by second user".Assert(() => RetrieveMessageReceived(users[1]).ShouldEqual(1));
"we will retrieve count of unread messages :1 ".Assert(() => RetrieveMessageUnreadCount(users[1]).ShouldEqual(1));
}
run by this command:
xunit.console.clr4.x86.exe "$(TargetPath)" /html "$(TargetDir)a.html"
in Build events it will generate the following HTML
————————————————————————
✔ When create two users and when send message from first user to second user, it will be friends
Output
Before : testXunit.clsTestUserSendMessage.CreateUsersAndSendMessage
After : testXunit.clsTestUserSendMessage.CreateUsersAndSendMessage
0.060s
✔ When create two users and when send message from first user to second user, we will NOT retrieve it searching message from second user
Output
Before : testXunit.clsTestUserSendMessage.CreateUsersAndSendMessage
After : testXunit.clsTestUserSendMessage.CreateUsersAndSendMessage
0.099s
✔ When create two users and when send message from first user to second user, we will retrieve count of unread messages :1
Output
Before : testXunit.clsTestUserSendMessage.CreateUsersAndSendMessage
After : testXunit.clsTestUserSendMessage.CreateUsersAndSendMessage
0.032s
✔ When create two users and when send message from first user to second user, we will retrieve it searching message from first user
Output
Before : testXunit.clsTestUserSendMessage.CreateUsersAndSendMessage
After : testXunit.clsTestUserSendMessage.CreateUsersAndSendMessage
0.053s
✔ When create two users and when send message from first user to second user, we will retrieve searching messages received by second user
Output
Before : testXunit.clsTestUserSendMessage.CreateUsersAndSendMessage
After : testXunit.clsTestUserSendMessage.CreateUsersAndSendMessage
—————————————–
Nice , isn’t it?
Homework :
1. use the ITLIst.tt template , search for
//TODO: prefix
and make generating same prefix for the table as in the Model1.Context.tt( search prefix)
( Do not forget the change the edmx file name in the .tt file!)
2. Make a .t4 file to share commonalities( such as edmx name)