NHibernate - Error dehydrating property value - updating entity - nhibernate

I'm having problem when saving an entity with association. Below is my code which gives the error
Fluent Class inherited from Fluent Migration
public override void Up() //Update your changes to the database
{
Create.Table("assinatura")
.WithColumn("id").AsInt32().Identity().PrimaryKey()
.WithColumn("usuario_id").AsInt32()
.WithColumn("isfreeplan").AsInt32() //1 sim 0 nao
.WithColumn("gratuito_datafinal").AsDateTime()
Create.Table("usuarios")
.WithColumn("id").AsInt32().Identity().PrimaryKey()
.WithColumn("nomecompleto").AsString(256) //Patricia dos Santos
.WithColumn("email").AsString(512) //patricia#gmail.com
.WithColumn("password").AsString(128) //123123123
Create.ForeignKey("IX_FK_AssinaturasUsuarios")
.FromTable("assinatura").ForeignColumn("usuario_id")
.ToTable("usuarios").PrimaryColumn("id");
}
Mapping of Table "Usuario"
public class UsuariosMap : ClassMapping<Usuario>
{
public enum Niveis { CADASTRADO = 0, REQUISITOU_PAGAMENTO = 1 }
public virtual int id { get; set; }
public virtual string nomecompleto { get; set; }
public virtual string email { get; set; }
public virtual string password { get; set; }
public UsuariosMap()
{
Table("usuarios");
Id(x => x.id, x => x.Generator(Generators.Identity));
Property(x => x.nomecompleto, x => x.NotNullable(true));
Property(x => x.email, x => x.NotNullable(true));
Property(x => x.password, x => x.NotNullable(true));
Bag(x => x.assinaturas, map => {
map.Table("assinatura");
map.Lazy(CollectionLazy.Lazy);
map.Inverse(true);
map.Key(k => k.Column(col => col.Name("usuario_id")));
map.Cascade(Cascade.All); //set cascade strategy
}, rel => rel.OneToMany());
}
Mapping of Table "Assinatura"
public class Assinatura
{
public virtual int Id { get; set; }
public virtual int usuario_id { get; set; }
public virtual int isfreeplan { get; set; }
public virtual DateTime gratuito_datafinal { get; set; }
public virtual Usuario usuario { get; set; }
}
public class AssinaturaMap : ClassMapping<Assinatura>
{
public AssinaturaMap()
{
Table("assinatura");
Id(x => x.Id, x => x.Generator(Generators.Identity));
Property(x => x.usuario_id, x => x.NotNullable(true));
Property(x => x.isfreeplan, x => x.NotNullable(true));
Property(x => x.gratuito_datafinal, x => x.NotNullable(true));
ManyToOne(x => x.usuario, x=>
{
x.Cascade(Cascade.DeleteOrphans);
x.Column("usuario_id");
x.ForeignKey("IX_FK_AssinaturasUsuarios");
});
}
}
When I try to update a User "Usuario" adding a new "Assinatura" I am getting an error
var user = Database.Session.Load<Usuario>(1);
var ass = new Assinatura
{
isfreeplan = 0,
gratuito_datafinal = DateTime.Now,
usuario = user
};
if (user != null)
{
user.assinaturas.Add(ass);
Database.Session.SaveOrUpdate(user);
}
An exception of type 'NHibernate.PropertyValueException' occurred in NHibernate.dll but was not handled in user code
{"Error dehydrating property value for Test.Models.Assinatura.usuario"}
Inner Exc: {"Parameter index is out of range."}
Property Name: usuario
I just want to do a basic one-to-many relationship between Usuario table and Assinature table (1 user has one or many assinaturas).

The exception:
Parameter index is out of range
is telling us, that we are working with one DB column twice. And that is because of this doubled mapping:
Property(x => x.usuario_id, x => x.NotNullable(true));
ManyToOne(x => x.usuario, x=>
{
x.Cascade(Cascade.DeleteOrphans);
x.Column("usuario_id");
...
It is ok to use one column for more properties (one value type, one reference).. but only for reading (loading values from DB)
For insert / update... we can use only one of these. And always is better to keep read/write reference, and property readonly
Property(x => x.usuario_id, x => {
x.NotNullable(true)
x.Insert(false)
x.Update(false)
})

Related

Fluent Nhibernate with SQL joining on to a child collection with a disrimitor value

here are my parent class mappings:
public class Result : BaseEntity<long, Result>
{
public virtual string Stuff { get; set; }
public virtual Iesi.Collections.Generic.ISet<ResultRequestParam> Request { get; set; }
public virtual Iesi.Collections.Generic.ISet<ResultResponseParam> Response { get; set; }
public Result()
{
Request = new HashedSet<Request>();
Response = new HashedSet<Response>();
}
}
public class ResultMap : ClassMapping<Result>
{
public ResultMap()
{
Id(x => x.Id, map => map.Generator(Generators.Identity));
Property(x => x.Stuff, map => { map.NotNullable(false); map.Length(256); });
Set(x => x.Request, map =>
{
map.Key(x => x.Column("ResultId"));
map.Cascade(Cascade.Persist | Cascade.Remove | Cascade.DeleteOrphans);
map.Inverse(true);
},
m => m.OneToMany());
Set(x => x.Response, map =>
{
map.Key(x => x.Column("ResultId"));
map.Cascade(Cascade.Persist | Cascade.Remove | Cascade.DeleteOrphans);
map.Inverse(true);
},
m => m.OneToMany());
}
}
here is my child class mapping:
public enum ResultParamType
{
Request = 0,
Response = 1
}
public abstract class ResultParam : BaseEntity<long, ResultParam>
{
public virtual string ParamKey { get; set; }
public virtual string ParamValue { get; set; }
public virtual Result Result { get; set; }
}
public class ResultParamMap : ClassMapping<ResultParam>
{
public ResultParamMap()
{
Table("ResultParam");
Id(x => x.Id, map => map.Generator(Generators.Identity));
Discriminator(x =>
{
x.Column("ParmType");
x.Type(NHibernateUtil.String);
x.Length(20);
x.Force(true);
x.NotNullable(true);
});
Property(x => x.ParamKey, map =>
{
map.Length(1000);
map.NotNullable(true);
});
Property(x => x.ParamValue, map =>
{
map.Length(3000);
map.NotNullable(false);
});
ManyToOne(x => x.Result, map =>
{
map.Column("ResultId");
map.NotNullable(true);
});
}
}
public class ResultRequestParam : ResultParam { }
public class ResultRequestParamMap : SubclassMapping<ResultRequestParam>
{
public ResultRequestParamMap()
{
DiscriminatorValue(ResultParamType.Request.ToString());
}
}
public class ResultResponseParam : ResultResponseParam { }
public class ResultResponseParamMap : SubclassMapping<ResultResponseParam>
{
public ResultResponseParamMap()
{
DiscriminatorValue(ResultParamType.Response.ToString());
}
}
Now In my provider I want to start my query with Result and then add restrictions for the child set. Similar to this query:
SELECT
*
FROM
[Result] a
JOIN [ResultParam] b on b.ResultId = a.Id
WHERE
b.ParamKey = 'hello' or b.ParamValue = 'hello'
But there's really no clear way to do this from what I've found. It seems everyone just starts with the child and works up to the parent. This wont work for me as I have two virtually mapped child entities via a discriminator value...
Welp since no one was of any help on this site I banged my head against the wall 45 times and just did a subquery. Example:
var subquery = QueryOver.Of<ResultRequestParam >()
.JoinAlias(x => x.Result, () => aR)
.Where(() => <filtering for child table>)
.Select(Projections.Distinct(Projections.Property(() => aR.Id)));
q = q.WithSubquery.WhereProperty(() => aR.Id).In(subquery);
I feel like this could be better but it works. Maybe this will help someone else who is stuck in Nhibernate hell.

NHibernate - Invalid index for this SqlParameterCollection with Count (Mapping By Code)

I am having a real problem with NHibernate Mapping By Code and a Composite Key in one of my classes.
The Domain class is as follows:-
public partial class UserRole {
public virtual int UserId { get; set; }
public virtual int RoleId { get; set; }
//public virtual UserRoleId Id { get; set; }
public virtual User User { get; set; }
public virtual Role Role { get; set; }
[NotNullNotEmpty]
public virtual DateTime VqsCreateDate { get; set; }
public virtual DateTime? VqsUpdateDate { get; set; }
[NotNullNotEmpty]
public virtual bool VqsMarkedForDelete { get; set; }
[Length(50)]
public virtual string VqsCreateUserName { get; set; }
[Length(50)]
public virtual string VqsLastUpdatedUserName { get; set; }
[NotNullNotEmpty]
[Length(128)]
public virtual string VqsCreateSessionId { get; set; }
[Length(128)]
public virtual string VqsLastUpdatedSessionId { get; set; }
[NotNullNotEmpty]
[Length(15)]
public virtual string VqsCreateIpAddress { get; set; }
[Length(15)]
public virtual string VqsLastUpdatedIpAddress { get; set; }
[Length(255)]
public virtual string VqsCreateSessionInfo { get; set; }
[Length(255)]
public virtual string VqsLastUpdatedSessionInfo { get; set; }
[NotNullNotEmpty]
public virtual bool VqsAllowDelete { get; set; }
public virtual bool? VqsAllowEdit { get; set; }
[Length(50)]
public virtual string VqsRffu1 { get; set; }
[Length(50)]
public virtual string VqsRffu2 { get; set; }
[Length(50)]
public virtual string VqsRffu3 { get; set; }
#region NHibernate Composite Key Requirements
public override bool Equals(object obj) {
if (obj == null) return false;
var t = obj as UserRole;
if (t == null) return false;
if (UserId == t.UserId
&& RoleId == t.RoleId)
return true;
return false;
}
public override int GetHashCode() {
int hash = GetType().GetHashCode();
hash = (hash * 397) ^ UserId.GetHashCode();
hash = (hash * 397) ^ RoleId.GetHashCode();
return hash;
}
#endregion
}
And the mapping class is as follows:-
public partial class UserRoleMap : ClassMapping<UserRole> {
public UserRoleMap() {
Schema("Membership");
Lazy(true);
ComposedId(compId =>
{
compId.Property(x => x.UserId, m => m.Column("UserId"));
compId.Property(x => x.RoleId, m => m.Column("RoleId"));
});
Property(x => x.VqsCreateDate, map => map.NotNullable(true));
Property(x => x.VqsUpdateDate);
Property(x => x.VqsMarkedForDelete, map => map.NotNullable(true));
Property(x => x.VqsCreateUserName, map => map.Length(50));
Property(x => x.VqsLastUpdatedUserName, map => map.Length(50));
Property(x => x.VqsCreateSessionId, map => { map.NotNullable(true); map.Length(128); });
Property(x => x.VqsLastUpdatedSessionId, map => map.Length(128));
Property(x => x.VqsCreateIpAddress, map => { map.NotNullable(true); map.Length(15); });
Property(x => x.VqsLastUpdatedIpAddress, map => map.Length(15));
Property(x => x.VqsCreateSessionInfo, map => map.Length(255));
Property(x => x.VqsLastUpdatedSessionInfo, map => map.Length(255));
Property(x => x.VqsAllowDelete, map => map.NotNullable(true));
Property(x => x.VqsAllowEdit);
Property(x => x.VqsRffu1, map => map.Length(50));
Property(x => x.VqsRffu2, map => map.Length(50));
Property(x => x.VqsRffu3, map => map.Length(50));
ManyToOne(x => x.User, map =>
{
map.Column("UserId");
////map.PropertyRef("Id");
map.Cascade(Cascade.None);
});
ManyToOne(x => x.Role, map =>
{
map.Column("RoleId");
////map.PropertyRef("Id");
map.Cascade(Cascade.None);
});
}
}
Whenever I try to insert into this table I get the following error:-
Invalid index for this SqlParameterCollection with Count 'N'
I have looked at all the occurrences of this error on StackExchange and across the majority of the internet for the last day and am still no further on.
There seem to be a lot of examples for Xml and Fluent mapping, but very little for Mapping By Code.
I think I have tried every combination of suggestions, but all to no avail.
I am fully aware that the problem is related to the fact that I have the ID fields in the table referenced twice and this is ultimately causing the error,
The problem is that I need both the ID and Entity Fields in my class as they are used extensively throughout the code.
I seem to be experiencing the issue as I have the combination of a composite and foreign keys in my many to many table.
Any help that anyone could provide to preserve my sanity would be very much appreciated.
Many thanks in advance.
Simon
You can set Insert(false) and Update(false) to prevent Nhibernate from generating an update or insert statement which includes the User and Role columns (which do not exist).
ManyToOne(x => x.User, map =>
{
map.Column("UserId");
////map.PropertyRef("Id");
map.Cascade(Cascade.None);
map.Insert(false);
map.Update(false);
});
ManyToOne(x => x.Role, map =>
{
map.Column("RoleId");
////map.PropertyRef("Id");
map.Cascade(Cascade.None);
map.Insert(false);
map.Update(false);
});
That's the same as Not.Update() Not.Insert() in Fluent mapping.
If you now create a valid UserRole object and set bot IDs to valid user/role ID NHibernate should be able to persist your object.

nHibernate map a filtered bag to a single property

I need to map an etity to a mater tabele of a DB.
This Entity has OneToMany with another.
I need to map a Collection othe the master entity to all rows of the Child table.
But I also need to map a Property with a single row getted from the child table and filtered by a criteria that return always only one row.
Someting like a Component but in a filtered child table.
This is My Mapping:
public class Test
{
public virtual string Id { get; set; }
public virtual string Description { get; set; }
public virtual IList<TestItem> Items { get; set; }
public virtual TestItem Item { get; set; }
public Test()
{
Items = new List<TestItem>();
}
}
public class TestItem
{
public virtual string Id { get; set; }
public virtual Test Test { get; set; }
public virtual string ItemCode { get; set; }
public virtual string ItemData { get; set; }
}
public class TestMap : ClassMapping<Test>
{
public TestMap()
{
Id(x => x.Id, m => m.Column("IDTest"));
//IPOTETICAL
SomeComponent(x => x.Item, c => // How to map a filtered collection to a single property??
{
c.Key(k =>
{
k.NotNullable(true);
k.Column("IDTest");
});
**// This Is the filter**
c.Filter("itemsFilter", f => f.Condition("ItemCode = :itemsCodeValue"));
}, r => r.OneToMany(m =>
{
m.NotFound(NotFoundMode.Exception);
m.Class(typeof(TestItem));
}));
Bag(x => x.Items, c => // All Child Rows
{
c.Key(k =>
{
k.NotNullable(true);
k.Column("IDTest");
});
c.Cascade(Cascade.All | Cascade.DeleteOrphans);
c.Lazy(CollectionLazy.NoLazy);
c.Inverse(true);
}, r => r.OneToMany(m =>
{
m.NotFound(NotFoundMode.Exception);
m.Class(typeof(TestItem));
}));
}
}
public class TestItemMap : ClassMapping<TestItem>
{
public TestItemMap()
{
Id(x => x.Id, m => m.Column("IDTestItem"));
ManyToOne(x => x.Test, m =>
{
m.Column("IDTest");
m.NotNullable(false);
m.Lazy(LazyRelation.NoLazy);
});
Property(x => x.ItemCode);
Property(x => x.ItemData);
}
}
I found something about DynamicComponent but I do not know if it is for me...
http://notherdev.blogspot.it/2012/01/mapping-by-code-dynamic-component.html
Thank You!!

Hierarchical entity parent key in NHibernate ByCode

Using NHibernate 3.2 ByCode configuration, I am attempting to map the following hierarchical entity:
public class BusinessType
{
public virtual Guid BusinessTypeId { get; set; }
public virtual Guid? ParentBusinessTypeId { get; set; }
public virtual String BusinessTypeName { get; set; }
public virtual ICollection<BusinessType> Children { get; set; }
}
with this ClassMapping:
public class BusinessTypeMapper : ClassMapping<BusinessType>
{
public BusinessTypeMapper()
{
Id(x => x.BusinessTypeId, x => x.Type(new GuidType()));
Property(x => x.ParentBusinessTypeId, x => x.Type(new GuidType()));
Property(x => x.BusinessTypeName);
Set(x => x.Children,
cm =>
{
// This works, but there is an ugly string in here
cm.Key(y => y.Column("ParentBusinessTypeId"));
cm.Inverse(true);
cm.OrderBy(bt => bt.BusinessTypeName);
cm.Lazy(CollectionLazy.NoLazy);
},
m => m.OneToMany());
}
}
This works fine, but I'd rather be able to specify the key of the relation using a lambda so that refactoring works. This seems to be available, as follows:
public class BusinessTypeMapper : ClassMapping<BusinessType>
{
public BusinessTypeMapper()
{
Id(x => x.BusinessTypeId, x => x.Type(new GuidType()));
Property(x => x.ParentBusinessTypeId, x => x.Type(new GuidType()));
Property(x => x.BusinessTypeName);
Set(x => x.Children,
cm =>
{
// This compiles and runs, but generates some other column
cm.Key(y => y.PropertyRef(bt => bt.ParentBusinessTypeId));
cm.Inverse(true);
cm.OrderBy(bt => bt.BusinessTypeName);
cm.Lazy(CollectionLazy.NoLazy);
},
m => m.OneToMany());
}
}
The problem is that this causes NHibernate to generate a column called businesstype_key, ignoring the already-configured ParentBusinessTypeId. Is there any way to make NHibernate use a lambda to specify the relation, rather than a string?
I never need to navigate from children to parents, only from parents
to children, so I hadn't thought it necessary
then remove public virtual Guid? ParentBusinessTypeId { get; set; } completly. NH will then only create "businesstype_key" (convention) and no "ParentBusinessTypeId". if you want to change that then you have to specify your prefered columnname with cm.Key(y => y.Column("yourpreferredColumnName"));

Nhibernate Conformist Mapping "Unable to determine type..."

The class:
public class SOPProcess : ISOPProcess
{
public virtual Guid Id { get; set; }
public virtual SOP SOP { get; set; }
public virtual ProcessType Type { get; set; }
public virtual SOPProcessInput Input { get; set; }
public virtual SOPProcessOutput Output { get; set; }
public virtual SOPProcessMeasures Measures { get; set; }
public virtual decimal YieldFactor { get; set; }
public virtual SOPProcess PreviousProcess { get; set; }
public virtual SOPProcess NextProcess { get; set; }
}
The Mapping:
public class SOPProcessMap : ClassMapping<SOPProcess>
{
public SOPProcessMap()
{
Id(s => s.Id, i => i.Generator(Generators.GuidComb));
Property(s => s.YieldFactor);
ManyToOne(s => s.SOP, m =>
{
m.Column("SopId");
m.Cascade(Cascade.All);
});
ManyToOne(s => s.Type, m =>
{
m.Column("ProcessTypeId");
m.Cascade(Cascade.All);
});
ManyToOne(s => s.NextProcess, m =>
{
m.Column("NextProcessId");
m.Cascade(Cascade.All);
});
ManyToOne(s => s.PreviousProcess, m =>
{
m.Column("PreviousProcessId");
m.Cascade(Cascade.All);
});
}
}
The Error:
NHibernate.MappingException: Could not determine type for: MES.ProcessManager.SOP.SOPProcess, MES.ProcessManager, for columns: NHibernate.Mapping.Column(id)
I hope it's something simple, this is my first project using the Conformist mapping, so maybe I'm just overlooking something.
From our discussion on the nhusers mailing list.
I ran across the same problems.
You haven't defined the type of relationship. See the line action => action.OneToMany()); in the mapping below.
public class SportMap : ClassMapping<Sport>
{
public SportMap()
{
Id(x => x.Id, map =>
{
map.Column("Id");
map.Generator(Generators.GuidComb);
});
Property(x => x.Name, map =>
{
map.NotNullable(true);
map.Length(50);
});
Bag(x => x.Positions, map =>
{
map.Key(k => k.Column(col => col.Name("SportId")));
map.Cascade(Cascade.All | Cascade.DeleteOrphans);
},
action => action.OneToMany());
Property(x => x.CreateDate);
Property(x => x.CreateUser);
Property(x => x.LastUpdateDate);
Property(x => x.LastUpdateUser);
}
}
It turned out that the problem was in my Set mappings in other classes. If you don't specify the action for the mapping, it throws this (misleading) error.