Creating Edmx files and testing
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:
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)