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
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.
To start, yes, there are tons of similar questions here on Stack Overflow, but I've browsed them all and the problems that plague most of them look correct on my issue.
Basically, I'm trying to access an Object's Object via my query, and that could be the problem: is that not allowed? I can access the Id field, always, but any other member variable cannot be accessed.
Here are my Objects:
public class Logfiles
{
public virtual int Id { get; set; }
public virtual bool IsActive { get; set; }
public virtual DateTime LastReference { get; set; }
}
public class LogMerges
{
public virtual int Id { get; set; }
public virtual DateTime CreationDate { get; set; }
public virtual Logfiles Logfile { get; set; }
}
And here are my mappings:
class LogfilesMap : ClassMap<Logfiles>
{
public LogfilesMap()
{
Not.LazyLoad();
Table("logfiles");
Id(x => x.Id, "id").Not.Nullable().Unique().GeneratedBy.Identity().UnsavedValue(0);
Map(x => x.IsActive, "is_active").Not.Nullable().Default("true");
Map(x => x.LastReference, "last_reference").Not.Nullable();
}
}
class LogMergesMap : ClassMap<LogMerges>
{
public LogMergesMap()
{
Not.LazyLoad();
Table("logmerges");
Id(x => x.Id, "id").Not.Nullable().Unique().GeneratedBy.Identity().UnsavedValue(0);
Map(x => x.CreationDate, "creation_date").Not.Nullable();
References(x => x.Logfile, "logfile_id");
}
}
My table and columns have the names:
logfiles - id, is_active, last_reference
logmerges - id, creation_date, logfile_id
My code that I'm using to query:
var query = session.QueryOver<LogMerges>()
.Where(log => log.Logfile.IsActive == true);
IEnumerable<LogMerges> logmerges = query.List().OrderBy(c => c.CreationDate);
Performing the Query causes the error and generates:
NHibernate.QueryException: could not resolve property: Logfile.IsActive of: LogMerges
The only thing I can guess is that I'm not allowed to do the "log.Logfile.IsActive" chain of objects in these queries. It compiles just fine and I can't see why I wouldn't be able to do this. If I change it to:
var query = session.QueryOver<LogMerges>()
.Where(log => log.Logfile.Id == 0);
...the query goes through. However, if I try to access the other member variable:
var query = session.QueryOver<LogMerges>()
.Where(log => log.Logfile.LastReference == DateTime.Now);
...I get a similar "could not resolve property" error message.
So if it turns out that I CANNOT do the Object chain of "log.Logfile.IsActive", what's the proper way to implement it? Do I need to perform a bunch of JOINs to do this?
Thanks in advance for any help!
Yes, you do need do need to join on Logfile. Nhibernate is ultimately going to turn your QueryOver query into SQL, so accessing a property on a referenced table doesn't make sense. In other words, you couldn't write the SQL:
select
*
from
logmerges
where
logmerges.logfile.last_reference = ...
Obviously you'd get a syntax error here--you'd need to join to LogFile:
select
*
from
logmerges
inner join logfiles on logfiles.id = logmerges.logfile_id
where
logfiles.last_reference = ...
And so therefore you can't write QueryOver like that either.
var query = session.QueryOver<LogMerges>()
.JoinQueryOver(lm => lm.LogFile)
.Where(lf => lf.LastReference == DateTime.Now)
.List<LogMerges>();
I have the following classes:
public class PhoneModel
{
public virtual ModelIdentifier SupportModels
}
public class ModelIdentifier
{
public virtual string Name
public virtual IList<string> Values
}
This is how i mapped it:
mapping.Component(x => x.SuppoertedModel, y =>
{
y.Map(x => x.Name, "FAMILY_ID");
y.HasMany(x => x.Values).Element("VALUE").Table("SUPPORTEDMODULS")
}
2 tables were created:
PhoneModel
column "FAMILY_ID"
SUPPORTEDMODELS
column "VALUE", "PHONE_MODEL_ID"
The problem is that when I am adding values, it will not save it to the SUPPORTEDMODELS table:
var pm = new PhoneModel();
pm.SupportedModels.Name = "11"
pm.SupportedModels.Values.Add("34");
you are missing HasMany(x => x.Values).Cascade.AllDeleteOrphan()
maybe session.Flush() or transaction.Commit() is missing in the test code
btw: if the order of the Values is not important then change it to ICollection<string> and HasMany().AsSet() since this allows NH to optimise some things and code can not rely on the order which is not relyable when using sql.
Hi I'm struggling do refine/refactoring a domain model and trying to move logic from application services into my domain model. Now I'm stuck with a NHibernate issue.
The model is a WorkEvaluation class that contains a Questionaire Template with Questions and it also contains a collection of QuestionWeight classes. The thing is that WorkEvaluation class also has an important property HitInterval that belongs closed to the QuestionWeight collection in WorkEvaluation. The concept is that you conduct an evaluation by answering a lot of questions (the anserws are excluded in this example) and finaly you apply some weights (percent weights) that modify answer scores. That means you can make some questions more important and other less important. Hit interval is also a tuning parameter that you use when you calculate TOTAL WorkEvaluation score (including weight modifications) and the result is for example: Totalscore = 100, Hitinterval 5% than we get a totalinterval of 95-105 and can be used to match other evaluations.
Enough of background.
I Want to encapsulate both list of QuestionWeights and HitInterval in a Value Object QuestionScoreTuning since these belongs together and should be applied at the same time.
And I also want to add some business logic into QuestionScoreTuning that do not belongs to workEvaluation.
How do I map i Fluent Nhibernate a Value Object (Component) that has the one-to-many collection and HitInterval and the reference back? This is my current code:
public class WorkEvaluation : DomainBase<long>, IAggregateRoot
{
public void ApplyTuning(QuestionScoreTuning tuning)
{
QuestionScoreTuning = tuning;
//TODO Raise Domain Event WorkEvaluationCompleted -
// which should recalculate all group scores
}
public QuestionScoreTuning QuestionScoreTuning { get; protected set; }
}
public class QuestionScoreTuning : ValueObject
{
private IList<QuestionWeight> _questionWeights;
public QuestionScoreTuning(IList<QuestionWeight> listOfWeights, long hitInterval)
{
_questionWeights = listOfWeights;
HitInterval = hitInterval;
}
public long HitInterval { get; protected set; }
protected override IEnumerable<object> GetAtomicValues()
{
return _questionWeights.Cast<object>();
}
/// <summary>
/// A list of all added QuestionWeights for this WorkEvaluation
/// </summary>
public IList<QuestionWeight> QuestionWeights
{
get { return new List<QuestionWeight>(_questionWeights); }
protected set { _questionWeights = value; }
}
protected QuestionScoreTuning()
{}
}
public class QuestionWeight : DomainBase<long>, IAggregateRoot
{
public QuestionWeight(Question question, WorkEvaluation evaluation)
{
Question = question;
WorkEvaluation = evaluation;
}
public Weight Weight { get; set; }
public Question Question { get; protected set; }
public WorkEvaluation WorkEvaluation { get; protected set; }
public override int GetHashCode()
{
return (Question.GetHashCode() + "|" + Weight).GetHashCode();
}
protected QuestionWeight()
{}
}
Fluent Mappings:
public class WorkEvaluationMapping : ClassMap<WorkEvaluation>
{
public WorkEvaluationMapping()
{
Id(x => x.ID).GeneratedBy.Identity();
References(x => x.SalaryReview).Not.Nullable();
References(x => x.WorkEvaluationTemplate).Column("WorkEvaluationTemplate_Id").Not.Nullable();
Component(x => x.QuestionScoreTuning, m =>
{
m.Map(x => x.HitInterval, "HitInterval");
m.HasMany(x => x.QuestionWeights).KeyColumn("WorkEvaluation_id").Cascade.All();
});
}
}
public class QuestionWeightMapping : ClassMap<QuestionWeight>
{
public QuestionWeightMapping()
{
Not.LazyLoad();
Id(x => x.ID).GeneratedBy.Identity();
Component(x => x.Weight, m =>
{
m.Map(x => x.Value, "WeightValue");
m.Map(x => x.TypeOfWeight, "WeightType");
});
References(x => x.Question).Column("Question_id").Not.Nullable().UniqueKey(
"One_Weight_Per_Question_And_WorkEvaluation");
References(x => x.WorkEvaluation).Column("WorkEvaluation_id").Not.Nullable().UniqueKey(
"One_Weight_Per_Question_And_WorkEvaluation");
}
}
All I want to accomplish is to move collection of QuestionWeights and HitInterval into a Value Object (Component mapping) since these will still be inside db table WorkEvaluation.
P.S I've look at some example solution DDDSample.net (Eric Evans DDD example in c#) and they accomplished this with the Itinerary class that takes a list as ctor parameter and is mapped as a Cargo component. Difference is that example has a list of valueobjects Leg BUT Leg has references to Location which is an entity class.
Hopefully maybe someone knows how to accomplish this. Thanks in advance...
/Bacce
Well. I Finally solved it. Now my WorkEvaluation object can be Applied with a QuestionScoreTuning object (a valueobject) that contains the list of weight and hitinterval. This turns out great and if anyone want more info about having collections inside value objects and mapping them in fluent NH, please ask here with a comment. I can supply code examples...
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