two aggregate roots in fluent nhibernate - fluent-nhibernate

The problem is that I have two aggregate roots
The Aggregate roots are
Project.
NewsArticle.
A Project can have a collection of related NewsArticle.
A NewsArticle can have a collection of related Projects.
The requirements are that:
A user can assoicate a number of NewsArticle from Projects.
A user can assoicate a number of Project from NewsArticles.
In the database.
NewsArticle --* NewsArticleProject *-- Project.
In the mappings
NewsArticle side
public void Override(AutoMapping<NewsArticle> mapping)
{
mapping.HasManyToMany(c => c.FeaturedProjects).Cascade.All().Table("NewsArticleProject").AsSet();
}
project side
public void Override(AutoMapping<Project> mapping)
{
mapping.HasManyToMany(c => c.FeaturedNewsArticles).Table("NewsArticleProject").Inverse().AsSet();
}
I also tried HasMany() but that gives me an error message complaining about the ColumnName which I also set.
I am struggling to get fluent nHibernate to play nicely into the mappings it so that it can for fulfil my requirements.
I can manage to get it to work for one side only but when I try to get it to work with the other side I get this error message.
Can't figure out what the other side of the many-to-many property 'FeaturedNewsArticles' should be.
Thanks in advance if anyone can help me come up with a solution.

Try and give responsibility for saving to both sides by using Inverse() on "NewsArticle" too.
public void Override(AutoMapping<NewsArticle> mapping)
{
mapping.HasManyToMany(c => c.FeaturedProjects).Cascade.All().Table("NewsArticleProject").Inverse().AsSet();
}

Related

Mapping an extension table that might not have a row

I'm working with Fluent nHibernate on a legacy database and have a main Person table and several extension tables containing additional information about the person. These extension tables are one-to-one, meaning that a person will only have one row on the extension table and the extension table should always map back to one person.
Table: Person
Columns: PersonID, FirstName, LastName, etc.
Table: PersonLogin
Columns: PersonID (FK, unique), UserName, Password, etc.
I have my mappings defined as this (with the irrelevant properties omitted):
public PersonMap()
{
Table("Person");
Id(x => x.Id, "PersonID").Not.Nullable();
References(x => x.Login, "PersonID").LazyLoad();
}
public LoginMap()
{
Table("PersonLogin");
Id(x => x.Id, "PersonID").GeneratedBy.Foreign("Person");
References(x => x.Person, "PersonID").LazyLoad();
}
This works when I have data on both tables, but I recently learned that some of the extension tables don't have data for all Person rows. This caused me to get errors during the query. So, I added .NotFound.Ignore() to my PersonMap making it look like this:
References(x => x.Login, "PersonID").LazyLoad().NotFound.Ignore();
That caused me to get unnecessary selects from the Login table due to https://nhibernate.jira.com/browse/NH-1001 when my business layer doesn't need to project any of the extension table values. It is causing the performance to be terrible in some of my search queries.
I've scoured a lot of posts, but haven't found a rock solid answer about how to address this scenario. Below are the options I've tried:
Option One:
Create rows on the extension table to ensure there is no Person without a row on the extension table and then remove the .NotFound.Ignore().
The issue with this option is that it's a legacy database and I'm not sure where I'd need to update to ensure that a PersonLogin is inserted when a Person is inserted.
Option Two:
Remove the PersonLogin reference from my PersonMap and custom load it inside my Person class. Like this:
public class Person
{
/// <summary> Gets or sets the PersonID </summary>
public virtual int Id { get; set; }
private bool loadedLogin;
private PersonLogin login;
public virtual PersonLogin Login
{
get
{
if (!loadedLogin)
{
login = SessionManager.Session().Get<PersonLogin>(Id);
loadedLogin = true;
}
return login;
}
set
{
login = value;
loadedLogin = true;
}
}
}
The issue I'm having with it is that I can't eagerly fetch the data when performing a query to pull back a large number of Person objects and their Logins.
Option Three:
I just started playing to see if I could write a custom IEntityNotFoundDelegate to not throw the exception for these objects.
private class CustomEntityNotFoundDelegate : IEntityNotFoundDelegate
{
public void HandleEntityNotFound(string entityName, object id)
{
if (entityName == "my.namespace.PersonLogin")
{
return;
}
else
{
throw new ObjectNotFoundException(id, entityName);
}
}
}
And I added this to the config
cfg.EntityNotFoundDelegate = new CustomEntityNotFoundDelegate();
It catches my scenario and returns back now instead of throwing the error, but now when I try to project those PersonLogin properties onto my business objects, it's attempting to use the Proxy object and throws this error that I'm trying to figure out if I can handle cleanly (possibly in a IPostLoadEventListener).
System.Reflection.TargetException occurred
Message = Non-static method requires a target
I think I've got this working now by keeping the .NotFound.Ignore().
I originally stated:
That caused me to get unnecessary selects from the Login table due to https://nhibernate.jira.com/browse/NH-1001 when my business layer doesn't need to project any of the extension table values. It is causing the performance to be terrible in some of my search queries.
I was able to tweak my LINQ queries to use the IQueryOver in some instances and to improve my use of LINQ in other scenarios to project only the necessary values. This appears to have resolved the queries from pulling back the extension tables since their values were not needed in the projections.
I thought that my queries weren't projecting these extension tables, but figured out that I had a method ToKeyValuePair that I was using in the projection to concatenate the ID and a Name field together of some related properties. That method was causing the objects to load completely since LINQ wasn't able to determine that the needed fields were present without joining to the extension table.

