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