I have a class with a boolean property, like this:
public bool HasPermission { get; set; }
but in our database, in this column we have information in varchar, and we have values like:
"yes","y","no","n","0","1"
How can I map this values to a bool property?
Sorry, should have written this as a comment but not enough rep; have you tried query substitutions?
Something like
<property name="query.substitutions">
true 1, false 0, true 'y', false 'n'
</property>
should work
Edit after comment: as keyess has pointed out, for fluent nhibernate, something like this
var props = new Dictionary<string, string>();
props.Add("query.substitutions","true=1;false=0")
props.Add("query.substitutions","true=yes;false=no")
props.Add(...)
var sessionFactory = Fluently.Configure().BuildConfiguration().AddProperties(props);
should work (see this answer)
Also, another approach may be used based on IUserType
Related
I have an entity A with a bool property (lets call it BProp).
In a second entity there is a bag mapping of A elements with a where condition on BProp like this:
<bag name="MyBag" cascade="all-delete-orphan" inverse="false" where="BProp = 1">
<key column="Structure_Id" />
<one-to-many entity-name="A" />
</bag>
The problem is tha BProp = 1 works with SqlServer and Oracle but breaks PostgreSQL which needs a condition like
where="BProp = true"
Is there a clever way to create a single hbm.xml mapping for all the three db I have to suppport?
As the query substition does not seem to be a usable solution in this filter case, you may go for query interceptor (here a quite rough implementation) :
public class BooleanInterceptor : EmptyInterceptor, IInterceptor
{
public string TrueToken { get; set; }
public string FalseToken { get; set; }
NHibernate.SqlCommand.SqlString IInterceptor.OnPrepareStatement(NHibernate.SqlCommand.SqlString sql)
{
return sql.Replace("{TrueToken}", TrueToken).Replace("{FalseToken}", FalseToken);
}
}
Your mapping would then be where="BProp = {TrueToken}"
It would be used like this
var interceptor = new BooleanInterceptor ();
using (var session = sessionFactory.OpenSession(interceptor))
{
interceptor.FalseToken = "0"; // this replacement value should be taken from a config file
interceptor.TrueToken = "1"; // see above
// your code here
session.Close();
}
I bet there are way better solutions, but I hope this will help anyway
-------------------------- previous answer
I use to insert query substitutions in my NHibernate configuration, like this
var cfg = new Configuration();
cfg.Properties.Add("dialect", "NHibernate.Dialect.MsSql2005Dialect");
.
cfg.Properties.Add("query.substitutions", "true 1, false 0");
.
I guess you could have your xml mapping with where="BProp = true" and just insert the cfg property for query.substitions depending if the dialect is MsSql / Oracle or PosgresSQL
Im using the Fluent NHibernate together with the automapping functionality. Now im lookin' for something like a configuration, setting, custom attribute - whatever - to declare an entity property as "ReadOnlyFromDb"
In the MsSql database im using a computed column in one of my tables where a value is calculated depending on some other values of the specific data row. Now I have declared this column in the entity class as
public virtual int STATUS { get; private set; }
On getting the specific data of the table everything works fine. The property STATUS is filled correct with the specific value in the datatable.
The problem occures when i try to SaveUpdate() the specific object.
I always get the exception
A computed column cannot be the target of an INSERT or UPDATE statement
Which is correct for my understanding - and its how it was supposed to be ;)!
Basically im looking for a configuration, setting, custom attribute - whatever - to say
Hey Fluent NHibernate - get the specific property propertyName but do not insert / update the property propertyName
Is there something like that? Or does a workaround exists for this case?
I've searched the fluent nhibernate wiki but have not found a smilliar case.
I hope that someone has already faced and solved this problem!
Here is the code snippet how I create the session (maybe it helps):
public static ISessionFactory GetNHibernateSession()
{
if (nhibernateSession != null)
return nhibernateSession;
if (ConfigurationManager.AppSettings[msSqlConnectionString] == null || ConfigurationManager.AppSettings[msSqlConnectionString] == String.Empty)
throw new NullReferenceException(String.Format("AppSetting '{0}' must not be null!", msSqlConnectionString));
//nhibernateSession = Fluently.Configure().Database(MsSqlConfiguration.MsSql2005.ConnectionString(ConfigurationManager.AppSettings[msSqlConnectionString]))
// .Mappings(m => m.AutoMappings.Add(AutoMap.AssemblyOf<T_ABWEICHUNGEN>().IgnoreBase<BaseClass>)).BuildSessionFactory();
nhibernateSession = Fluently.Configure().Database(MsSqlConfiguration.MsSql2005.ConnectionString(ConfigurationManager.AppSettings[msSqlConnectionString]))
.Mappings(m => m.AutoMappings.Add(AutoMap.Assembly(System.Reflection.Assembly.GetExecutingAssembly())
.Override<T_ABWEICHUNGEN>(map =>map.Map(d => d.A08_STATUS_FILE).Length(2147483647))
.IgnoreBase(typeof(BaseClass))
.IgnoreBase(typeof(IDColumn))
.IgnoreBase(typeof(MsSqlQuery))
.IgnoreBase(typeof(MsSqlParamCollection))
.IgnoreBase(typeof(AbweichungStatus))
)).BuildSessionFactory();
return nhibernateSession;
}
}
thanks so far for the response - helped me so far.
But - is there a way to get this solved more 'dynamicly'?
For example:
I want to declare a custom attribute called [ReadOnlyDbField] now declare all properties of the entity with this cusomt attribute to say: Just read this value and do not update / insert it.
Basically i want to say in the configuration
Map all properties with the custom attribute [ReadOnlyDbField] to Not.Insert().Not.Update()
Is there a way to implement this?
.Override<Entity>(map => map.Map(d => d.STATUS).Not.Insert().Not.Update())
update: account for edited question
public class ReadonlyDbFielConvention : AttributePropertyConvention<ReadOnlyDbField>
{
protected override void Apply(ReadOnlyDbField attribute, IPropertyInstance instance)
{
instance.Not.Insert();
instance.Not.Update();
}
}
I'm pretty sure what sort of things can be done with NHibernate but I wanted to check with the community on this one.
If I have an entity Foo:
public class Foo
{
public int Id { get; set; }
public string Name { get; set; }
public bool HasRegistered { get; set; }
}
I would like the property HasRegistered to be true if there is a corresponding record in the Actions table (i.e. has the Foo's Id as a foreign key and a particular code 'BLAH' in another field), and to be false if there is not.
So, for example, HasRegistered would be true if there is an Action record with the following fields:
FooId (equal to the Foo's Id)
Code (equal to the value 'BLAH')
Is such mapping possible?
From my knowledge of NHibernate, this is not possible with a "transparent" mapping feature, like property or as you can do with bags os sets and so on..
The simplest way to that is using a formula over that property and make that property "un-mutable" so the property does'm involve in insert/update scenarios.
hope this helps.
so this could be an example:
<property
name="HasRegistered"
formula="sql formula to evaluate"
insert="false" update="false"/>
as source of more info about nhibernate mapping features about (property in this case) is the following:
NHibernate mapping: property
Can someone explain this little mystery to me about how NHibernate handles composite elements.
I have classes that look like this;
public class Blog
{
public virtual int Id
{
get;
private set;
}
public virtual ISet<Comment> Comments
{
get;
set;
}
}
public class Comment
{
public virtual string CommentText
{
get;
set;
}
public virtual DateTime Date
{
get;
set;
}
}
and mappings like this;
<class name="Blog" table="blog">
<id name="Id" column="id" unsaved-value="0">
<generator class="hilo"/>
</id>
<set name="Comments" table="blog_comments">
<key column="blog_id" />
<composite-element class="Comment">
<property name="CommentText" column="comment" not-null="true" />
<property name="Date" column="date" not-null="true" />
</composite-element>
</set>
</class>
However when i perform a select like this;
using (ITransaction transaction = session.BeginTransaction())
{
Blog blog = session.CreateCriteria(typeof(Blog))
.SetFetchMode("Comments", FetchMode.Eager)
.Add(Expression.IdEq(2345))
.UniqueResult();
transaction.Commit();
}
NHibernate issues a select with a join to get the blog with posts BUT then deletes all comments and then inserts the comments! Why is it doing this? If i do not use a transaction then it will ONLY perform the select and not the DELETE and INSERT as I would expect. What am I missing? I am using NHibernate 2.0
I think you need to override Equals() and GetHashCode() on Comment. NHibernate doesn't have an ID to go on for entity equality so you have to define what makes a comment entity equal to another comment.
Could be wrong :)
Edit
From nhibernate.info (8.2)
Note: if you define an ISet of composite elements, it is very important to implement Equals() and GetHashCode() correctly.
And an example of implementing Equals / GetHashCode from nhibernate.info (4.3)
public class Cat
{
...
public override bool Equals(object other)
{
if (this == other) return true;
Cat cat = other as Cat;
if (cat == null) return false; // null or not a cat
if (Name != cat.Name) return false;
if (!Birthday.Equals(cat.Birthday)) return false;
return true;
}
public override int GetHashCode()
{
unchecked
{
int result;
result = Name.GetHashCode();
result = 29 * result + Birthday.GetHashCode();
return result;
}
}
}
My question would be why you are committing if you only need to do a select? I believe the reason it's deleting all the comments is that when you call commit on the transaction, the blog object and it's associated comments are cached in the session that is used to create the transaction. When you call the commit, you are causing all the objects in the session to be saved which is causing the save back to the database. I'm not clear on why it's deleting the comments but it is correct behaviour to save the objects.
I also stumbled upon this today:
NHibernate is deleting my entire
collection and recreating it instead
of updating the table.
This generally happens when NHibernate
can't figure out which items changed
in the collection. Common causes are:
replacing a persistent collection entirely with a new collection instance
passing NHibernate a manually constructed object and calling Update on it.
serializing/deserializing a persistent collection apparently also causes this problem.
updating a with inverse="false" - in this case, NHibernate can't construct SQL to update an individual collection item.
Thus, to avoid the problem:
pass the same collection instance that you got from NHibernate back to it (not necessarily in the same session),
try using some other collection instead of ( or ), or
try using inverse="true" attribute for .
Is it possible to do a mapping in NHibernate without using an Id?
What I'm trying to do is, call a stored procedure using
session.CreateSqlQuery( myQuery ).AddEntity( typeof( MyEntity ) );
The procedure is an aggregate and there is no Id field being returned, but I would still like to take advantage of NHibernate mapping the data returned into an entity. The only thing I have come up with so far is having the procedure add a field "O as Id" or something, and have the entity have a fake Id field.
Your entity must be "unique" in some way, even if it doesn't have an Id field.
Decide which properties, when taken together, must be unique, and use a composite key over them...
<composite-id>
<key-property name="Property1" column="Column1"/>
<key-property name="Property2" column="Column2"/>
</composite-id>
I found this really isn't possible. The data returned from the stored procedure has to have an Id also. It worked to just create a dummy column with an Id, and a dummy property on the object, but I just decided to convert the data returned by NHibernate to an object by hand.
Basically, you're not supposed to use stored procedures with NHibernate. It IS an ORM afterall. In my case, I had no choice but to use the procedure.
From #Andy McCluggage's post:
You can very much do this in the mapping:
<composite-id>
<key-property name="Property1" column="Column1"/>
<key-property name="Property2" column="Column2"/>
</composite-id>
But, you must override Equals() and GetHashCode() in your model:
public class MyClass
{
public virtual DateTime Property1;
public virtual string Property2;
public virtual int SomeOtherProperty;
public override bool Equals(object obj)
{
if(obj == null)
return false;
var t = obj as MyClass;
if(t == null)
return false;
if (this.Property1 == t.Property1 && this.Property2 == t.Property2)
return true;
return false;
}
public override int GetHashCode()
{
return (this.Property1 + "|" + this.Property2).GetHashCode();
}
}
I need to double-check the xsd, but I believe either id or composite-id are required. However, per the docs, name isn't required. So, you should be able to specify an almost-blank id section.
In a similar situation, I've set the class to mutable="false" and mapped the id to the row index and set the generator in the id mapping to assigned.