How do I use Fluent Nhibernate many-to-many for optimal performance?

I have a product table that has a many-to-many relation to itself (using a two-column many-to-many table) and I have set it up in Fluent NHibernate with the following code:
public class ProductConfiguration : ClassMap<Product>
{
public ProductConfiguration()
{
Table("Product");
Id(p => p.Id).GeneratedBy.Guid();
Map(p => p.Name).Not.Nullable().Length(254);
Map(p => p.Description).Not.Nullable().Length(1000);
Map(p => p.CreatedAt).Not.Nullable();
HasManyToMany(p => p.CrossSell)
.Table("ProductCrossSell")
.ParentKeyColumn("Id")
.ChildKeyColumn("ProductId");
}
}
My MVC application has two pages that uses this setup:
Index - Uses a generic repository GetAll method to display all products.
Detail - Uses a generic repository GetById method to display one product and any related cross sell products setup in the many-to-many realation.
It looks like NHibernate is set to LazyLoad the many-to-many by default so when I fire up the application and watch it in profiler I can see that it does LazyLoad the many-to-many with the following alert "Use of implicit transactions is discouraged".
How do I get rid of this alert? I couldn't find any information on how to wrap a LazyLoad inside a transaction to get rid the alert. Is it even possible?
Is there a way to not lazyload this by telling NHibernate that whenever I ask for GetById make sure to join the tables a get everything in one query? I tried using .Fetch.Join() in the many-to-many mapping but that also affected my GetAll query which now displays a joined result set as well which is incorrect.
What is the best apprach for this kind of simple scenario?
Thanks
The way to get rid of the warning is to access the object graph and fully populate the UI elements inside a single transaction.
Not by configuration. You can create an HQL query that eager fetches the association and use that query for a specific view. I would stick with lazy loading and not make that optimization unless needed. The HQL would be:
return session.CreateQuery("from ProductionConfiguration pc join fetch pc.CrossSell where pc.Id = ?")
.SetGuid(0, id)
.List<ProductConfiguration>();
All collections are lazily loaded in NHibernate by default.
You must be triggering loading with a call of some kind (maybe even with the debugger watches)

NHibernate: Using slightly different hbm mapping files depending on context

