why are the children eager loaded ?
This is the relation :
public class Order
{
private IList<Product> _Product;
public virtual IReadOnlyCollection<Product> Products { get => _Product.ToList(); }
}
public class Product
{
private IList<Item> _Item;
protected internal virtual IReadOnlyCollection<Item> Items { get => _Item.ToList(); }
}
public class Item
{
private string _name;
public string Name { get => _name; }
private decimal _quantity;
public decimal Quantity { get => _quantity; }
}
Some method that gets the aggregate from DB
void Get(aggregateId)
{
var q = await Session.GetAsync<TAggregate>(aggregateId, cancellationToken);
var y = q as Order;
if (NHibernateUtil.IsInitialized(y.Products)) /// => TRUE
}
The Configurations for Order and Product
OrderConfiguration :
mapping
.HasMany<Product>(Reveal.Member<Order>("Products"))
.Access.LowerCaseField(Prefix.Underscore)
.Cascade.AllDeleteOrphan()
.Not.KeyNullable()
.Not.KeyUpdate()
.LazyLoad();
ProductConfiguration
mapping
.HasMany<Item>(Reveal.Member<Product>("Items"))
.LazyLoad()
.Component(
composit =>
{
composit
.Map(instruction => instruction.Name)
.Column("Name")
.Not.Nullable();
composit
.Map(instruction => instruction.Quantity)
.Column("Quantity")
.Not.Nullable();
});
Why does NHibernate eager loads all the data instead of waiting for it to be loaded only if needed ?
How can this be made lazy ?
Your use of ToList() on the _Product and _Item collections is inadvertently triggering the lazy loading.
Related
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'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...
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 am currently using component maps like this:
public class UserMapping
{
public UserMapping()
{
Id(c => c.Id).GeneratedBy.HiLo("100");
Map(c => c.UserName);
Component(c => c.Country, CountryComponentMapping.Map);
}
}
public sealed class CountryComponentMapping
{
public static void Map(ComponentPart<Country> part)
{
part.Map(x => x.CountryName)
part.Map(x => x.CountryAlpha2)
}
}
I like this becuase I only have to define the mapping for the component/value object in one place.
How would I go about using the same semantics for a collection of the component? (e.g. lets assume we wanted to change this to a collection of countries on the user entity)
You can map this as a Component Collection. Unfortunately there is no overload to HasMany().Component() in Fluent NHibernate that allows you to specify that you want to use a derived class of ComponentMap. You can use a modification of your technique above though.
public sealed class UserMap : ClassMap<User> {
public UserMap() {
Id(c => c.Id).GeneratedBy.HiLo("100");
Map(x => x.Name);
HasMany(x => x.Countries).Component(CountryComponentMapping.Map);
}
}
public sealed class CountryComponentMapping {
public static void Map(CompositeElementBuilder<Country> part) {
part.Map(x => x.CountryName);
part.Map(x => x.CountryAlpha2)
}
}
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