How can I map this:
public class Customer
{
private IList<Order> _orders;
public IEnumerable<Order>
GetAllOrders()
{
return _orders;
}
}
On the project page are some samples but none is about this situation.
There is this sample:
// model
public class Account
{
private IList<Customer> customers = new List<Customer>();
public IList<Customer> Customers
{
get { return customers; }
}
}
// mapping
HasMany(x => x.Customers)
.Access.AsCamelCaseField();
But it assumes that Account has public field Customers and that scenario is different as mine. I tried some possible options but none works:
HasMany(x => Reveal.Propertie("_orders"))
Private fields works fine in simple property mapping but collection mapping
is quite different. Any idea? Thanks
The easiest solution is to expose your collection as a public property Orders instead of the GetAllOrders() method. Then your mapping is
HasMany(x => x.Orders)
.Access.AsCamelCaseField(Prefix.Underscore);
and your class is
public class Customer
{
private IList<Order> _orders = new List<Order>();
public IEnumerable<Order> Orders
{
get { return _orders; }
}
}
If that doesn't work for you, it is possible to map private properties using Fluent NHibernate's Reveal mapping.
Edited to add: Having just done this, the correct answer is:
HasMany<Order>(Reveal.Property<Customer>("_orders")) etc.
The collection must be exposed as a protected virtual property to allow proxying:
protected virtual IList<Order> _orders { get; set; }
This answer put me on the right track.
Thanks.
Your solution is fine. However, there could be situations(hypotetical) when you dont want to reveal your private collection. This mapping scenario is not explained in your linked post because there is difference between mapping simple propertie as descibed in that post and collection mapping. My attempt to use HasMany(x => Reveal.Propertie("_orders")) failed because of raised exception.
You can map a completely private collection using Reveal.Member(), but it has a specific and non-obvious restriction: the Expression that HasMany() accepts has to return either IEnumerable<TReferenced> or object.
For your class:
public class Customer
{
private IList<Order> _orders;
public IEnumerable<Order> GetAllOrders()
{
return _orders;
}
}
the following line will populate the _orders collection:
HasMany(Reveal.Member<Customer, IEnumerable<Order>>("_orders"));
//additional mapping behaviors
For completeness - the following line gives a compiler error:
HasMany(Reveal.Member<Customer, IList<Order>>("_orders"));
Related
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.
I am trying to map my collections with FNHib automapping. The problems that I want to solve are:
1) I want all my collections in the project to be mapped via private field. How can I say that globally?
2) Is there any way to automap bidirectional relationship without explicitly overriding each of my entities.
class OrganizationEntity example:
private ISet<> _collectionWarehouse;
public virtual IEnumerable<WarehouseEntity> CollectionWarehouse
{
get{return _collectionWarehouse; }
set{_collectionWarehouse = new HashedSet<WarehouseEntity>((ICollection<WarehouseEntity>)value)}
}
Class WarehouseEntity example:
public virtual OrganizationEntity Organization{get;set;}
You can map your collections to a private field 'globally' with the following convention:
// assumes camel case underscore field (i.e., _mySet)
public class CollectionAccessConvention : ICollectionConvention
{
public void Apply(ICollectionInstance instance) {
instance.Access.CamelCaseField(CamelCasePrefix.Underscore);
}
}
Whenever you want to set a 'global' automap preference in FNH, think conventions. The you use the IAutoOverride on a given class map if you need to.
As far has the set (a HashSet is usually what I really want also) part, the last time I had to do some mapping, I did need to do an override, like:
public class ActivityBaseMap : IAutoMappingOverride<ActivityBase>
{
public void Override(AutoMapping<ActivityBase> m)
{
...
m.HasMany(x => x.Allocations).AsSet().Inverse();
}
}
I do agree that should translate into a convention though, and maybe you can do that these days. Please post if you figure it out.
HTH,
Berryl
CODE TO USE A HASHSET as an ICollection =================
public virtual ICollection<WarehouseEntity> Wharehouses
{
get { return _warehouses ?? (_warehouses = new HashSet<WarehouseEntity>()); }
set { _warehouses = value; }
}
private ICollection<WarehouseEntity> _warehouses;
I'm trying to use the automap functionality in fluent to generate a
DDL for the following model and program, but somehow I keep getting
the error "Association references unmapped class: IRole" when I call
the GenerateSchemaCreationScript method in NHibernate. When I replace
the type of the ILists with the implementation of the interfaces (User
and Role) everything works fine. What am I doing wrong here? How can I
make fluent use the implemented versions of IUser and IRole as defined
in Unity?
public interface IRole
{
string Title { get; set; }
IList<IUser> Users { get; set; }
}
public interface IUser
{
string Email { get; set; }
IList<IRole> Roles { get; set; }
}
public class Role : IRole
{
public virtual string Title { get; set; }
public virtual IList<IUser> Users { get; set; }
}
public class User : IUser
{
public virtual string Email { get; set; }
public virtual IList<IRole> Roles { get; set; }
}
I use the following program to generate the DDL using the
GenerateSchemaCreationScript in NHibernate:
class Program
{
static void Main(string[] args)
{
var ddl = new NHibernateSessionManager();
ddl.BuildConfiguration();
}
}
public class NHibernateSessionManager
{
private ISessionFactory _sessionFactory;
private static IUnityContainer _container;
private static void InitContainer()
{
_container = new UnityContainer();
_container.RegisterType(typeof(IUser), typeof(User));
_container.RegisterType(typeof(IRole), typeof(Role));
}
public ISessionFactory BuildConfiguration()
{
InitContainer();
return
Fluently.Configure().Database(MsSqlConfiguration.MsSql2008
.ConnectionString("ConnectionString"))
.Mappings(m => m.AutoMappings.Add(
AutoMap.AssemblyOf<IUser>()))
.ExposeConfiguration(BuildSchema)
.BuildSessionFactory();
}
private void BuildSchema(Configuration cfg)
{
var ddl = cfg.GenerateSchemaCreationScript(new
NHibernate.Dialect.MsSql2008Dialect());
System.IO.File.WriteAllLines("Filename", ddl);
}
}
I am in the same situation as you. Having used the ClassMap before I know you can do this with Fluent but I had never used the AutoMapping feature before. I have successfully been able to do a one to one mapping with the AutoMapper using an IReferenceConvention (see previous SO post).
I have now hit the same problem as you where I have a one to many mapping which I am now having a problem with. There is an IHasManyConvention interface which I have started to look at but have had no luck as of yet.
Just because some thing is hard to do it doesn't make it wrong, mapping to interfaces defiantly has value and can easily be done in the raw nHibernate mapping files or by using Fluents ClassMap mapping files. I think once people start do more with AutoMapping feature there will be more blog posts.
EDIT
I have found an interim solution using an IAutoMappingOverride. Below is a rough example of what you need.
public class RoleAutoMappingOverride : IAutoMappingOverride<Role>
{
public void Override(AutoMapping<Role> mapping)
{
mapping.HasMany<User>( x => x.Users ).KeyColumn( "User_id" );
}
}
EDIT
A college of mine has worked out a better solution that uses conventions instead of the override. This covers how to do a single class but if you look at the SO post I mentioned before you can see how this could be made generic.
public class Foo : IHasManyConvention
{
public void Apply(IOneToManyCollectionInstance instance)
{
if (instance.ChildType == typeof(Role))
{
instance.Relationship.CustomClass<User>();
}
}
}
EDIT
I have now turned this and my other post into a blog post:
http://bronumski.blogspot.com/2011/01/making-fluent-nhibernate-automapper.html
You can't provide an interface as the type T in AssemblyOf<T>, you need to provide a concrete type. Or you could use the method that accepts an assemply:
.Mappings(m => m.AutoMappings.Add(
AutoMap.Assembly(myAssembly)))
Edit: The problem is that your classes contain collections of interface types instead of class type. I don't know if it's possible to automap interfaces in this manner. Also, I think there's rarely any value in using interfaces to specify domain objects.
I want to control my domain's interaction with a collection, so I thought I'd make the collection protected and provide a read-only wrapper around it so that the contents are visible, but I can ensure that items are not added directly to the collection.
So I have the following code:
public class MyClass
{
public virtual ICollection<Thread> Threads
{
get { return new ReadOnlyWrappedCollection<Thread>(this.ThreadsInternal); }
}
protected virtual ICollection<Thread> ThreadsInternal { get; private set; }
}
I tried this:
this.Map(c => c.Threads)
.Access.None();
The result was a MappingException: Could not determine type for: System.Collections.Generic.ICollection'1[[Thread]], mscorlib, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089, for columns: NHibernate.Mapping.Column(Threads)
I tried this:
this.HasMany(c => c.Threads)
.Access.None();
The result was an InvalidOperationException: Tried to add collection 'Threads' when already added
If I omit the mapping, I get PropertyNotFoundException: Could not find a setter for property 'Threads' in class 'MyClass'
How can I persuade NHibernate to ignore this property in the mapping? I'm using Fluent NHibernate, but please post examples in hbm too.
I don't think you can map an ICollection. Regardless, I'm following a similar pattern and I've found that the best way to map it is to map a private IList.
Class:
public class Invoice
{
private IList<InvoiceItem> _items;
public Invoice()
{
_items = new List<InvoiceItem>();
}
public virtual IEnumerable<InvoiceItem> Items
{
get { return _items; }
}
}
Mapping:
public class InvoiceMap : ClassMap<Invoice>
{
public InvoiceMap()
{
Table("Invoice");
HasMany(x => x.Items).KeyColumn("InvoiceId")
.Access.CamelCaseField(Prefix.Underscore)
.Cascade.AllDeleteOrphan()
.Inverse()
.AsBag().LazyLoad();
}
}
The key line in the mapping is .Access.CamelCaseField(Prefix.Underscore) which tells NHibernate to use the private field _items. Note that The collection can still be cast to IList, but you could wrap it in a read only collection if needed.
I have the following idea:
Business object implemented as interface or abstract class with certain properties as read only to all layers except the DAL layer. I also want my business objects in another assembly than the DAL (for testing purposes), so marking the properties is not an option for me.
Examples could be one to one relationships or other properties.
I have almost solved the issue by doing the following
abstract class User
{
public virtual long UserId {get; protected set;}
public virtual string Password {get; protected set;}
...
}
In the DAL:
public class DbUser : User
{
internal virtual void SetPassword(string password) {...}
}
I then map this using fluent as
ClassMap<User> {...}
SubclassMap<DbUser> {...}
The problem I get is that fluent tries to create a table named DbUser.
If I skip the SubclassMap and creates a DbUser object and tries to save it I get an "No persister for this object" error.
Is it possible to solve?
You could probably override what is done with Fluent
public class DbUser: IAutoMappingOverride<DbUser>
{
public void Override(AutoMapping<DbUser> mapping)
{
//tell it to do nothing now, probably tell it not to map to table,
// not 100% on how you'd do this here.
}
}
Or you could have an attribute
public class DoNotAutoPersistAttribute : Attribute
{
}
And in AutoPersistenceModelGenerator read for attribute in Where clause to exclude it.
Check would be something like
private static bool CheckPeristance(Type t) {
var attributes = t.GetCustomAttributes(typeof (DoNotAutoPersistAttribute), true);
Check.Ensure(attributes.Length<=1, "The number of DoNotAutoPersistAttribute can only be less than or equal to 1");
if (attributes.Length == 0)
return false;
var persist = attributes[0] as DoNotAutoPersistAttribute;
return persist == null;
}
Then it kind of depends how you're adding entities but you're probably adding via assembly so this might do it for you:
mappings.AddEntityAssembly(typeof(User).Assembly).Where(GetAutoMappingFilter);
....
...
private static bool GetAutoMappingFilter(Type t)
{
return t.GetInterfaces().Any(x => CheckPeristance(x)); //you'd probably have a few filters here
}