One of my applications is a public website, the other is an intranet. The public website runs using a limited security user that must access a certain table through a view, whereas the intranet can access the table itself.
This seems like it would be quite simple to setup using Fluent NHibernate. In my ClassMap I could do a check like this:
public class MyEntityClassMap : ClassMap<MyEntity>
{
public MyEntityClassMap()
{
if (NHibernateConfig.Current.Context == "intranet")
Table("t_MyEntity");
else
Table("v_MyEntity_pub");
... etc
}
}
Is there a simple way of doing this for embedded hbm files? The only method I can think of would be to have two copies of the hbm file, which would be confusing and far from ideal.
Is there perhaps a better way of achieving the same result?
Actually what you ask it is possible. You can actually access the embedded XML files and alter their content before the SessionFactory is build (on Application Start).
Assuming your will choose to reference the "t_MyEntity" in your entities by default here is how you can dynamically change this reference when you want to reference the "v_MyEntity_pub" table instead (the code may not work as it is but you will get the idea):
NHibernate.Cfg.Configuration cfg = new NHibernate.Cfg.Configuration();
cfg.AddAssembly(ASSEMBLYNAME);
if (NHibernateConfig.Current.Context != "intranet") //this is how you have stated you distinguish the intranet application from the other one.
{
string[] resourcesNames = assembly.GetManifestResourceNames();
foreach (string resourceName in resourcesNames)
{
StreamReader sr = new StreamReader(assembly.GetManifestResourceStream(resourceName));
string resourceContent = sr.ReadToEnd();
resourceContent = resourceContent.Replace("t_MyEntity", "v_MyEntity_pub");
cfg.AddXmlString(resourceContent);
}
}
ISessionFactory sessionFactory = cfg.BuildSessionFactory();
The above code should be executed only once for the lifetime of your application and only for the intranet application.
Although this is perhaps not the most helpful answer to your problem, I don't believe that this is possible in a mapping file. I also don't think that two hbm files would work for the same name, as it would be unable to distinguish between the two, you would instead have to have two identical objects each with slightly different names and mapping files. Which as you said in your question, would be completely confusing and ideal would simply be a spot on the horizon that you were hoping to, someday, reach.
Why is it that can't access everything directly through the view? I'm assuming there is no writing involved in this process? Is there any way you can change this method of accessing data while still maintaining your security?

Nhibernate entity with multiple Many-To-Many lists of the same type?

Does anybody know how I would map an entity with two many-to-many collections of the same child type.
My database structure is this....
The "normal" relationship will be....
tbl_Parent
col_Parent_ID
tbl_Parent_Child_Xref
col_Parent_ID
col_Child_ID
tbl_Child
col_Child_ID
The alternative relationship is...
tbl_Parent
col_Parent_ID
tbl_Include_ParentChild_Xref
col_Parent_ID
col_Child_ID
tbl_Child
col_Child_ID
The entity and mapping look like this...
public partial class ParentEntity : AuditableDataEntity<ParentEntity>
{
public virtual IList<ChildEntity> Children { get; set; }
public virtual IList<ChildEntity> IncludedChildren { get; set; }
}
public partial class ParentMap : IAutoMappingOverride<ParentEntity>
{
public void Override(AutoMapping<ParentEntity> mapping)
{
mapping.Table("tbl_Parent");
mapping.HasManyToMany(x => x.Children)
.Table("tbl_Parent_Child_Xref")
.ParentKeyColumn("col_Parent_ID")
.ChildKeyColumn("col_Child_ID")
.Inverse()
.Cascade.All();
mapping.HasManyToMany(x => x.IncludedChildren)
.Table("tbl_Include_ParentChild_Xref")
.ParentKeyColumn("col_Parent_ID")
.ChildKeyColumn("col_Child_ID")
.Inverse()
.Cascade.All();
}
}
The error that I'm getting is
"System.NotSupportedException: Can't figure out what the other side of the many-to-many property 'Children' should be."
I'm using NHibernate 2.1.2, FluentNhibernate 1.0.
It seems FNH is confused because you seem to map the same object (ChildEntity) to two different tables, if I'm not mistaken.
If you don't really need the two lists to get separated, perhaps using a discriminating value for each of your lists would solve the problem. Your first ChildEntity list would bind to the discriminationg value A, and you sesond to the discriminating value B, for instance.
Otherwise, I would perhaps opt for a derived class of your ChildEntity, just not to have the same name of ChildEntity.
IList<ChildEntity> ChildEntities
IList<IncludedChildEntity> IncludedChildEntities
And both your objects classes would be identitical.
If you say it works with NH, then it might be a bug as already stated. However, you may mix both XML mappings and AutoMapping with FNH. So, if it does work in NH, this would perhaps be my preference. But think this workaround should do it.
You know I'm just shooting in the dark here, but it almost sounds like your ChildEntity class isn't known by Hibernate .. that's typically where I've seen that sort of message. Hibernate inspects your class and sees this referenced class (ChildEntity in this case) that id doesn't know about.
Maybe you've moved on and found the issue at this point, but thought I'd see anyway.
Fluent is confused because you are referencing the same parent column twice. That is a no-no. And as far as I can tell from the activity i have seen, a fix is not coming any time soon.
You would have to write some custom extensions to get that working, if it is possible.
To my great pity, NHibernate cannot do that. Consider using another ORM.

Is it possible to use Linq-SQL without drag-and-drop?

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