Is there a way to map a column that may or may not exist in database or to map it dynamically to a column?
You can dynamically change the mappings but I don't think there is a way to map a column that may or may not exist. The below example is how you might do it if you were using fluent nhibernate.
var fluentConfiguration = Fluently.Configure(NHibernate.Cfg.Configuration().Configure())
.Mappings(m =>
m.FluentMappings
.AddFromAssemblyOf<OrderMap>()
.Conventions.AddFromAssemblyOf<PascalCaseColumnNameConvention>())
.ProxyFactoryFactory("NHibernate.Bytecode.DefaultProxyFactoryFactory, NHibernate");
var config = fluentConfiguration.BuildConfiguration();
foreach(PersistentClass persistentClass in config.ClassMappings)
{
//Check to see if there are any missing columns from this mapping.
//If so remove the column from the mapping.
//TODO: Query the database and catch errors related to missing columns
// If a column is missing remove it
}
var sessionFactory = config.BuildSessionFactory();
Related
I'm using by code mappings and trying to map a manytomany. This works fine but I need OrderBy for the child collection items. I noticed this has been omitted (it does exist in the HBM mappings). e.g.
public class Mapping : EntityMapper<Category>
{
public Mapping()
{
Set(x => x.Items, m =>
{
m.Table("ItemCategories");
m.Key(k => k.Column("CategoryId"));
m.Inverse(true);
m.Cascade(Cascade.None);
}, col => col.ManyToMany(m =>
{
m.Columns(x => x.Name("ItemId"));
//m.OrderBy("Score desc"); // missing in Nh4.x?
}));
}
}
Is there a workaround for this? I tried following the suggestion in this article whereby I can set the property before the session factory is built but it has no effect. e.g.
cfg.GetCollectionMapping(typeof(Category).FullName + ".Items").ManyToManyOrdering = "Score desc";
cfg.BuildSessionFactory();
Am I doing something wrong or is OrderBy on manytomany not supported in Nh4?
Also, is it possible to restrict the maximum number of items retrieved in the collection?
Replaced the many to many with one to many and introduced an entity that represents the relationship (followed advice from this article).
This has the upside of allowing you to map the order-by column as well as other columns, and also solved the issue of restricting the number of items in the collection by using the one-to-many's Where() and Filter() clauses.
how can I map a dynamic table name to a entity?
Ex, I can have many tables, all have the same structure, however, has its different name, how can I map this using Fluent NHibernate?
remembering that before compiling I do not know the name of these tables.
Can anyone help me?
What you'll probably need to do is put a place holder in the fluent nhibernate mapping for the table name and then replace it after you build the mappings.
Take a look at the following article and I think the first code sample will give you an idea of what you need to do.
http://ayende.com/blog/3294/dynamic-mapping-with-nhibernate
I was able to do this. Here is a rough example:
var fluentConfiguration = Fluently.Configure(NHibernate.Cfg.Configuration().Configure())
.Mappings(m =>
m.FluentMappings
.AddFromAssemblyOf<OrderMap>()
.Conventions.AddFromAssemblyOf<PascalCaseColumnNameConvention>())
.ProxyFactoryFactory("NHibernate.Bytecode.DefaultProxyFactoryFactory, NHibernate");
var config = fluentConfiguration.BuildConfiguration();
foreach(PersistentClass persistentClass in config.ClassMappings)
{
if(persistentClass.MappedClass == typeof(Order))
{
persistentClass.Table.Name = "Dynamic Table";
}
}
//You could substitute above for each loop with linq unless you have a bunch to replace then use foreach
//config.ClassMappings.First(x => x.MappedClass == typeof(Order)).Table.Name = "Dynamic Table";
var sessionFactory = config.BuildSessionFactory();
using(ISession session = sessionFactory.OpenSession())
{
Order order = session.Query<Order>().FirstOrDefault();
}
i'm trying to create a filter, using fluent nH (1.2) automapping with nH 2.1.2.
I've followed the example here, but I keep getting the exception:
filter-def for filter named 'DateFilter' was never used to filter classes nor collections..
the filter class:
public class DateFilter : FilterDefinition
{
public DateFilter()
{
WithName(Consts.FilterConsts.DATE_FILTER)
.AddParameter("date", NHibernate.NHibernateUtil.DateTime)
.WithCondition("DATEPART(dayofyear,EntityTime) = DATEPART(dayofyear, :date)")
;
}
}
and in the mapping override:
mapping.HasMany(x => x.Stuff)
.LazyLoad()
.ReadOnly()
.ApplyFilter<DateFilter>();
here's my configuration code.
Fluently.Configure()
.Database(MsSqlConfiguration.MsSql2008
.DefaultSchema("dbo") //set default schema to enable full-qualified queries
.AdoNetBatchSize(batchSize > 0 ? batchSize : 1)
.UseReflectionOptimizer()
.ConnectionString(c => c.FromConnectionStringWithKey(connectionStringKey))
.Cache(c => c.UseQueryCache()
.ProviderClass(
isWeb ? typeof(NHibernate.Caches.SysCache2.SysCacheProvider).AssemblyQualifiedName //in web environment- use sysCache2
: typeof(NHibernate.Cache.HashtableCacheProvider).AssemblyQualifiedName //in dev environmet- use stupid cache
))
)
.Mappings(m => m.AutoMappings.Add(
AutoMap.AssemblyOf<Domain.Entity>(cfg) //automapping the domain entities
.IncludeBase<Domain.SomethingBase>() //ensure that although SomethingBase is a base class, map it as well. this enables us to store all Something sub-classes in the same table
.IncludeBase<Domain.OrOtherBase>() //create a table for the abstract 'OrOtherBase' class
.UseOverridesFromAssemblyOf<MappingOverrides.MappingOverride>()
.Conventions.Add(DefaultCascade.All()) //make sure that all saves are cascaded (i.e when we save a zone, its queues are saved as well)
.Conventions.AddFromAssemblyOf<IdGenerationWithHiLoConvention>()
))
.Mappings(m => m.FluentMappings.Add(typeof(DateFilter)));
if I move the line before the automapping part, I get the exception:
NHibernate.MappingException: filter-def for filter named 'DateFilter' was not found.
can anybody tell me what I'm doing wrong?
OK, so I figured this out. When you add the mappings separately like that, they end up in different mappings and it will either complain that you never use the filter or complain that it can't find the filter, because it's not looking both places. The solution is to add it directly to the automap, so in your case like:
//other stuff up here
.Mappings(m => m.AutoMappings.Add(() => {
var a = AutoMap.AssemblyOf<Domain.Entity>(cfg)
.IncludeBase<Domain.SomethingBase>() //and also cascades and conventions and stuff
a.Add(typeof(DateFilter));
return a;
}));
Kinda gross because .Add() isn't fluent, but it does work.
according to this Post it is possible to change the naming convention from "[TableName]_id" to "[TableName]ID". However when I saw the code, I wasn't able to do it with my rather new (about 6 weeks old) version of Fluent NHibernate.
Original code:
var cfg = new Configuration().Configure();
var persistenceModel = new PersistenceModel();
persistenceModel.addMappingsFromAssembly(Assembly.Load("Examinetics.NHibernate.Data"));
persistenceModel.Conventions.GetForeignKeyNameOfParent = type => type.Name + "ID";
persistenceModel.Configure(cfg);
factory = cfg.BuildSessionFactory();
I came up with this:
PersistenceModel model = new PersistenceModel();
model.AddMappingsFromAssembly(assemblyType.Assembly);
model.ConventionFinder.Add(
ConventionBuilder.Reference.Always(part
=> part.ColumnName(part.EntityType.Name + part.Property.Name)));
configuration.ExposeConfiguration(model.Configure);
return configuration.BuildConfiguration();
but this gives me the original Table, because EntityType is not the referenced Entity and I see no property to get the "parent" entity.
How can I do this?
See The answer about removing underscores in Ids using Fluent NHibernate Automapping conventions here.
A portion of the text, the tables and classes are in the link.
Solution:
Add a convention, it can be system wide, or more restricted.
ForeignKey.EndsWith("Id")
Code example:
var cfg = new StoreConfiguration();
var sessionFactory = Fluently.Configure()
.Database(/* database config */)
.Mappings(m =>
m.AutoMappings.Add(
AutoMap.AssemblyOf<Product>(cfg)
.Conventions.Setup(c =>
{
c.Add(ForeignKey.EndsWith("Id"));
}
)
.BuildSessionFactory();
Now it will automap the ShelfId column to the Shelf property in Product.
I just fell in love with NHibernate and the fluent interface. The latter enables very nice mappings with refactoring support (no more need for xml files).
But nobody is perfect, so I am missing the many-to-any mapping in fluent. Does anybody know if it is already there? If so, a simple line of code would be nice.
But to stick to the header of the question, is there any way to combine fluent and normal NHibernate mapping.
Currently I use the following lines for my test setup WITH fluent, and the second code block for my test WITHOUT fluent (with XML mappings). How can I tell fluent to use fluent IF AVAILABLE and XML otherwise...
var cfg = new Configuration();
cfg.AddProperties(MsSqlConfiguration.MsSql2005.ConnectionString.Is(_testConnectionstring).ToProperties());
cfg.AddMappingsFromAssembly(typeof(CatMap).Assembly);
new SchemaExport(cfg).Create(true, true);
var persistenceModel = new PersistenceModel();
persistenceModel.addMappingsFromAssembly(typeof(CatMap).Assembly);
IDictionary<string, string> properties = MsSqlConfiguration.MsSql2005.UseOuterJoin().ShowSql().ConnectionString.Is(_testConnectionstring).ToProperties();
properties.Add("command_timeout", "340");
session = new SessionSource(properties, persistenceModel).CreateSession();
Without Fluent...
config = new Configuration();
IDictionary props = new Hashtable();
props["connection.provider"] = "NHibernate.Connection.DriverConnectionProvider";
props["dialect"] = "NHibernate.Dialect.MsSql2005Dialect";
props["connection.driver_class"] = "NHibernate.Driver.SqlClientDriver";
props["connection.connection_string"] = "Server=localhost;initial catalog=Debug;Integrated Security=SSPI";
props["show_sql"] = "true";
foreach (DictionaryEntry de in props)
{
config.SetProperty(de.Key.ToString(), de.Value.ToString());
}
config.AddAssembly(typeof(CatMap).Assembly);
SchemaExport se = new SchemaExport(config);
se.Create(true, true);
factory = config.BuildSessionFactory();
session = factory.OpenSession();
That's it...
Chris
PS: I really like this site, the GUI is perfect, and the quality of all articles is incredible. I think it will be huge :-) Have to register...
ManyToAny's currently aren't implemented (as of time of writing).
Regarding your setup for fluent and non-fluent mappings, you're almost there with your first example.
var cfg = MsSqlConfiguration.MsSql2005
.ConnectionString.Is(_testConnectionstring)
.ConfigureProperties(new Configuration());
cfg.AddMappingsFromAssembly(typeof(CatMap).Assembly); // loads hbm.xml files
var model = new PersistenceModel();
model.addMappingsFromAssembly(typeof(CatMap).Assembly); // loads fluent mappings
mode.Configure(cfg);
new SchemaExport(cfg).Create(true, true);
The main difference is that the SchemaExport is last. I assume your first example was actually loading the fluent mappings, but it'd already created the schema by that point.
You can do exactly what you want to do entirely within Fluent NHibernate.
The following code will use Fluent NHibernate syntax to fluently configure a session factory that looks for HBM (xml) mapping files, fluent mappings, and conventions from multiple possible assemblies.
var _mappingAssemblies = new Assembly[] { typeof(CatMap).Assembly };
var _autoPersistenceModel = CreateAutoPersistenceModel();
Fluently.Configure()
.Database(MsSqlConfiguration.MsSql2005.ConnectionString(_testConnectionstring))
.Mappings(m =>
{
foreach (var assembly in _mappingAssemblies)
{
m.HbmMappings.AddFromAssembly(assembly);
m.FluentMappings.AddFromAssembly(assembly)
.Conventions.AddAssembly(assembly);
}
m.AutoMappings.Add(_autoPersistenceModel );
})
.ExposeConfiguration(c => c.SetProperty("command_timeout", "340"))
.BuildSessionFactory();
There are many other options available to you as well: Fluent NHibernate Database Configuration
Mapping from Foo to Baa:
HasManyToMany< Baa > ( x => Baas )
.AsBag ( ) //can also be .AsSet()
.WithTableName ( "foobar" )
.WithParentKeyColumn ( "fooId" )
.WithChildKeyColumn ( "barId" ) ;
Check out the examples in ClassMapXmlCreationTester - they also show what the default column names are.