How to map a collection of abstract classes in nhibernate - nhibernate

I have been reading Nhibernate in Action, but the section on mapping polymorphic collections is a little too short on how to do this.
I have the following code
[Class]
[Discriminator(Column="MachineType",TypeType=typeof(string))]
public abstract class Machine
{
[Property]
public string Name{get;set;}
}
[Subclass(DiscriminatorValue="Heavy",ExtendsType=typeof(Machine))]
public class HeavyMachine : Machine
{
[Property]
public int Weight { get; set; }
}
[Subclass(DiscriminatorValue="Fast",ExtendsType=typeof(Machine))]
public class FastMachine : Machine
{
[Property]
public float Speed { get; set; }
}
[Class]
public class Module
{
List<Machine> machines = new List<Machine>();
[Bag(Name = "Machines", Cascade = "all", Lazy = false, Inverse=true)]
[Key(1, Column = "Machine")]
[OneToMany(2, ClassType = typeof(Machine))]
public IList<Machine> Machines
{
get
{
return machines.AsReadOnly();
}
private set
{
machines = value.ToList();
}
}
}
With the code above I am not getting any errors, but the Collection of machines in Module remains empty after retreiving my objects from the database. The mapping of the Machine (and it's subclasses) does seem ok, because a property of type Machine is correctly returned.
What Nhibernate.Mapping.Attributes do I need to map my collection of abstract classes?
thx in advance!

Ok I found the solution.
After removing the "Inverse=true" tag from my IList mapping it worked.

Related

Return Entity Framework objects over WCF

We have a problem concerning Entity Framework objects and sending them through WCF.
We have a database, and Entity Framework created classes from that database, a 'Wallet' class in this particular situation.
We try to transfer a Wallet using this code:
public Wallet getWallet()
{
Wallet w = new Wallet();
w.name = "myname";
w.walletID = 123;
return w;
}
We need to transfer that Wallet class, but it won't work, we always encounter the same exception:
"An error occurred while receiving the HTTP response to localhost:8860/ComplementaryCoins.svc. This could be due to the service endpoint binding not using the HTTP protocol. This could also be due to an HTTP request context being aborted by the server (possibly due to the service shutting down). See server logs for more details."
We searched on the internet, and there is a possibility that the error is due to the need of serialization of Entity Framework-objects.
We have absolutely no idea if this could be the case, and if this is the case, how to solve it.
Our DataContract looks like this (very simple):
[DataContract]
public partial class Wallet
{
[DataMember]
public int getwalletID { get { return walletID; } }
[DataMember]
public string getname { get { return name; } }
}
Does anyone ever encountered this problem?
EDIT: Our Entity Framework created class looks like this:
namespace ComplementaryCoins
{
using System;
using System.Collections.Generic;
public partial class Wallet
{
public Wallet()
{
this.Transaction = new HashSet<Transaction>();
this.Transaction1 = new HashSet<Transaction>();
this.User_Wallet = new HashSet<User_Wallet>();
this.Wallet_Item = new HashSet<Wallet_Item>();
}
public int walletID { get; set; }
public string name { get; set; }
public virtual ICollection<Transaction> Transaction { get; set; }
public virtual ICollection<Transaction> Transaction1 { get; set; }
public virtual ICollection<User_Wallet> User_Wallet { get; set; }
public virtual ICollection<Wallet_Item> Wallet_Item { get; set; }
}
}
Thanks for helping us.
I had the same problem some time ago and the solution for this was:
The entity framework was returning a serialized class instead of normal class.
eg. Wallet_asfawfklnaewfklawlfkawlfjlwfejlkef instead of Wallet
To solve that you can add this code:
base.Configuration.ProxyCreationEnabled = false;
in your Context file.
Since the context file is auto generated you can add it in the Context.tt
In the Context.tt file it can be added around lines 55-65:
<#=Accessibility.ForType(container)#> partial class <#=code.Escape(container)#> : DbContext
{
public <#=code.Escape(container)#>()
: base("name=<#=container.Name#>")
{
base.Configuration.ProxyCreationEnabled = false;
<#
if (!loader.IsLazyLoadingEnabled(container))
{
#>
this.Configuration.LazyLoadingEnabled = false;
<#
Try specifying a setter for the properties, something like this :
[DataContract]
public partial class Wallet
{
[DataMember]
public int getwalletID { get { return walletID; } set { } }
[DataMember]
public string getname { get { return name; } set { } }
}
If it still doesn't work, you may consider creating an intermediate POCO class for this purpose, and use mapper library like AutoMapper or ValueInjecter to transfer the data from the EF objects.
The POCO class should have same properties as your EF class :
[DataContract]
public class WalletDTO
{
[DataMember]
public int walletID { get; set; }
[DataMember]
public string name { get; set; }
}
And modify your method to return this class instead :
public WalletDTO getWallet()
{
Wallet w = new Wallet(); // or get it from db using EF
var dto = new WalletDTO();
//assuming we are using ValueInjecter, this code below will transfer all matched properties from w to dto
dto.InjectFrom(w);
return dto;
}
Are you trying to recieve a IEnumerable<Wallets>? If - yes, please modify your server class that returns the IEnumerable by adding .ToArray() method

NHibernate: Get concrete type of referenced abstract entity

I have the following classes:
public abstract class FooBase
{
public virtual Guid Id { get; set; }
}
public class FooTypeA : FooBase
{
public virtual string TypeAStuff { get; set; }
}
public class Bar
{
public virtual Guid Id { get; set; }
public virtual FooBase Foo { get; }
}
FooBase and FooTypeA are mapped using the table-per-class-heirarchy pattern.
Bar is mapped like this:
public class BarDbMap : ClassMap<Bar>
{
public BarDbMap()
{
Id(x => x.Id);
References(x => x.Foo)
.LazyLoad();
}
}
So when I load a Bar, its Foo property is only a proxy.
How do I get the subclass type of Foo (i.e. FooTypeA)?
I have read through a lot of NH docs and forum posts. They describe ways of getting that work for getting the parent type, but not the subclass.
If I try to unproxy the class, I receive errors like:
object was an uninitialized proxy for FooBase
I worked out how to avoid the exception I was receiving. Here is a method that unproxies FooBase:
public static T Unproxy<T>(this T obj, ISession session)
{
if (!NHibernateUtil.IsInitialized(obj))
{
NHibernateUtil.Initialize(obj);
}
if (obj is INHibernateProxy)
{
return (T) session.GetSessionImplementation().PersistenceContext.Unproxy(obj);
}
return obj;
}
Add a Self property to FooBase and use that to check the type:
public abstract class FooBase
{
public virtual Guid Id { get; set; }
public virtual FooBase Self { return this; }
}
Usage:
if (Bar.Foo.Self is FooTypeA) { // do something }
To get the "unproxied" type you could add a method like this to FooBase:
public virtual Type GetTypeUnproxied() {
return GetType();
}
When this method is invoked on a proxy the type of the underlying object will be returned.
However, from your description it seems you are trying to do this outside of the NHibernate session and that won't work with this strategy either. To invoke any method on the proxy where the call is proxied to the underlying object it needs to be instantiated and that can only happen within the NHibernate session since the actual type of the object is stored in the database (in a discriminator column for the table-per-class-hierarchy inheritance strategy). So my guess is that you need to make sure that the proxy is initialized before closing the session if you need to check the type later.
If the reason for lazy loading the Bar->FooBase relation is that FooBase (or a derived type) might contain large amounts of data and you are using NHibernate 3 you could use lazy properties instead.

Bundling a list of entities into a component

With FluentNHibernate I have mapped a UserPreference entity which references the GeneralPreference, GeneralPreferenceOption, and Profile entities:
public class UserPreference
{
public virtual long Id { get; set; }
public virtual Profile Profile { get; set; }
public virtual GeneralPreference Preference { get; set; }
public virtual GeneralPreferenceOption Value { get; set; }
}
It's easy enough to map a list of UserPreference on my Profile entity, but what I actually would like to do is wrap this list inside another class so that I can simplify operations concerning a user's given preferences:
public class Preferences
{
public IList<UserPreferences> UserPreferences{get;set;}
public Language Language {
{
//look up the language preference here
}
}
This kind of feels like a Component, but Components were not created for this type of scenario. Does anyone have any pointers on how I might map this?
I figured out a way to do this by mapping a private property on my Profile Entity. Using the techniques from the Fluent NHibernate wiki on mapping private properties (http://wiki.fluentnhibernate.org/Fluent_mapping_private_properties) I map a collection of UserPreference on my Profile Entity. Then I create another class PropertyHandler which takes an IEnumerable as a constructor parameter and make an instance of this a public property on Profile as well:
public class Profile
{
private PreferenceHandler _preferenceHandler;
get { return _preferenceHandler ?? (_preferenceHandler = new PreferenceHandler(UserPreferences)); }
private IEnumerable<UserPreference> UserPreferences { get; set; }
public static class Expressions
{
public static readonly Expression<Func<Profile, IEnumerable<UserPreference>>> UserPreferences = x => x.UserPreferences;
}
}
Notice the nested static class. It's used to enable mapping of a private property with FluentNHibernate.
The mapping class looks something like this:
public class ProfileMappings : ClassMap<Profile>
{
public ProfileMappings()
{
//... other mappings
HasMany(Profile.Expressions.UserPreferences);
}
}
I can now use the PreferenceHandler class to create helper methods over my collection of UserPreference.
An alternative is to build extension methods for IEnumberable. This works, but I decided not to do this because
1) I'm not really extending the IEnumerable functionality and
2) my helper methods disappear inamongst all the other IEnumerable extension methods making the whole thing a bit cluttered.

Fluent NHibernate - automapping: allow null for single properties

I know this question has been raised in similar form multiple times, but none of the threads could give me the concrete answer to my question.
I use Fluent NHibernate and Fluent`s auto-mapping to map my domain entities. Right now, I use this convention class to set all properties NOT NULL:
public class NotNullColumnConvention : IPropertyConvention
{
public void Apply(FluentNHibernate.Conventions.Instances.IPropertyInstance instance)
{
instance.Not.Nullable();
}
}
The big question is:
What do I need to do, to allow single properties of my entity classes to be NULL?
Here is one of my entity classes:
public class Employee : Entity
{
public virtual string FirstName { get; set; }
public virtual string LastName { get; set; }
}
I´d be really pleased, if someone can finally help me out! All possible search string I have entered into Google return pages, marked as already visited...
Thanks,
Arne
EDIT: Changed title ... Want to allow NULL for single properties
Create an attribute :
[AttributeUsage(AttributeTargets.Property, AllowMultiple = false)]
public class CanBeNullAttribute : Attribute
{
}
And a convention :
public class CanBeNullPropertyConvention : IPropertyConvention, IPropertyConventionAcceptance
{
public void Accept(IAcceptanceCriteria<IPropertyInspector> criteria)
{
criteria.Expect(
x => !this.IsNullableProperty(x)
|| x.Property.MemberInfo.GetCustomAttributes(typeof(CanBeNullAttribute), true).Length > 0);
}
public void Apply(IPropertyInstance instance)
{
instance.Nullable();
}
private bool IsNullableProperty(IExposedThroughPropertyInspector target)
{
var type = target.Property.PropertyType;
return type.Equals(typeof(string)) || (type.IsGenericType && type.GetGenericTypeDefinition().Equals(typeof(Nullable<>)));
}
}
Drop the attribute on top of your properties.

Fluent NHibermate and Polymorphism and a Newbie!

I'm a fluent nhibernate newbie and I'm struggling mapping a hierarchy of polymorhophic objects. I've produced the following Model that recreates the essence of what I'm doing in my real application.
I have a ProductList and several specialised type of products;
public class MyProductList
{
public virtual int Id { get; set; }
public virtual string Name {get;set;}
public virtual IList<Product> Products { get; set; }
public MyProductList()
{
Products = new List<Product>();
}
}
public class Product
{
public virtual int Id { get; set; }
public virtual string ProductDescription {get;set;}
}
public class SizedProduct : Product
{
public virtual decimal Size {get;set;}
}
public class BundleProduct : Product
{
public virtual Product BundleItem1 {get;set;}
public virtual Product BundleItem2 {get;set;}
}
Note that I have a specialised type of Product called BundleProduct that has two products attached.
I can add any of the specialised types of product to MyProductList and a bundle Product can be made up of any of the specialised types of product too.
Here is the fluent nhibernate mapping that I'm using;
public class MyListMap : ClassMap<MyList>
{
public MyListMap()
{
Id(ml => ml.Id);
Map(ml => ml.Name);
HasManyToMany(ml => ml.Products).Cascade.All();
}
}
public class ProductMap : ClassMap<Product>
{
public ProductMap()
{
Id(prod => prod.Id);
Map(prod => prod.ProductDescription);
}
}
public class SizedProductMap : SubclassMap<SizedProduct>
{
public SizedProductMap()
{
Map(sp => sp.Size);
}
}
public class BundleProductMap : SubclassMap<BundleProduct>
{
public BundleProductMap()
{
References(bp => bp.BundleItem1).Cascade.All();
References(bp => bp.BundleItem2).Cascade.All();
}
}
I haven't configured have any reverse mappings, so a product doesn't know which Lists it belongs to or which bundles it is part of.
Next I add some products to my list;
MyList ml = new MyList() { Name = "Example" };
ml.Products.Add(new Product() { ProductDescription = "PSU" });
ml.Products.Add(new SizedProduct() { ProductDescription = "Extension Cable", Size = 2.0M });
ml.Products.Add(new BundleProduct()
{
ProductDescription = "Fan & Cable",
BundleItem1 = new Product() { ProductDescription = "Fan Power Cable" },
BundleItem2 = new SizedProduct() { ProductDescription = "80mm Fan", Size = 80M }
});
When I persist my list to the database and reload it, the list itself contains the items I expect ie MyList[0] has a type of Product, MyList[1] has a type of SizedProduct, and MyList[2] has a type of BundleProduct - great!
If I navigate to the BundleProduct, I'm not able to see the types of Product attached to the BundleItem1 or BundleItem2 instead they are always proxies to the Product - in this example BundleItem2 should be a SizedProduct.
Is there anything I can do to resove this either in my model or the mapping?
Thanks in advance for your help.
As it stands, the BundleItem1 and BundleItem2 properties will always have a Product proxy because NH creates your proxies without touching the database, so it doesn't know if they are Products or some derived type. But when you call a method on your bundle items, NH should hit the DB and load the correct record, and you should get polymorphic behavior.
You could test this out. Add an override of ToString to your SizedProduct:
public override string ToString()
{
return "I'm a sized product!";
}
Then load your BundleProduct and do this:
Debug.WriteLine(bp.BundleItem1.ToString());
Debug.WriteLine(bp.BundleItem2.ToString());
You should find that the second call prints out "I'm a sized product!", and this will demonstrate that you have working polymorphism.
Assuming this all worked as I've described, its time to tackle the real question: what exactly do you want to do? Maybe you could provide some code that doesn't actually work as you would like it to.