I have the following classes:
public class PhoneModel
{
public virtual ModelIdentifier SupportModels
}
public class ModelIdentifier
{
public virtual string Name
public virtual IList<string> Values
}
This is how i mapped it:
mapping.Component(x => x.SuppoertedModel, y =>
{
y.Map(x => x.Name, "FAMILY_ID");
y.HasMany(x => x.Values).Element("VALUE").Table("SUPPORTEDMODULS")
}
2 tables were created:
PhoneModel
column "FAMILY_ID"
SUPPORTEDMODELS
column "VALUE", "PHONE_MODEL_ID"
The problem is that when I am adding values, it will not save it to the SUPPORTEDMODELS table:
var pm = new PhoneModel();
pm.SupportedModels.Name = "11"
pm.SupportedModels.Values.Add("34");
you are missing HasMany(x => x.Values).Cascade.AllDeleteOrphan()
maybe session.Flush() or transaction.Commit() is missing in the test code
btw: if the order of the Values is not important then change it to ICollection<string> and HasMany().AsSet() since this allows NH to optimise some things and code can not rely on the order which is not relyable when using sql.
Related
To start, yes, there are tons of similar questions here on Stack Overflow, but I've browsed them all and the problems that plague most of them look correct on my issue.
Basically, I'm trying to access an Object's Object via my query, and that could be the problem: is that not allowed? I can access the Id field, always, but any other member variable cannot be accessed.
Here are my Objects:
public class Logfiles
{
public virtual int Id { get; set; }
public virtual bool IsActive { get; set; }
public virtual DateTime LastReference { get; set; }
}
public class LogMerges
{
public virtual int Id { get; set; }
public virtual DateTime CreationDate { get; set; }
public virtual Logfiles Logfile { get; set; }
}
And here are my mappings:
class LogfilesMap : ClassMap<Logfiles>
{
public LogfilesMap()
{
Not.LazyLoad();
Table("logfiles");
Id(x => x.Id, "id").Not.Nullable().Unique().GeneratedBy.Identity().UnsavedValue(0);
Map(x => x.IsActive, "is_active").Not.Nullable().Default("true");
Map(x => x.LastReference, "last_reference").Not.Nullable();
}
}
class LogMergesMap : ClassMap<LogMerges>
{
public LogMergesMap()
{
Not.LazyLoad();
Table("logmerges");
Id(x => x.Id, "id").Not.Nullable().Unique().GeneratedBy.Identity().UnsavedValue(0);
Map(x => x.CreationDate, "creation_date").Not.Nullable();
References(x => x.Logfile, "logfile_id");
}
}
My table and columns have the names:
logfiles - id, is_active, last_reference
logmerges - id, creation_date, logfile_id
My code that I'm using to query:
var query = session.QueryOver<LogMerges>()
.Where(log => log.Logfile.IsActive == true);
IEnumerable<LogMerges> logmerges = query.List().OrderBy(c => c.CreationDate);
Performing the Query causes the error and generates:
NHibernate.QueryException: could not resolve property: Logfile.IsActive of: LogMerges
The only thing I can guess is that I'm not allowed to do the "log.Logfile.IsActive" chain of objects in these queries. It compiles just fine and I can't see why I wouldn't be able to do this. If I change it to:
var query = session.QueryOver<LogMerges>()
.Where(log => log.Logfile.Id == 0);
...the query goes through. However, if I try to access the other member variable:
var query = session.QueryOver<LogMerges>()
.Where(log => log.Logfile.LastReference == DateTime.Now);
...I get a similar "could not resolve property" error message.
So if it turns out that I CANNOT do the Object chain of "log.Logfile.IsActive", what's the proper way to implement it? Do I need to perform a bunch of JOINs to do this?
Thanks in advance for any help!
Yes, you do need do need to join on Logfile. Nhibernate is ultimately going to turn your QueryOver query into SQL, so accessing a property on a referenced table doesn't make sense. In other words, you couldn't write the SQL:
select
*
from
logmerges
where
logmerges.logfile.last_reference = ...
Obviously you'd get a syntax error here--you'd need to join to LogFile:
select
*
from
logmerges
inner join logfiles on logfiles.id = logmerges.logfile_id
where
logfiles.last_reference = ...
And so therefore you can't write QueryOver like that either.
var query = session.QueryOver<LogMerges>()
.JoinQueryOver(lm => lm.LogFile)
.Where(lf => lf.LastReference == DateTime.Now)
.List<LogMerges>();
I need a way to query in Nhibernate for items that have a Dictionary Property containing value.
Assume:
public class Item
{
public virtual IDictionary<int, string> DictionaryProperty {get; set;}
}
and mapping:
public ItemMap()
{
HasMany(x => x.DictionaryProperty)
.Access.ReadOnlyPropertyThroughCamelCaseField(Prefix.Underscore)
.AsMap<string>(
index => index.Column("IDNumber").Type<int>(),
element => element.Column("TextField").Type<string>().Length(666)
)
.Cascade.AllDeleteOrphan()
.Fetch.Join();
}
I want to query all Items that have a dictionary value of "SomeText". The following example in Linq fails:
session.Query<Item>().Where(r => r.DictionaryProperty.Any(g => g.Value == "SomeText"))
with error
cannot dereference scalar collection element: Value
So is there any way to achieve that in NHibernate? Linq is not an exclusive requirement but its preffered. Not that I'm not interested to query over dictionary keys that can be achieved using .ContainsKey . Φορ this is similar but not the same
Handling IDictionary<TValueType, TValueType> would usually bring more issues than advantages. One way, workaround, is to introduce a new object (I will call it MyWrapper) with properties Key and Value (just an example naming).
This way we have to 1) create new object (MyWrapper), 2) adjust the mapping and that's it. No other changes... so the original stuff (mapping, properties) will work, because we would use different (readonly) property for querying
public class MyWrapper
{
public virtual int Key { get; set; }
public virtual string Value { get; set; }
}
The Item now has
public class Item
{
// keep the existing for Insert/Updae
public virtual IDictionary<int, string> DictionaryProperty {get; set;}
// map it
private IList<MyWrapper> _properties = new List<MyWrapper>();
// publish it as readonly
public virtual IEnumerable<MyWrapper> Properties
{
get { return new ReadOnlyCollection<MyWrapper>(_properties); }
}
}
Now we will extend the mapping:
HasMany(x => x.Properties)
.Access.ReadOnlyPropertyThroughCamelCaseField(Prefix.Underscore)
.Component(c =>
{
c.Map(x => x.Key).Column("IDNumber")
c.Map(x => x.Value).Column("TextField")
})
...
;
And the Query, which will work as expected:
session
.Query<Item>()
.Where(r =>
r.Properties.Any(g => g.Value == "SomeText")
)
NOTE: From my experience, this workaround should not be workaround. It is preferred way. NHibernate supports lot of features, but working with Objects brings more profit
I'm new to Hibernate and I'm struggling with problem:
I have 3 tables and I need to present some data from these tables on DataGridView.
Normally - without hibernate, I have to make select and bind columns.
But I don't need all columns from these tables.
I have made sample project, where I select data from one table. And I store data as collection of hibernate poco object. Then I bind it as datasource to DataGridView and it is fine.
How to bind if I need columns from more than one table?
Should I have one class with columns. Or 3 poco classes? Or I totally wrong and I need to make this on mapping level?
If you just need to display data (and not change it), you could define a class for the objects you want to have listed in the DataGridView, e.g.
CombinedClassForGrid
{
public Id { get; set; }
public PropertyA1 { get; set; }
public PropertyB1 { get; set; }
public PropertyB2 { get; set; }
}
With NHibernate you can query the tables like this, assuming that ClassA has a property ClassBRef:
ClassB bAlias = null;
CombinedClassForGrid cForGrid = null;
IList<CombinedClassForGrid> result = session.QueryOver<ClassA>()
.JoinAlias(a => a.ClassBRef, () => bAlias)
.SelectList(list => list
.Select(a => a.Id).WithAlias(() => cForGrid.Id)
.Select(a => a.Property1).WithAlias(() => cForGrid.PropertyA1)
.Select(a => bAlias.Property1).WithAlias(() => cForGrid.PropertyB1)
.Select(a => bAlias.Property2).WithAlias(() => cForGrid.PropertyB2))
.TransformUsing(Transformers.AliasToBean<CombinedClassForGrid>())
.List<CombinedClassForGrid>();
Then you should be able to bind result to your DataGridView.
I am using SQL CE as a database for running local and CI integration tests (normally our site runs on normal SQL server). We are using Fluent Nhibernate for our mapping and having it create our schema from our Mapclasses. There are only two classes with a one to many relationship between them. In our real database we use a non dbo schema. The code would not work with this real database at first until i added schema names to the Table() methods. However doing this broke the unit tests with the error...
System.Data.SqlServerCe.SqlCeException : There was an error parsing the query. [ Token line number = 1,Token line offset = 26,Token in error = User ]
These are the classes and associatad MapClasses (simplified of course)
public class AffiliateApplicationRecord
{
public virtual int Id { get; private set; }
public virtual string CompanyName { get; set; }
public virtual UserRecord KeyContact { get; private set; }
public AffiliateApplicationRecord()
{
DateReceived = DateTime.Now;
}
public virtual void AddKeyContact(UserRecord keyContactUser)
{
keyContactUser.Affilates.Add(this);
KeyContact = keyContactUser;
}
}
public class AffiliateApplicationRecordMap : ClassMap<AffiliateApplicationRecord>
{
public AffiliateApplicationRecordMap()
{
Schema("myschema");
Table("Partner");
Id(x => x.Id).GeneratedBy.Identity();
Map(x => x.CompanyName, "Name");
References(x => x.KeyContact)
.Cascade.All()
.LazyLoad(Laziness.False)
.Column("UserID");
}
}
public class UserRecord
{
public UserRecord()
{
Affilates = new List<AffiliateApplicationRecord>();
}
public virtual int Id { get; private set; }
public virtual string Forename { get; set; }
public virtual IList<AffiliateApplicationRecord> Affilates { get; set; }
}
public class UserRecordMap : ClassMap<UserRecord>
{
public UserRecordMap()
{
Schema("myschema");
Table("[User]");//Square brackets required as user is a reserved word
Id(x => x.Id).GeneratedBy.Identity();
Map(x => x.Forename);
HasMany(x => x.Affilates);
}
}
And here is the fluent configuraton i am using ....
public static ISessionFactory CreateSessionFactory()
{
return Fluently.Configure()
.Database(
MsSqlCeConfiguration.Standard
.Dialect<MsSqlCe40Dialect>()
.ConnectionString(ConnectionString)
.DefaultSchema("myschema"))
.Mappings(m => m.FluentMappings.AddFromAssembly(typeof(AffiliateApplicationRecord).Assembly))
.ExposeConfiguration(config => new SchemaExport(config).Create(false, true))
.ExposeConfiguration(x => x.SetProperty("connection.release_mode", "on_close")) //This is included to deal with a SQLCE issue http://stackoverflow.com/questions/2361730/assertionfailure-null-identifier-fluentnh-sqlserverce
.BuildSessionFactory();
}
The documentation on this aspect of fluent is pretty weak so any help would be appreciated
As usual, 10 minutes after posting i answer my own questions. The trick was to not declare the schema in the the ClassMaps. Instead i used the DefaultSchema method in the fluent configuration. So my actual 'live' configuration looks like this :
var configuration = Fluently.Configure()
.Database(MsSqlConfiguration.MsSql2008.ConnectionString(c => c.FromConnectionStringWithKey("connectionStringKey"))
.DefaultSchema("myschema"))
.Mappings(m => m.FluentMappings.AddFromAssemblyOf<AffiliateApplicationRecord>());
return configuration;
And my integration tests look like this:
return Fluently.Configure()
.Database(
MsSqlCeConfiguration.Standard
.Dialect<MsSqlCe40Dialect>()
.ConnectionString(ConnectionString))
.Mappings(m => m.FluentMappings.AddFromAssembly(typeof(AffiliateApplicationRecord).Assembly))
.ExposeConfiguration(config => new SchemaExport(config).Create(false, true))
.ExposeConfiguration(x => x.SetProperty("connection.release_mode", "on_close")) //This is included to deal with a SQLCE issue http://stackoverflow.com/questions/2361730/assertionfailure-null-identifier-fluentnh-sqlserverce
.BuildSessionFactory();
Hopefully someone else will get somevalue out of this...
I think this has more to do with SQL Server CE than nhibernate. I am pretty sure that Sql Server CE will not accept schemas at all. its not a supported feature.
See the Create Table Documentation on MSDN
Does anyone know how can we automatically map dynamic components using Fluent Automapping in NHibernate?
I know that we can map normal classes as components, but couldn't figure out how to map dictionaries as dynamic-components using fluent automapping.
Thanks
We've used the following approach successfully (with FluentNH 1.2.0.712):
public class SomeClass
{
public int Id { get; set; }
public IDictionary Properties { get; set; }
}
public class SomeClassMapping : ClassMap<SomeClass>
{
public SomeClassMapping()
{
Id(x => x.Id);
// Maps the MyEnum members to separate int columns.
DynamicComponent(x => x.Properties,
c =>
{
foreach (var name in Enum.GetNames(typeof(MyEnum)))
c.Map<int>(name);
});
}
}
Here we've mapped all members of some Enum to separate columns where all of them are of type int. Right now I'm working on a scenario where we use different types for the dynamic columns which looks like this instead:
// ExtendedProperties contains custom objects with Name and Type members
foreach (var property in ExtendedProperties)
{
var prop = property;
part.Map(prop.Name).CustomType(prop.Type);
}
This also works very well.
What I'm still about to figure out is how to use References instead of Map for referencing other types that have their own mapping...
UPDATE:
The case with References is unfortunately more complicated, please refer to this Google Groups thread. In short:
// This won't work
foreach (var property in ExtendedProperties)
{
var prop = property;
part.Reference(dict => dict[part.Name]);
}
// This works but is not very dynamic
foreach (var property in ExtendedProperties)
{
var prop = property;
part.Reference<PropertyType>(dict => dict["MyProperty"]);
}
That's all for now.
I got struggle with exactly the same problem. With fluent nHibernate we cannot map this but on my own I somehow was able to solve this. My solution is to build lambda expression on the fly and the assign this into object. For instance, lets say that:
Let my copy part of the site that Oliver refer:
DynamicComponent(
x => x.Properties,
part =>
{
// Works
part.Map("Size").CustomType(typeof(string));
// Works
var keySize = "Size";
part.Map(keySize).CustomType(typeof(string));
// Does not work
part.Map(d => d[keySize]).CustomType(typeof(string));
// Works
part.References<Picture>(d => d["Picture"]);
// Does not work
var key = "Picture";
part.References<Picture>(d => d[key]);
});
And we have this problem that we need to hardcode "Picture" in mapping. But somehow after some research I created following solution:
var someExternalColumnNames = GetFromSomewhereDynamicColumns();
'x' is a DynamicComponent callback in fluent Nhibernate e.g. (DynamicColumns): DynamicComponent(a => a.DynamicColumns, x => (...content of method below...))
foreach(var x in someExternalColumnNames)
{
if (x.IsReferenceToPerson == true)
{
var param = Expression.Parameter(typeof(IDictionary), "paramFirst");
var key = Expression.Constant(x.Name);
var me = MemberExpression.Call(param, typeof(IDictionary).GetMethod("get_Item"), new[] { key });
var r = Expression.Lambda<Func<IDictionary, object>>(me, param);
m.References<Person>(r, x.Name);
}
else
{
m.Map(x.Name)
}
}
//
// Some class that we want to reference, just an example of Fluent Nhibernate mapping
public class PersonMap : ClassMap<Person>
{
public PersonMap()
{
Table("Person");
Id(x => x.PersonId, "PersonId");
Map(x => x.Name);
}
}
public class Person
{
public virtual Guid PersonId { get; set; }
public virtual string Name { get; set; }
public Person()
{ }
}
Maybe it would be helpful