NHibernate 3 LINQ : How to filter IQueryable to select only objects of class T and its subclasses? - nhibernate

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>()

Related

Orchard CMS ISessionConfigurationEvents and 1:N, N:N Relationships?

I have spent many days trying to implement relationships within OrchardCMS 1.9.1 between my custom contentParts to no avail.
Strewn across the internet are many others trying to achieve the same thing, who have also failed; giving me the impression that it's impossible?
Though recently I read an article at: http://www.ideliverable.com/blog/isessionconfigurationevents that gave the impression that all things possible with Fluent Nhibernate should be possible within Orchard.
So I implemented:
public class DbMapping : ISessionConfigurationEvents
{
public void Created(FluentConfiguration cfg, AutoPersistenceModel defaultModel)
{
defaultModel.UseOverridesFromAssemblyOf<ProfilePartRecord>().Alterations(x => x.AddFromAssemblyOf<ProfileOverride>());
defaultModel.UseOverridesFromAssemblyOf<LocationPartRecord>().Alterations(x => x.AddFromAssemblyOf<LocationOverride>());
}
public void Prepared(FluentConfiguration cfg) { }
public void Building(Configuration cfg) { }
public void Finished(Configuration cfg) { }
public void ComputingHash(Hash hash) { }
}
public class LocationOverride : IAutoMappingOverride<LocationPartRecord>
{
public void Override(AutoMapping<LocationPartRecord> mapping)
{
//[ Profile ] <--> [ Location ]
//mapping.Id(x => x.Id, "LocationPartRecord_id"); //As it's not in the model due to being a contentPart, NH will throw an error because of such.
mapping.Map(x => x.Type);
mapping.Map(x => x.Name);
mapping.References(x => x.ProfilePartRecord, "ProfilePartRecord_id");
}
}
public class ProfileOverride : IAutoMappingOverride<ProfilePartRecord>
{
public void Override(AutoMapping<ProfilePartRecord> mapping)
{
//[ Profile ] 0.1 <---> N [ Location ]
//NEW
mapping.HasMany(x => x.Locations)
.Inverse()
//.KeyColumn("ProfilePartRecord_id")
.Cascade.All()
.ForeignKeyCascadeOnDelete()
.ForeignKeyConstraintName("FK_Location__Profile");
}
}
MODELS:
public class ProfilePartRecord : ContentPartRecord
{
public virtual string FirstName { get; set; }
public virtual string LastName { get; set; }
[CascadeAllDeleteOrphan]
public virtual IList<LocationPartRecord> Locations { get; set; }
public ProfilePartRecord()
{
Locations = new List<LocationPartRecord>();
}
}
public class LocationPartRecord : ContentPartRecord
{
public virtual string Type { get; set; }
public virtual string Name { get; set; }
//For HasMany
[CascadeAllDeleteOrphan]
public virtual ProfilePartRecord ProfilePartRecord { get; set; }
}
MIGRATION:
SchemaBuilder.CreateTable("ProfilePartRecord",
table => table
.ContentPartRecord()
//PK: ProfilePartRecord_id
.Column<string>("FirstName")
.Column<string>("LastName")
//System
.Column<DateTime>("CreatedAt")
);
ContentDefinitionManager.AlterPartDefinition("ProfilePart",
builder => builder.Attachable());
ContentDefinitionManager.AlterTypeDefinition("Profile", t => t
.WithPart(typeof(ProfilePart).Name)
.WithPart("UserPart")
);
ContentDefinitionManager.AlterTypeDefinition("User", t => t
.WithPart("ProfilePart")
);
SchemaBuilder.CreateTable("LocationPartRecord",
table => table
.ContentPartRecord()
//PK: LocationPartRecord_id
//FK:
.Column<int>("ProfilePartRecord_id")
.Column<string>("Type")
.Column<string>("Name")
//System
.Column<DateTime>("CreatedAt")
);
ContentDefinitionManager.AlterPartDefinition("LocationPart",
builder => builder.Attachable());
ContentDefinitionManager.AlterTypeDefinition("Location", type => type
.WithPart("CommonPart")
.WithPart("LocationPart")
.Creatable()
.Listable());
But alas, I still can't create a relationship between these two entities. I can do such via Migration, but this is very limited - as in - I can't set the relationship to Cascade.
Can anyone shed some light on whether this is possible, and if so, how? Thanks
This may not get you the whole way, but I believe it will help. One thing I have done for performance reasons as well as to establish relationships at the database level between my parts is to use the "CreateForeignKey" and "CreateIndex" in the Migration. Here is an example that should work for you
// Add foreign key
SchemaBuilder.CreateForeignKey(
"FK_LocationProfile",
"LocationPartRecord", new[] { "ProfilePartRecord_id" },
"ProfilePartRecord", new[] { "Id" });
// Add index
SchemaBuilder.AlterTable("LocationPartRecord",
table => table
.CreateIndex("IDX_ProfilePartRecord_Id", "ProfilePartRecord_Id")
);
With these relationships defined, I wonder if that will in any way impact the NHibernate work you are doing.
As for how we have done the overall goal I believe you are trying to achieve, you can monitor the "ProfilePart" "Delete" event in the "LocationPart" handler and apply your own cascading delete logic there to ensure that there are no "LocationPart" left around.

Fluent Nhibernate Mapping - one-to-many inside a Value Object?

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...

How to automap a collection of components with Fluent NHibernate?

All of my entities and value objects implement marker interfaces IEntity and IValueObject. I have set them up to be treated as components like so:
public override bool IsComponent(Type type)
{
return typeof(IValueObject).IsAssignableFrom(type);
}
public override bool ShouldMap(Type type)
{
return typeof(IEntity).IsAssignableFrom(type) || typeof(IValueObject).IsAssignableFrom(type);
}
Unfortunately, this does not seem to allow entities that have collections of value objects to be automapped as component collections. For example:
public class MyEntity : IEntity
{
public IList<MyValueObject> Objects { get; set; }
}
public class MyValueObject : IValueObject
{
public string Name { get; set; }
public string Value { get; set; }
}
Is there any way to define a convention such that, any time an IEntity has an IList of a type that implements IValueObject, it gets mapped as if I had specified:
HasMany(x => x.Objects)
.Component(x => {
x.Map(m => m.Name);
x.Map(m => m.Value);
});
What I don't want to do is have to manually do these overrides for every class and write out each property for the value object again and again.
Create a new class that inherits from HasManyStep (FluentNHibernate.Automapping.Steps).
Override the ShouldMap() method with something like :
return base.ShouldMap(member) && IsCollectionOfComponents(member)
Add your logic to :
public void Map(ClassMappingBase classMap, Member member)
{ ... }
Replace the default step with your new one :
public class MyMappingConfiguration : DefaultAutomappingConfiguration
{
public override IEnumerable<IAutomappingStep> GetMappingSteps(AutoMapper mapper, IConventionFinder conventionFinder)
{
var steps = base.GetMappingSteps(mapper, conventionFinder);
var finalSteps = steps.Where(c => c.GetType() != typeof(FluentNHibernate.Automapping.Steps.HasManyToManyStep)).ToList();
var idx = finalSteps.IndexOf(steps.Where(c => c.GetType() == typeof(PropertyStep)).First());
finalSteps.Insert(idx + 1, new MyCustomHasManyStep(this));
return finalSteps;
}
}
Note : You could also get the original source code of HasManyStep.cs and copy it to your project to introduce your custom logic.

dynamic-component fluent automapping

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

AutoMapping a Composite Element in Fluent Nhibernate

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())