2
votes

I'm trying to save a new parent item & new child item to a legacy database

I get my data just fine, when I save it saves both the parent and the child. however the Child's SillyNameParentId is always 0

The table structure I can't change so I have to figure out how to make this work.

Here is the SQL generated

NHibernate: INSERT INTO SillyNameParent(Description, Active) VALUES (@p0, @p1); select SCOPE_IDENTITY();@p0 = 'Test' [Type: String (1073741823)], @p1 = True [Type: Boolean (0)] NHibernate: INSERT INTO SillyChild (SillyNameCategoryId, sillyNameParentid) VALUES (@p0, @p1); select SCOPE_IDENTITY();@p0 = 0 [Type: Int32 (0)], @p1 = 1 [Type: Int32 (0)]

Ultimately I want @p1 to be set as SillyNameParentId/Parentid

Tables:

SillyNameParent
    Column PK-IsIdentity: ParentId
    Column varchar(255): Description
    Column bit: Active

SillyChild //I'm a lookup table 
    Column int IsIdentity: Id
    Column int PK: SillyNameParentId
    Column int PK: SillyNameCategoryID

MODELS:

public class SillyNameParent: Entity
{
    public SillyNameParent()
    {
        Children= new List<SillyChild>();
    }
    public virtual string AreaOfConcernDesc { get; set; }
    public virtual bool Active { get; set; }
    public virtual IList<SillyChild> Children{ get; set; }
}

public class SillyChild: Entity
{
    public virtual int SillyNameParentId { get; set; }
    public virtual int SillyNameCategoryId{ get; set; }
    public virtual SillyNameParent Parent { get; set; }

}

MAPS:

public class SillyNameParentMap : IAutoMappingOverride<SillyNameParent>
{
    public void Override(AutoMapping<SillyNameParent> mapping)
    {
        mapping.Table("SillyNameParent");
        mapping.Id(x => x.Id).Column("ParentId").GeneratedBy.Identity();
        mapping.Map(x => x.Description).Not.Nullable();
        mapping.Map(x => x.Active).Nullable();
        mapping.HasMany(x => x.children)
            .Cascade.All()
            .KeyColumn("SillyNameParentId")
            .Not
            .LazyLoad();
    }
}

public class SillyChildMap: IAutoMappingOverride<SillyChild>
{
    public void Override(AutoMapping<SillyChild> mapping)
    {
        mapping.Table("SillyChild");
        mapping.Id(x => x.Id).Column("Id").GeneratedBy.Identity();
        mapping.Map(x => x.SillyNameParentId).Not.Nullable();
        mapping.Map(x => x.SillyNameCategoryId).Not.Nullable();
        mapping.HasOne(x => x.Parent).ForeignKey("SillyNameParentId");
    }
}
2

2 Answers

3
votes

I would say, you are almost there. Just the mapping parent/child should be like this:

SillyNameParentMap:

public void Override(AutoMapping<SillyNameParent> mapping)
{
    ...
    mapping.HasMany(x => x.Children)
        .Cascade.All()
        .KeyColumn("SillyNameParentId")
        .Inverse() // this is the way how to manage insertions
        .Not
        .LazyLoad();   

SillyChildMap:

public void Override(AutoMapping<SillyChild> mapping)
{
    ...
    mapping.References(x => x.Parent)
       .Column("SillyNameParentId");

This mapping would end up in correct insertion, just do it like this:

var parent = new SillyNameParent();
parent... // set properties

var child = new SillyChild();
child... // set properties

parent.Children.Add(child); // parent knows about child
child.Parent = parent; // always do set the relation both ways

sillyService.SaveSillyParent(snp)

EDIT: the select 2 issue

What the exception says, is the doubled column mapping. It is an issue in case of INSERT or UPDATE. But with NHibernate we can solve it easily:

mapping.Map(x => x.SillyNameParentId)
    .Not.Nullable()
    .Not.Insert()
    .Not.Update();
mapping.References(x => x.Parent)
    .Column("SillyNameParentId");

So, now we have both properties mapped (the reference Parent and its int representation). Only the reference will be used for INSERT, UPDATE. But both could be used for Select, Filter/Where and Order by...

1
votes

Here is a work around... Because the identity is being set by the DB I'm guessing I need to save the parent before saving the children. There has got to be a better way however here is what I came up with

 public T SaveReturnEntity(T entity)
    {
        try
        {
            this.Session.Save(entity);
        }
        catch
        {
            if (this.Session.IsOpen)
            {
                this.Session.Close();
            }

            throw;
        }

        this.Session.Flush();
        return entity;
    }



 return _sillyParent.SaveReturnEntity(sillyNameParent) != null;


 [Test]
    public void Save_sillyParent()
    {
        var sillyService = ServiceMiniMart.CreateSillyParent();

        var filter = new sillyQueryFilter();
        {
            CategoryId = 1
        };      
        var snp= new SillyNameParent
            {
                Active = true,
                Description= "Test"
            };
        if (sillyService.SaveSillyParent(snp))
        {
           snp.Category = new List<SillyChild>
               {
                   new SillyChild
                       {
                           SillyNameParentId= snp.Id,
                           SillyNameCategoryId= filter.CategoryId.Value,
                           Parent = snp
                       }
               };
        }


       var a =  sillyService.SaveSillyParent(snp);
    }