It will involve a great deal of code if you will do it manually with SQL, or even from a run-off-the-mill ORM. But with NHibernate, this is a simple undertaking.
With NHibernate, just resetting the object's Id to 0 can make NHibernate able to persist the object (and its collections and sub-collections) to a new row in the database. And parent object's collections and sub-collections are able to reference the cloned parent's assigned key(e.g. SCOPE_IDENTITY). These are done automatically by NHibernate for you.
It's very simple:
using System; using System.Collections.Generic; using System.Linq; using System.Text; using NHibernateDeepCopyDemo.Models; using NHibernateDeepCopyDemo.DbMapping; using NHibernate.Linq; namespace NHibernateDeepCopyDemo { class Program { static void Main(string[] args) { var d = new DemoNh(); var q = d.MakeCopy(1); if (q.QuestionId == 1) throw new Exception("Failed"); if (q.QuestionComments[0].QuestionCommentId == 1) throw new Exception("Failed"); if (q.QuestionComments[1].QuestionCommentId == 2) throw new Exception("Failed"); if (q.Answers[0].AnswerId == 1) throw new Exception("Failed"); if (q.Answers[1].AnswerId == 2) throw new Exception("Failed"); if (q.Answers[0].AnswerComments[0].AnswerCommentId == 1) throw new Exception("Failed"); if (q.Answers[0].AnswerComments[1].AnswerCommentId == 2) throw new Exception("Failed"); if (q.Answers[1].AnswerComments[0].AnswerCommentId == 3) throw new Exception("Failed"); if (q.Answers[1].AnswerComments[1].AnswerCommentId == 4) throw new Exception("Failed"); Console.WriteLine("\nOK!"); Console.ReadLine(); } } public class DemoNh { public Question MakeCopy(int id) { using (var sess = NhMapping.GetSessionFactory().OpenSession()) { var q = sess.Query<Question>().Single(x => x.QuestionId == id); foreach (var item in q.QuestionComments) { sess.Evict(item); item.QuestionCommentId = 0; } foreach (var ansItem in q.Answers) { foreach (var ansCommentItem in ansItem.AnswerComments) { sess.Evict(ansCommentItem); ansCommentItem.AnswerCommentId = 0; } sess.Evict(ansItem); ansItem.AnswerId = 0; } sess.Evict(q); q.QuestionId = 0; var nq = sess.Merge(q); sess.Flush(); return nq; } } } }
These are the models(think of Stackoverflow):
using System; using System.Collections.Generic; using System.Linq; using System.Text; namespace NHibernateDeepCopyDemo.Models { public class Question { public virtual int QuestionId { get; set; } public virtual string QuestionText { get; set; } public virtual Person AskedBy { get; set; } public virtual Person QuestionModifiedBy { get; set; } public virtual IList<QuestionComment> QuestionComments { get; set; } public virtual IList<Answer> Answers { get; set; } } public class QuestionComment { public virtual Question Question { get; set; } public virtual int QuestionCommentId { get; set; } public virtual string QuestionCommentText { get; set; } public virtual Person QuestionCommentBy { get; set; } } public class Answer { public virtual Question Question { get; set; } public virtual int AnswerId { get; set; } public virtual string AnswerText { get; set; } public virtual Person AnsweredBy { get; set; } public virtual Person AnswerModifiedBy { get; set; } public virtual IList<AnswerComment> AnswerComments { get; set; } } public class AnswerComment { public virtual Answer Answer { get; set; } public virtual int AnswerCommentId { get; set; } public virtual string AnswerCommentText { get; set; } public virtual Person AnswerCommentBy { get; set; } } public class Person { public virtual int PersonId { get; set; } public virtual string PersonName { get; set; } } }
Mapping:
using System; using System.Collections.Generic; using System.Linq; using NHibernate; using FluentNHibernate.Cfg; using FluentNHibernate.Cfg.Db; using FluentNHibernate.Automapping; using FluentNHibernate.Conventions.Helpers; using FluentNHibernate.Conventions; using FluentNHibernate.Conventions.Instances; using NHibernateDeepCopyDemo.Models; namespace NHibernateDeepCopyDemo.DbMapping { public static class NhMapping { private static ISessionFactory _isf = null; public static ISessionFactory GetSessionFactory() { if (_isf != null) return _isf; var cfg = new StoreConfiguration(); var sessionFactory = Fluently.Configure() .Database(MsSqlConfiguration.MsSql2008.ShowSql().ConnectionString( "Server=localhost; Database=NhFetch; Trusted_Connection=true;" )) .Mappings(m => m.AutoMappings .Add(AutoMap.AssemblyOf<Person>(cfg) .Conventions.Add<ReferenceConvention>() .Override<Question>(x => x.HasMany(y => y.QuestionComments).KeyColumn("Question_QuestionId").Cascade.AllDeleteOrphan().Inverse()) .Override<Question>(x => x.HasMany(y => y.Answers).KeyColumn("Question_QuestionId").Cascade.AllDeleteOrphan().Inverse()) .Override<Answer>(x => x.HasMany(y => y.AnswerComments).KeyColumn("Answer_AnswerId").Cascade.AllDeleteOrphan().Inverse()) ) ) .BuildSessionFactory(); _isf = sessionFactory; return _isf; } } public class StoreConfiguration : DefaultAutomappingConfiguration { readonly IList<Type> _objectsToMap = new List<Type>() { // whitelisted objects to map typeof(Person), typeof(Question), typeof(QuestionComment), typeof(Answer), typeof(AnswerComment) }; public override bool IsId(FluentNHibernate.Member member) { // return base.IsId(member); return member.Name == member.DeclaringType.Name + "Id"; } public override bool ShouldMap(Type type) { return _objectsToMap.Any(x => x == type); } } public class ReferenceConvention : IReferenceConvention { public void Apply(IManyToOneInstance instance) { instance.Column( instance.Name + "_" + instance.Property.PropertyType.Name + "Id"); } } }
Supporting database:
drop table AnswerComment; drop table Answer; drop table QuestionComment; drop table Question; drop table Person; create table Person ( PersonId int identity(1,1) primary key, PersonName nvarchar(100) not null ); create table Question ( QuestionId int identity(1,1) primary key, QuestionText nvarchar(100) not null, AskedBy_PersonId int not null references Person(PersonId), QuestionModifiedBy_PersonId int null references Person(PersonId) ); create table QuestionComment ( Question_QuestionId int not null references Question(QuestionId), QuestionCommentId int identity(1,1) primary key, QuestionCommentText nvarchar(100) not null, QuestionCommentBy_PersonId int not null references Person(PersonId) ); create table Answer ( Question_QuestionId int not null references Question(QuestionId), AnswerId int identity(1,1) primary key, AnswerText nvarchar(100) not null, AnsweredBy_PersonId int not null references Person(PersonId), AnswerModifiedBy_PersonId int null references Person(PersonId) ); create table AnswerComment ( Answer_AnswerId int not null references Answer(AnswerId), AnswerCommentId int identity(1,1) primary key, AnswerCommentText nvarchar(100) not null, AnswerCommentBy_PersonId int not null references Person(PersonId) ); insert into Person(PersonName) values('John'); declare @john int = SCOPE_IDENTITY(); insert into Person(PersonName) values('Paul'); declare @paul int = SCOPE_IDENTITY(); insert into Person(PersonName) values('George'); declare @george int = SCOPE_IDENTITY(); insert into Person(PersonName) values('Ringo'); declare @ringo int = SCOPE_IDENTITY(); insert into Person(PersonName) values('Brian'); declare @brian int = SCOPE_IDENTITY(); insert into Person(PersonName) values('Ely'); declare @ely int = SCOPE_IDENTITY(); insert into Person(PersonName) values('Raymund'); declare @raymund int = SCOPE_IDENTITY(); insert into Person(PersonName) values('Buddy'); declare @buddy int = SCOPE_IDENTITY(); insert into Person(PersonName) values('Marcus'); declare @marcus int = SCOPE_IDENTITY(); insert into Question(QuestionText,AskedBy_PersonId) values('What''s the answer to life and everything?',@john); declare @question int = SCOPE_IDENTITY(); insert into QuestionComment(Question_QuestionId,QuestionCommentText,QuestionCommentBy_PersonId) values(@question,'what is that?',@paul); insert into QuestionComment(Question_QuestionId,QuestionCommentText,QuestionCommentBy_PersonId) values(@question,'nice question',@george); insert into Answer(Question_QuestionId,AnswerText,AnsweredBy_PersonId) values(@question,'42',@ringo); declare @answer1 int = SCOPE_IDENTITY(); insert into Answer(Question_QuestionId,AnswerText,AnsweredBy_PersonId) values(@question,'9',@brian); declare @answer2 int = SCOPE_IDENTITY(); insert into AnswerComment(Answer_AnswerId,AnswerCommentText,AnswerCommentBy_PersonId) values(@answer1, 'I think so', @ely); insert into AnswerComment(Answer_AnswerId,AnswerCommentText,AnswerCommentBy_PersonId) values(@answer1, 'I''m sure', @raymund); insert into AnswerComment(Answer_AnswerId,AnswerCommentText,AnswerCommentBy_PersonId) values(@answer2, 'Really 9?', @ely); insert into AnswerComment(Answer_AnswerId,AnswerCommentText,AnswerCommentBy_PersonId) values(@answer2, 'Maybe 10?', @raymund); select * from Question; select * from QuestionComment; select * from Answer; select * from AnswerComment;
As far as I know, doing this on other ORMs is very tedious.
Happy Computing! ツ
No comments:
Post a Comment