I am looking for some way to dynamically map database tables classes in my application using nhibernate (or if some other ORM works then let me know). I am fairly new to nhibernate, I used entity frameworks in the past though.
Most of my application will be using a static structures and fluent nhibernate to map them.
However there are multiple database tables that will be needed to be created and mapped to objects at each install site. These will all have as a base structure (id,name etc) however they will have additional fields depending on the type of data they are capturing. From some reading I found that I can use the "dynamic-component" mapping in xml to add fields using an IDictionary Attributes property. This is the first step and seems relatively straight forward. Ref (http://ayende.com/blog/3942/nhibernate-mapping-dynamic-component)
The second step is where I am struggling. I will need to define tables and map them depending on the client’s need. As stated above each of the tables will have a set of static properties, and some dynamic ones. They will also need to reference a static “Location”Class as shown below
Location (STATIC) (id,coordinates)
-----DynamicTable1 (DYNAMIC) (id,Name,location_id, DynamicAttribute1, DynamicAttribute2........)
-----DynamicTable2 (DYNAMIC) (id,Name,location_id, DynamicAttributeA, DynamicAttributeB....)
We will need to be able to create / map as many of these DynamicTables as the client needs. DynamicTable1, DynamicTable2 etc will most likely be different in some ways for most client sites. Is there any way in nhibernate to achieve this? The creating / management of the tables in the Database will be managed elsewhere, I just need some way to get this to map in my ORM.
A bit of background
This application will be used to store geological data. As geological data is inherently different depending on where it is, and geologist are using different methods and looking for different elements (gold, coal etc), the data structure to store this information needs to be extremely flexible.
Take a look at the new Mapping By Code functionality of NH 3.2. It should make it easy to create new table definitions at runtime. In contrast to Fluent, you don't need to write a mapping class, you just can add new classes in for loops:
// lookup all dynamic tables in the database using SQL or SMO or whatever
var dynamicTables = GetDynamicTables();
// map all dynamic tables
foreach(var table in dynamicTables)
{
mapper.Class<MyGenericEntity>(ca =>
{
// use an entity name to distinguish the mappings.
ca.EntityName(table.Name);
ca.Id(x => x.Id, map =>
{
map.Column("Id");
map.Generator(Generators.HighLow, gmap => gmap.Params(new { max_low = 100 }));
});
// map properties, using what ever is required: if's, for's ...
ca.Property(x => x.Something, map => map.Length(150));
});
}
Using the entity name you can store and load the entities to and from different tables, even if they are mapped as the same entity class. It is like Duck Typing With NHibernate..
Believe me, it won't be easy. If you are interested in a big challenge which impresses every NH expert, just go for it. If you just want to get it working you should choose a more classic way: create a static database model which is able to store dynamic data in a generic way (say: name value pairs).
see answer in Using nNHibernate with Emitted Code
class DynamicClass
{
public virtual int Id { get; set; }
public virtual string Name { get; set; }
public virtual Location Location { get; set; }
public virtual IDictionary DynamicData { get; set; }
}
Template
<hibernate-mapping>
<class name="DynamicClass">
...
<dynamic-component name="DynamicData">
<!--placeholder -->
</dynamic-component>
</class>
</hibernate-mapping>
replace <!--placeholder --> with generated
<property
name="P1"
type="int" />
<property
name="P2"
type="string" />
configure Sessionfactory
var sessionFactory = new NHibernate.Cfg.Configuration()
.AddXml(generatedXml)
... // DatabaseIntegration and other mappings
.BuildSessionFactory();
Query
var query = session.CreateCriteria<DynamicClass>();
foreach (var restriction in restrictions)
{
query.Add(Restrictions.Eq(restriction.Name, restriction.Value))
}
var objects = query.List<DynamicClass>();
Edit: ups i havent realised you need multiple tables per client
Option 1:
<class name="DynamicClass" table="tablenameplaceholder"> with replace and a different Sessionfactory for each dynamic class
Option 2:
Subclassing the dynamic class and use TPS (table per subclass) mappings
Option 3: see Stefans answer just with xml
<class name="DynamicTable1" class="DynamicClass" table="DynamicTable1">
Related
I'm just trying to get my head around nHibernate and have a query. When setting up the mappings file (with Fluent or regular .hbm.xml files) you specify relationships (bags; one-to-many, etc) and sub-types - the idea being (I believe) is that when you fetch an object it also fetches and matching data. My question is can I programmatically tell my query to ignore that relationship?
So, below, there is a Foo class with a list of Bar objects. Within the mappings file this would be a one-to-many relationship and sometimes I want to retrieve a Foo with all Bars BUT sometimes I want to just retrieve the Foo object without the Bar, for performance reasons. How can I do this?
public class Foo { public int Id { get; set; } public List<Bar> { get; set; } }
public class Bar { public int Id { get; set; }
Cheers
The relationship shouldn't be loaded automatically unless you turn off Lazy Loading or specify it to be eager loaded in the query.
Edit:
To answer your questions in the comment below.
1) It's done as part of the query. An basic example using QueryOver in NHibernate 3.0 would look something like:
var result = Session.QueryOver()
.Fetch(x => x.Category).Eager
.Where(x => x.Price > 10)
.List();
I think with ICriteria it's "SetFetchMode("Category", FetchMode.Eager)"
2) If you turn off lazy-loading on the mapping for an object, it will effectively always be eager loaded. Tho I suggest you eager load on a query-by-query basis to avoid the possibility of having a massive chain of data loaded, or loading data you don't actually need.
I have this scenario:
class A
{
public virtual int Id { get; set; }
public virtual B Child { get; set; }
}
class B
{
public virtual int Id { get; set; }
}
In the mapping of class A, I have a reference to class B:
map.Reference(a => a.Child).LazyLoad();
Now when I do something like:
Session.Query<TypeOfA>().Select(a => a);
Apart from the normal select * from ATable I get n selects from the BTable for each A line. Is like lazy loading is not working.
My questions are:
How to I make the lazyload work here ?
Can I bring the A entities and B entities in a single query ?
Thank you,
Lazy loading is switched on by default and should actually work. If there would be a problem, for instance if it can't generate the proxy for class B, it would complain when creating the session factory.
Are you sure that the queries for B are done by the query itself, and not be subsequent access to A?
You could optimize the access to B in two ways: fetch them together with A in a single query. (I don't know fluent, this is the xml way to configure it:)
<many-to-one fetch="join" ...>
This has some problems when used with lists and could also blow up your query a lot. It is of course not lazy loading at all.
Another, very nice and powerful optimization is batch fetching. It allows the instances to be fetched in separate queries, but fetches several of them at once.
<class name="B" batch-size="20" ...>
This would fetch 20 B's at once in one query. It is also available for lists:
<one-to-many fetch-size="20" ...>
Expanding on Stafan's suggestion to use batch-size, a quick Google search reveals that Fluent NHibernate now supports BatchSize for queries. From the docs:
ClassMap<T> BatchSize(int size)
Sets the query batch size for this entity.
Never used it myself, and the documentation is minimal (like a lot of FNH), but maybe you can find some sample code.
I have the following model which I have created and mapped with nHibernate.
Using lazy loading so I don't need to get the Vehicles for the Dealer at the start.
Public class Dealer
{
public virtual string Name { get;set;}
public virtual IList<Vehicles> Vehicles { get;set;}
}
Now let's assume the Dealer has thousands of vehicles.
If I do Dealer.Vehicles.Count then NH will select and pull all the data.
What is the best way to simply get a count? Is there any way in which I can get a count with out declaring A new property dealerCount within the Dealer Class?
Also there is a feature in Hibernate which I believe will be implemented in a newer version of NH called Extra Lazy Loading. Would this solve the problem?
extra lazy loading would issue sql instead of populating the collection for certain operations such as Count or Contains. In fluent mappings its used as:
HasMany(x => x.CollectionProperty).ExtraLazyLoad();
or HBM
<one-to-many lazy="extra" ...
It's only usefull if you have large collections and need the special behavior.
Use count projection (Projections.RowCount)
If i want to use Linq-SQL i also have to drag the DB Table unto the designer surface to create the entity classes.
I always like full control in my application and do not like the classes created by dotnet.
Is it possible to provide this connection between Linq and the DB using my own Data Access Layer Entity classes?
How can i get it done?
You can write your own classes very easily using Linq-to-SQL - just involves painting your classes with some Attributes.
For Example, this is a very simple table I have in one of my projects, and it works with Linq-to-SQL just fine:
[Table(Name = "Categories")]
public class Category : IDataErrorInfo
{
[Column(IsPrimaryKey = true, IsDbGenerated = true, AutoSync = AutoSync.OnInsert)]
public int Id { get; set; }
[Column] public string Name { get; set; }
[Column] public string ExtensionString { get; set; }
}
The code was very easy, especially if you make your property names line up with your table names (you don't have to).
Then you just need a Repository to connect to the DB:
class CategoryRepository : ICategoryRepository
{
private Table<Category> categoryTable;
public CategoryRepository(string connectionString)
{
categoryTable = (new DataContext(connectionString)).GetTable<Category>();
}
}
Of course there is more to it, but this shows you the very basics and it is not hard to do once you understand it. This way you have 100% control over your classes and you can still take advantage of Linq-to-SQL.
I learned this approach from Pro ASP.NET MVC Framework, an awesome book.
If you want to see more, all of my Linq-to-SQL classes were written from scratch on one of my projects you can browse here.
To avoid drag & drop you can take a look at SqlMetal.exe.
However, it sounds like you really are requesting Persistence Ignorance, and I'm not sure that this is possible with L2S - it certainly isn't possible with LINQ to Entities until .NET 4...
I once wrote a blog post on using SqlMetal.exe and subsequently modifying the generated schema - perhaps you will find it useful, although it has a different underlying motivation.
I've got a couple tutorials up on CodeProject that walk through how to do this, including how to handle the relationships (M:M, 1:M, M:1) in an OO way and keep them in synch as you make updates:
A LINQ Tutorial: Mapping Tables to Objects
A LINQ Tutorial: Adding/Updating/Deleting Data
NHibernate noob here. Looking for advice on how to map the following common scenario:
[Store]
id pk
Name
[StockItem]
id pk
Name
[StockItemStore]
id pk
StockItemId fk
StoreId fk
ParLevel
I have created a domainmodel that allows various StockItems to be assigned to various Stores via the StockItem Entity using a AssignToStore(Store store) method.
I am now using nhibernate to create my db schema. How do I setup the mapping files for this basic scenario?
Any tips greatly appreciated.
Chev
Unfortunately this relationship isn't the easiest thing to model in nHibernate and there are some inherent problems you will encounter when trying to do queries on the data in the linking table, that will require some complicated workarounds, but once you get it set up it works quite well.
My approach to this is to set it up as two many-to-one mappings with the following relationship in the Store mapping and the inverse relationship in the StockItem mapping.
<bag name="StockItems" table="StockItemStore" lazy="true">
<key column="StoreId" />
<composite-element class="SuperStore.Components.StockItemStore, SuperStore.Components">
<property name="ParLevel" type="Int32" />
<many-to-one name="StockItem" column="StockItemId" class="SuperStore.Components.StockItem, SuperStore.Components" fetch="join" cascade="all" />
</composite-element>
</bag>
the Store class will have the following collection:
public virtual IList< StockItemStore > StockItems {get;set;}
and StockItem will have the inverse again:
public virtual IList< StockItemStore > Stores {get;set;}
The StockItemStore object is able to contain either object (Store or StockItem) and any additional information that is in the linking table. (in this case just the ParLevel.
depending on which side you are looking at the StockItemStore object from either Store or StockItem will null. It could be broken out into two classes, but I find this approach easier to work with. It just requires you as the developer to know which side you are approaching it from, but its a good tradeoff for making the code simpler and more reusable in my opinion
public class StockItemStore
{
private StockItem stockItem;
private Store store;
public virtual StockItem StockItem
{
get
{
if (stockItem == null)
{
stockItem = new StockItem();
}
return stockItem;
}
set
{
stockItem = value;
}
}
public virtual Store store
{
get
{
if (store == null)
{
store = new Store();
}
return store;
}
set
{
store = value;
}
}
public virtual int ParLevel { get; set; }
}
My approach doesn't use the single unique identifier in the StockItemStore as you defined in your question, but rather what amounts to a composite key in the linking table. But it has served me well in the past overall. I'm sure you could shoehorn that id in somehow if you really needed it.
This approach works great for querying using HQL. if you are trying to use ICriteria queries it tends to get a little wonky. There are some solutions out there with using database views to simulate another table to use for querying that have worked for me.
If you need to do ICriteria queries let me know and I can post some sample code.
Max
I would strongly recommend reading up on nHibernate. Here's a very good starting point:
The nHibernate FAQ
I would also recommend that you do the mappings by hand the first couple of times. After that, you should check out Fluent nHibernate. Fluent nHibernate can (among other things) automatically generate the mappings for you from your domain model and also help you to generate the database schema. It is a very flexible tool that is getting better and better. You'll find it here:
Fluent nHibernate
Good luck!