Does anyone know how can we automatically map dynamic components using Fluent Automapping in NHibernate?
I know that we can map normal classes as components, but couldn't figure out how to map dictionaries as dynamic-components using fluent automapping.
Thanks
We've used the following approach successfully (with FluentNH 1.2.0.712):
public class SomeClass
{
public int Id { get; set; }
public IDictionary Properties { get; set; }
}
public class SomeClassMapping : ClassMap<SomeClass>
{
public SomeClassMapping()
{
Id(x => x.Id);
// Maps the MyEnum members to separate int columns.
DynamicComponent(x => x.Properties,
c =>
{
foreach (var name in Enum.GetNames(typeof(MyEnum)))
c.Map<int>(name);
});
}
}
Here we've mapped all members of some Enum to separate columns where all of them are of type int. Right now I'm working on a scenario where we use different types for the dynamic columns which looks like this instead:
// ExtendedProperties contains custom objects with Name and Type members
foreach (var property in ExtendedProperties)
{
var prop = property;
part.Map(prop.Name).CustomType(prop.Type);
}
This also works very well.
What I'm still about to figure out is how to use References instead of Map for referencing other types that have their own mapping...
UPDATE:
The case with References is unfortunately more complicated, please refer to this Google Groups thread. In short:
// This won't work
foreach (var property in ExtendedProperties)
{
var prop = property;
part.Reference(dict => dict[part.Name]);
}
// This works but is not very dynamic
foreach (var property in ExtendedProperties)
{
var prop = property;
part.Reference<PropertyType>(dict => dict["MyProperty"]);
}
That's all for now.
I got struggle with exactly the same problem. With fluent nHibernate we cannot map this but on my own I somehow was able to solve this. My solution is to build lambda expression on the fly and the assign this into object. For instance, lets say that:
Let my copy part of the site that Oliver refer:
DynamicComponent(
x => x.Properties,
part =>
{
// Works
part.Map("Size").CustomType(typeof(string));
// Works
var keySize = "Size";
part.Map(keySize).CustomType(typeof(string));
// Does not work
part.Map(d => d[keySize]).CustomType(typeof(string));
// Works
part.References<Picture>(d => d["Picture"]);
// Does not work
var key = "Picture";
part.References<Picture>(d => d[key]);
});
And we have this problem that we need to hardcode "Picture" in mapping. But somehow after some research I created following solution:
var someExternalColumnNames = GetFromSomewhereDynamicColumns();
'x' is a DynamicComponent callback in fluent Nhibernate e.g. (DynamicColumns): DynamicComponent(a => a.DynamicColumns, x => (...content of method below...))
foreach(var x in someExternalColumnNames)
{
if (x.IsReferenceToPerson == true)
{
var param = Expression.Parameter(typeof(IDictionary), "paramFirst");
var key = Expression.Constant(x.Name);
var me = MemberExpression.Call(param, typeof(IDictionary).GetMethod("get_Item"), new[] { key });
var r = Expression.Lambda<Func<IDictionary, object>>(me, param);
m.References<Person>(r, x.Name);
}
else
{
m.Map(x.Name)
}
}
//
// Some class that we want to reference, just an example of Fluent Nhibernate mapping
public class PersonMap : ClassMap<Person>
{
public PersonMap()
{
Table("Person");
Id(x => x.PersonId, "PersonId");
Map(x => x.Name);
}
}
public class Person
{
public virtual Guid PersonId { get; set; }
public virtual string Name { get; set; }
public Person()
{ }
}
Maybe it would be helpful
Related
TLDR version: I'm having trouble getting my DDD domain model to work with NHibernate. If my value object itself contains a collection of value objects, I can't assign a new value without getting an NHibernate exception, and want to know what the best practice is in this situation.
Longer version:
Say I have an entity which contains a value object as a property, ValueObjectA, which itself contains a set of a different value objects of type ValueObjectB.
ValueObjectB only exists meaningfully as a property of ValueObjectA, i.e. if myEntity.ValueObjectA == null, it doesn't make sense for ValueObjectB to exist either.
I've written some example code to illustrate what I mean, with simplifications for brevity.
public class Entity
{
public int Id { get; private set; }
public ValueObjectA ValueObjectA { get; set; }
// Constructor: public Entity(ValueObjectA valueObjectA)
}
public class ValueObjectA : IEquatable<ValueObjectA>
{
public string X { get; private set; }
public ISet<ValueObjectB> ValueObjectBs { get; private set; }
// Constructor: public ValueObjectA(string x, ISet<ValueObjectB> valueObjectBs)
// Implementation of Equals/GetHahcode
}
public class ValueObjectB : IEquatable<ValueObjectB>
{
public int Y { get; private set; }
public int Z { get; private set; }
// Constructor: public ValueObjectB(int y, int z)
// Implementation of Equals/GetHahcode
}
I have a corresponding mapping class using mapping by code:
public class EntityMap : ClassMapping<Entity>
{
public EntityMap()
{
Table("Entity");
Id(x => x.Id, map => map.Generator(Generators.Identity));
Component(x => x.ValueObjectA, c =>
{
c.Property(x => x.X);
// Component relation is equilavent to <composite-element> in xml mappings
c.Set(x => x.ValueObjectBs, map =>
{
map.Table("ValueObjectB");
map.Inverse(true);
map.Cascade(Cascade.All | Cascade.DeleteOrphans);
map.Key(k => k.Column("Id"));
}, r => r.Component(ce =>
{
ce.Property(x => x.Y);
ce.Property(x => x.Z);
}));
});
}
}
The properties of ValueObjectA are mapped to the Entity table, but the properties of ValueObjectA.ValueObjectB are mapped to another table, since it is a one to many relationship. When a ValueObjectB is removed, I want that row to be deleted in the ValueObjectB table.
Since value objects are immutable, when I change the properties of entity.ValueObjectA, I should create a new instance of ValueObjectA. The problem is that the set of ValueObjectBs is a reference type, so when I try to save the entity with a different ValueObjectA, NHibernate will throw an exception because the original set that NHibernate is tracking is no longer referenced:
A collection with cascade="all-delete-orphan" was no longer referenced
by the owning entity instance.
Consider the following code:
var valueObjectBs_1 = new HashSet<ValueObjectB>
{
new ValueObjectB(1, 2),
new ValueObjectB(3, 4)
};
var valueObjectA_1 = new ValueObjectA("first", valueObjectBs_1);
var entity = new Entity(valueObjectA_1);
// Save entity, reload entity
var valueObjectBs_2 = new HashSet<ValueObjectB>
{
new ValueObjectB(1, 2)
};
var valueObjectA_2 = new ValueObjectA("second", valueObjectBs_2);
entity.ValueObjectA = valueObjectA_2;
// Save entity again
// NHIBERNATE EXCEPTION
I've managed to get around this by creating another ValueObjectA in order to preserve the reference to the set, e.g.
valueObjectA_1.ValueObjectBs.Remove(new ValueObjectB(3, 4));
entity.ValueObjectA = new ValueObjectA(valueObjectA_2.X, valueObjectA_1.ValueObjectBs);
However... that feels like a code smell - even if I wrote a custom setter for Entity.ValueObjectA, the implementation is starting to get complicated where the design is supposed to be simple.
public class Entity
{
// ...
private ValueObjectA valueObjectA;
public ValueObjectA ValueObjectA
{
// get
set
{
// Add/Remove relevant values from ValueObjectA.ValueObjectBs
valueObjectA = new ValueObjectA(value.X, ValueObjectA.ValueObjectBs);
}
}
}
What is the best practice in this type of situation? Or is this a sign that I'm trying to do something which violates the principles of DDD?
What you have is an anemic domain model.
You should replace public setters of the entity with methods that have meaningful names from the Ubiquitous language, that check the invariants and that do all the necessary cleanup in case of value objects replacements.
Although it may seem that things are more complicated this is payed back by the fact the now the entity is in full control about what happens with its internals. You now have full encapsulation.
I need a way to query in Nhibernate for items that have a Dictionary Property containing value.
Assume:
public class Item
{
public virtual IDictionary<int, string> DictionaryProperty {get; set;}
}
and mapping:
public ItemMap()
{
HasMany(x => x.DictionaryProperty)
.Access.ReadOnlyPropertyThroughCamelCaseField(Prefix.Underscore)
.AsMap<string>(
index => index.Column("IDNumber").Type<int>(),
element => element.Column("TextField").Type<string>().Length(666)
)
.Cascade.AllDeleteOrphan()
.Fetch.Join();
}
I want to query all Items that have a dictionary value of "SomeText". The following example in Linq fails:
session.Query<Item>().Where(r => r.DictionaryProperty.Any(g => g.Value == "SomeText"))
with error
cannot dereference scalar collection element: Value
So is there any way to achieve that in NHibernate? Linq is not an exclusive requirement but its preffered. Not that I'm not interested to query over dictionary keys that can be achieved using .ContainsKey . Φορ this is similar but not the same
Handling IDictionary<TValueType, TValueType> would usually bring more issues than advantages. One way, workaround, is to introduce a new object (I will call it MyWrapper) with properties Key and Value (just an example naming).
This way we have to 1) create new object (MyWrapper), 2) adjust the mapping and that's it. No other changes... so the original stuff (mapping, properties) will work, because we would use different (readonly) property for querying
public class MyWrapper
{
public virtual int Key { get; set; }
public virtual string Value { get; set; }
}
The Item now has
public class Item
{
// keep the existing for Insert/Updae
public virtual IDictionary<int, string> DictionaryProperty {get; set;}
// map it
private IList<MyWrapper> _properties = new List<MyWrapper>();
// publish it as readonly
public virtual IEnumerable<MyWrapper> Properties
{
get { return new ReadOnlyCollection<MyWrapper>(_properties); }
}
}
Now we will extend the mapping:
HasMany(x => x.Properties)
.Access.ReadOnlyPropertyThroughCamelCaseField(Prefix.Underscore)
.Component(c =>
{
c.Map(x => x.Key).Column("IDNumber")
c.Map(x => x.Value).Column("TextField")
})
...
;
And the Query, which will work as expected:
session
.Query<Item>()
.Where(r =>
r.Properties.Any(g => g.Value == "SomeText")
)
NOTE: From my experience, this workaround should not be workaround. It is preferred way. NHibernate supports lot of features, but working with Objects brings more profit
Hi i have setup my SessionFactory to cache entities and queries:
private ISessionFactory CreateSessionFactory()
{
var cfg = new Configuration().Proxy(
properties => properties.ProxyFactoryFactory<DefaultProxyFactoryFactory>()).DataBaseIntegration(
properties =>
{
properties.Driver<SqlClientDriver>();
properties.ConnectionStringName = this.namedConnection;
properties.Dialect<MsSql2005Dialect>();
}).AddAssembly(this.resourceAssembly).Cache(
properties =>
{
properties.UseQueryCache = true;
properties.Provider<SysCacheProvider>();
properties.DefaultExpiration = 3600;
});
cfg.AddMapping(this.DomainMapping);
new SchemaUpdate(cfg).Execute(true, true);
return cfg.BuildSessionFactory();
}
This is my user mapping
public class UserMapping : EntityMapping<Guid, User>
{
public UserMapping()
{
this.Table("USERS");
this.Property(
x => x.CorpId,
mapper => mapper.Column(
c =>
{
c.Name("CorporateId");
c.UniqueKey("UKUserCorporateId");
c.NotNullable(true);
}));
this.Set(
x => x.Desks,
mapper =>
{
mapper.Table("DESKS2USERS");
mapper.Key(km => km.Column("UserId"));
mapper.Inverse(false);
mapper.Cascade(Cascade.All | Cascade.DeleteOrphans | Cascade.Remove);
},
rel => rel.ManyToMany(mapper => mapper.Column("DeskId")));
this.Cache(
mapper =>
{
mapper.Usage(CacheUsage.ReadWrite);
mapper.Include(CacheInclude.All);
});
}
}
What I want to do is get a user or query some users and add information to the domain object and cache the updated object.
public class User : Entity<Guid>, IUser
{
public virtual string CorpId { get; set; }
public virtual ISet<Desk> Desks { get; set; }
public virtual MailAddress EmailAddress { get; set; }
public virtual string Name
{
get
{
return string.Format(CultureInfo.CurrentCulture, "{0}, {1}", this.SurName, this.GivenName);
}
}
public virtual string GivenName { get; set; }
public virtual string SurName { get; set; }
}
something like this:
var users = this.session.Query<User>().Cacheable().ToList();
if (users.Any(user => user.EmailAddress == null))
{
UserEditor.UpdateThroughActiveDirectoryData(users);
}
return this.View(new UserViewModel { Users = users.OrderBy(entity => entity.Name) });
or this:
var user = this.session.Get<User>(id);
if (user.EmailAddress == null)
{
UserEditor.UpdateThroughActiveDirectoryData(user);
}
return this.View(user);
The UpdateThroughActiveDirectory methods work but are executed everytime i get data from the cache, the updated entities do not keep the additional data. Is there a way to also store this data in nhibernates 2nd level cache?
NHibernate doesn't cache entire entity in second level cache. It caches only the state / data from the mapped properties. You can read more about it here: http://ayende.com/blog/3112/nhibernate-and-the-second-level-cache-tips
There's an interesting discussion in comments of that post that explains this a little further:
Frans Bouma: Objects need to serializable, are they not? As we're talking about multiple appdomains. I wonder what's more
efficient: relying on the cache of the db server or transporting
objects back/forth using serialization layers.
Ayende Rahien: No, they don't need that. This is because NHibernate doesn't save the entity in the cache. Doing so would open
you to race conditions. NHibernate saves the entity data alone,
which is usually composed of primitive data (that is what the DB can
store, after all). In general, it is more efficient to hit a cache
server, because those are very easily scalable to high degrees, and
there is no I/O involved.
I want to upgrade my application to use NHiberante 3 instead of NHibernate 2.1.2 but faced some problems with the new LINQ provider. This question is about one of them. Assume that I have a following hierarchy of classes:
public abstract class PageData
{
public int ID { get; set; }
public string Title { get; set; }
}
public class ArticlePageData : PageData
{
public DateTime PublishedDate { get; set; }
public string Body { get; set; }
}
public class ExtendedArticlePageData : ArticlePageData
{
public string Preamble { get; set; }
}
I use Fluent NHibernate to map these classes to the database:
public class PageDataMap : ClassMap<PageData>
{
public PageDataMap()
{
Table("PageData");
Id(x => x.ID);
Map(x => x.Title);
DiscriminateSubClassesOnColumn("PageType");
}
}
public class ArticlePageDataMap : SubclassMap<ArticlePageData>
{
public ArticlePageDataMap()
{
Join("ArticlePageData", p =>
{
p.KeyColumn("ID");
p.Map(x => x.PublishedDate);
p.Map(x => x.Body);
});
}
}
public class ExtendedArticlePageDataMap : SubclassMap<ExtendedArticlePageData>
{
public ExtendedArticlePageDataMap ()
{
Join("ExtendedArticlePageData", p =>
{
p.KeyColumn("ID");
p.Map(x => x.Preamble);
});
}
}
And then I want to query all pages and do some filtering:
IQueryable<PageData> pages = session.Query<PageData>();
...
var articles = pages.OfType<ArticlePageData>().Where(x => x.PublishedDate >= (DateTime.Now - TimeSpan.FromDays(7))).ToList();
NHibernate 3.0.0 fails with the NotSupported exception in this case, but there is bugfix NH-2375 in the developing version of NH which leads this code to work. But, unfortunately, OfType() method filters the objects by exact type and only selects objects of ArticlePageData class. The old Linq to NH provider selects ArticlePageData and ExtendedArticlePageData in the same case.
How can I do such filtering (select only objects of class T and its subclasses) with the new Linq to NH provider?
session.Query<T>().OfType<SubT>() makes little sense, and it won't let you filter on properties of the subclass. Use session.Query<SubT>() instead.
You can use
var articles = pages.AsEnumerable().OfType<ArticlePageData>().Where(x => x.PublishedDate >= (DateTime.Now - TimeSpan.FromDays(7))).ToList();
and wait for NHibernate 3.0.1.
or maybe you can use
session.Query<ArticlePageData>()
instead of
session.Query<PageData>()
I'm trying to get the AutoPersistence model to map several composite elements. However, it seems that either I end up mapping it as an entity, dropping down to manual mapping or it just doesn't plain work. Here's some code that demonstrates my problem:
using System;
using System.Collections.Generic;
using FluentNHibernate.AutoMap;
using FluentNHibernate.Cfg;
using FluentNHibernate.Conventions.Helpers;
using NHibernate.Cfg;
namespace Scanner {
public class Root {
public int Id { get; set; }
public string Name { get; set; }
public ICollection<Component> Components { get; set; }
}
public class Component {
public string Name { get; set; }
}
class Example {
public void DoesntGetComponents()
{
Configuration configuration = new Configuration();
configuration.SetProperty("ConnectionString", "");
configuration.SetProperty("dialect", "NHibernate.Dialect.MsSql2005Dialect");
var config = Fluently.Configure(configuration)
.Mappings(m => m.AutoMappings.Add(AutoMapping))
.BuildConfiguration();
var sql2005 = new NHibernate.Dialect.MsSql2005Dialect();
foreach (var line in config.GenerateSchemaCreationScript(sql2005))
{
Console.WriteLine(line);
}
}
static AutoPersistenceModel AutoMapping() {
AutoPersistenceModel model = new AutoPersistenceModel();
return model
.AddEntityAssembly(typeof(Root).Assembly)
.WithSetup(e => e.IsComponentType = t => t == typeof(Component))
.Where(t => t == typeof(Root))
.MergeWithAutoMapsFromAssemblyOf<Root>()
.ConventionDiscovery.Add(ForeignKey.Format((p, t) => (p == null ? t.Name : p.Name) + "Id"))
.ConventionDiscovery.Add(Table.Is(t => t.EntityType.Name))
;
}
}
}
(Sorry it's so long, but it's the minimal code required to demonstrate the problem. This particular version of the code fails to register the component type at all.
So, what am I doing wrong?
It seems that the component in itself is not the problem, but the mapping of a collection of components. If you would map the component directly onto the Root class, this would not be any problem.
A possible workaround is making the Component class an entity (adding an ID) and overriding the mapping of Components to cascade + auto delete orphans:
AutoPersistenceModel
.ForTypesThatDeriveFrom<Root>(map => map.HasMany(root => root.Components).Cascade.AllDeleteOrphan())