mapping by code in nhibernate - nhibernate

I'm examin some mapping examples which uses mapping by code, and I have one simple question
If I have two properties which are mapped like this
Property(x => x.UserName, m =>
{
m.Length(50);
m.NotNullable(true);
});
Property(x => x.UpperUserName, m =>
{
m.Length(50);
m.NotNullable(true);
m.UniqueKey(“UniqueUpperUserName”);
m.Access(Accessor.Field);
});
What this m.Access(Accessor.Field); means?
And why it's used on these second property UpperUserName and not in the first one?
Thanks.

It means that NHibernate won't use the property itself when reading and writing values, but the underlying field.
// This will be used
var string upperUserName;
public string UpperUserName
{
get { return upperUserName; }
// Maybe this is a read-only property,
// so we must allow NHibernate to update the value somehow
// set { upperUserName = value; }
}
You can read more on available access types in NHibernate documentation. Just scroll down to Access and Naming strategies tables.

Related

I can not update a record with fluent nhibernate 3 and map with generatedby.assigned generator

I have a design with fluent n hibernate, I use nhibernate linq to query the database. The app use a domain driver architecture. The database is MSSQL 2012. I don't have problem to insert, update or delete with any table except the case of one table that use ID without seed identity. then I need to use in the map the following:
public class SySchoolLogoMap : ClassMap<SySchoolLogo>
{
public SySchoolLogoMap()
{
ReadOnly();
Table("SySchoolLogo");
Id(x => x.ID).Column("ImgId").GeneratedBy.Assigned();
Map(x => x.ContentType).Column("ContentType").Nullable();
Map(x => x.ImagenBytes).Column("Image").Not.Nullable().Length(50000);
Map(x => x.ImgLenth).Column("ImgLen").Nullable();
Map(x => x.ImageFile).Column("imgFile").Nullable();
Map(x => x.OfficialUse).Column("OfficialUse").Nullable();
Map(x => x.ImageCode).Column("ImageCode").Nullable();
Map(x => x.Description).Column("Description").Nullable();
}
}
The domain is as following:
public class SySchoolLogo : DomainEntityWithTypedID<int>
{
... abbreviate
}
The base DomainEntityWithTypedID
has only a ID integer that is used as Table primary key.
The update operation is using nhibernate link, I I am sure that the service is called and executed.
The update service is the following:
[HttpPost]
public HttpResponseMessage UpdateLogo([FromBody]SchoolLogoOutputModel logodata)
{
try
{
var logo = repository.Get<SySchoolLogo>(logodata.ID);
if (logo == null)
{
throw new HttpBadRequestResponseException("The Image does not exists or was erased in server.");
}
logo.UpdateLogo(logodata.Description, logodata.ImageCode, logodata.OfficialUse);
repository.SaveAndFlush(logo);
return new HttpResponseMessage(HttpStatusCode.OK);
} //catch ignored to abbreviate.
I had debugged the procedure and I am sure that the operation of Save was executed, also I tried with Update, Merge and all flush variant. The operation return as OK, but the database is not updated.
I am missing something, but I can not find what is, any help?
Well, you are using 5.1.3. class mapping:
<class
...
mutable="true|false" (4)
...
(4) mutable (optional, defaults to true): Specifies that instances of the class are (not) mutable.
which is the above mapping in fluent:
public SySchoolLogoMap()
{
ReadOnly(); // mutable="false"
And that would be the reason why NHiberante correctly does NOT execute any UPDATE...

How to map more than one tier of subclasses to nhibernate entity (with bycode)?

I am trying to setup some mappings and am getting this exception:
Cannot extend unmapped class: CommonEntity
[MappingException: Cannot extend unmapped class: CommonEntity]
NHibernate.Cfg.XmlHbmBinding.ClassBinder.GetSuperclass(String
extendsName) +217
NHibernate.Cfg.XmlHbmBinding.MappingRootBinder.AddEntitiesMappings(HbmMapping
mappingSchema, IDictionary`2 inheritedMetas) +352
NHibernate.Cfg.XmlHbmBinding.MappingRootBinder.Bind(HbmMapping
mappingSchema) +85
NHibernate.Cfg.Configuration.AddDeserializedMapping(HbmMapping
mappingDocument, String documentFileName) +156
I have 3 classes. Entity, CommonEntity and User. Theres no entity or commonentity table, only a User table. User inherits from CommonEntity and CommonEntity inherits from Entity. Entity and CommonEntity are abstract.
I have defined this mapping:
public class Mapping : ConventionModelMapper
{
public Mapping()
{
IsRootEntity((type, declared) =>
{
return typeof(Entity<Guid>) == type.BaseType;
});
IsEntity((x,y) => typeof(Entity<Guid>).IsAssignableFrom(x) && !x.IsAbstract && !x.IsInterface);
Class<Entity<Guid>>(x =>
{
x.Id(c => c.Id, m=>m.Generator(Generators.GuidComb));
x.Version(c=>c.Version, (vm) => { });
});
}
}
Which is used like this:
var types = typeof(Mapping).Assembly.GetExportedTypes().Where(t => typeof(Entity<Guid>).IsAssignableFrom(t));
var mapping = new Mapping().CompileMappingFor(types);
configuration.AddMapping(mapping);
Both User and CommonEntity are in the "types" array. I have tried adding a mapping for CommonEntity too but it made no difference.
Class<CommonEntity>(x =>
{
x.Property(c => c.DateCreated, m => m.Type<UtcDateTimeType>());
x.Property(c => c.DateModified, m => m.Type<UtcDateTimeType>());
});
Also tried calling Subclass instead of Class. If i inherit User directly from Entity everything works fine. Any help?
The problem appears to have been that CommonEntity was meeting the requirement for IsRootEntity. I modified it like so and things seem to be working now.
IsRootEntity((type, declared) =>
{
return !type.IsAbstract &&
new[] {typeof (Entity<Guid>), typeof (CommonEntity)}.Contains(type.BaseType);
});

NHibvernate 3.2 many to many not populating join table

I have two classes, user and role, defined as:
public class User : Entity
{
// other properties ...
public virtual string Username
public virtual ICollection<Role> Roles { get; set; }
}
public class Role : Entity
{
public virtual string Name { get; set; }
public virtual ICollection<User> Users { get; set; }
}
In my mapping code, I have the following:
mapper.Class<User>(map =>
{
map.Bag(x=>x.Roles,
cm=>
{
cm.Table("UserRole");
cm.Cascade(Cascade.All);
cm.Key(k => k.Column("[User]"));
},
em=>
{
em.ManyToMany(mm =>
{
mm.Column("[Role]");
});
});
});
mapper.Class<Role>(map =>
{
map.Bag(x=>x.Users,
cm=>
{
cm.Inverse(true);
cm.Table("UserRole");
cm.Key(k=>k.Column("[Role]"));
},
em =>
{
em.ManyToMany(mm =>
{
mm.Column("[User]");
});
});
});
The mappings generate the expected schema, but the join table is never populated. Adding a new user with a new Role in its collection persists the role and then the user to the appropriate tables, but the join table is left empty. Why?
Edit: I still have not made any progress on this. I'm absolutely sure the mapping is correct, and the correct schema is generated, but the join table simply isn't populated. For test purposes, I'm generating entities using NBuilder like so:
var roles = new Role[]
{
new Role("Admin"),
new Role("Manager"),
new Role("User")
};
var users = Builder<User>.CreateListOfSize(10)
.TheFirst(1)
.Do(x =>
{
x.Roles.Add(roles[0]);
x.Roles.Add(roles[1]);
roles[0].Users.Add(x);
roles[1].Users.Add(x);
})
.All()
.With(x => x.Id = 0)
.And(x => x.Version = 0)
.And(x => x.Username = "test user")
.And(x => x.Password = "test password")
.Do(x =>
{
x.Roles.Add(roles[2]);
roles[2].Users.Add(x);
}
.Build();
foreach (var u in users) session.Save(u);
The User and Role entities are persisted correctly, but the join table remains empty. This means I cannot effective query the roles for a given user later, which nullifies the point.
Make sure you have both classes referencing each other.
I think that code, similar to one below, should work for you:
role.Users.Add(user);
user.Roles.Add(role);
session.Save(user); // NH only saves user and role, so it can get auto-generated identity fields
session.Flush(); // NH now can save into cross-ref table, because it knows required information (Flush is also called inside of Transaction.Commit())
I found a good answer to a question about many-to-many with lot of explanations and quotes from NH documentation. I think it worth to read it.
[EDIT]
In answer to this somewhat similar question there is discussion in which need for explicit transaction to save into cross-table is mentioned.
I also edited code above with adding session.Flush() to reflect my findings.
I ended up downloading the NHibernate source and referencing that directly so I could step through it. It turns out that it had something to do with the fact that my code for generating the test data was not wrapped in an explicit session transaction. Once I added that, it was fine. I'd love to see some kind of explanation on this, as I wasn't able to follow the code very clearly, but I'm at least satisfied that the problem is solved.

CompositeId causes Could not compile the mapping document error

I am trying to use CompositeId to map to a legacy system. The source database has a composite primary key so I can't use the normal this.Id mapping.
Here is my attempt to map it:
public PriorityListPartMap()
{
this.Schema("EngSchedule");
this.Table("vPriorityListPart");
this.CompositeId().KeyProperty(x => x.AssemblyPartNumber).KeyProperty(x => x.PartNumber);
this.Map(x => x.CurrentDueDate);
this.Map(x => x.OrderLine);
this.Map(x => x.OrderNumber);
this.Map(x => x.PartDescription);
this.Map(x => x.ProductCode);
this.Map(x => x.Revision);
}
When I try to create the session factory this mapping causes the error:
Could not compile the mapping document: (XmlDocument)
I tried removing the CompositeId mapping and replaced it with:
this.Id(x => x.AssemblyPartNumber).GeneratedBy.Assigned();
The error goes away with that mapping but I can't really use that since the AssemblyPartNumber is not unique.
Is there a different way to map to a table with a composite primary key?
Thanks,
Matthew MacFarland
What is the inner exception for "Could not compile the mapping document: (XmlDocument)"? My theory is it will be "composite-id class must override Equals(): YOURNAMESPACE.PriorityListPart".
For entities requiring composite-ids, the object itself is used as the key. In order for objects that are 'the same' to be recognized as so, you need to override the Equals and GetHashCode methods.
An example Equals method for your entity would be something like this:
public override bool Equals(object obj)
{
var other = obj as PriorityListPart;
if (ReferenceEquals(null, other)) return false;
if (ReferenceEquals(this, other)) return true;
return this.AssemblyPartNumber == other.AssemblyPartNumber &&
this.PartNumber == other.PartNumber;
}
An example GetHashCode method for your entity would be something like this:
public override int GetHashCode()
{
unchecked
{
int hash = GetType().GetHashCode();
hash = (hash * 31) ^ AssemblyPartNumber.GetHashCode();
hash = (hash * 31) ^ PartNumber.GetHashCode();
return hash;
}
}
This also means that if you want to retrieve an object, you cannot have a single key to do it with. To properly retrieve a specific object with its composite key components, the key you use is actually an instance of the object with the composite key components set to the entity you wish to retrieve.
This is why the Equals() method must be overridden, so that NHibernate has the ability to determine which object you are actually trying to retrieve, based on what you specify in the Equals method.

Mapping Enum as string in NHibernate 3.2 mapping by code

Using NHibernate 3.2 mapping by code (not fluent-nhibernate), I'm trying to map an Enum field to a string column instead of the default int representation. I can't get the right syntax.
For example:
public class Account {
public enum StateType { Pending, Active, Cancelled, Suspended }
...
public virtual StateType State { get; set; }
...
}
In the XML mapping, you can use NHibernate.Type.EnumStringType (see this link), but how do I do it in mapping by code?
NHibernate.Mapping.ByCode.ModelMapper mapper = new NHibernate.Mapping.ByCode.ModelMapper();
mapper.Class<Account>(map => {
map.Id(x => x.Id, attr => {
attr.Column("id");
attr.Generator(NHibernate.Mapping.ByCode.Generators.Identity);
});
// Default 'int' mapping
//map.Property(x => x.State);
// Cannot implicitly convert type 'StateType' to 'NHibernate.Type.EnumStringType'
//map.Property<NHibernate.Type.EnumStringType<Account.StateType>>(x => x.State);
Update:
Using this mapping, I managed to get it to save as a string to the DB, but I now get an exception when loading from the DB to the object model.
map.Property(x => x.State, attr => { attr.Type(NHibernateUtil.String); });
This is the exception I get when trying to load the object:
Invalid Cast (check your mapping for property type mismatches); setter of Model.Account
Got it! The following syntax works:
map.Property(x => x.State, attr => attr.Type<NHibernate.Type.EnumStringType<Account.StateType>>());