I have been using joinedsubclass inheritence mapping for a while and decided to switch to table-per-class because it allows more efficient subtype descrimination. However on making the change I now get the below exception.
Object with id: 1 was not of the specified subclass: MyModel.FooBase (Discriminator was: '2')
Abridged classes are as follows.
public abstract FooBase
{
/* snip other properties */
public virtual string Name { get;set; }
public virtual IList<Child> Children { get; set; }
}
public class Bar : FooBase
{
public virtual string NickName { get;set; }
}
public class Child
{
public virtual FooBase Parent { get;set; }
}
And the mappings
public sealed class ChildMap : ClassMap<Child>
{
public ChildMap()
{
/*snip*/
References(x => x.Parent).ColumnName("ParentId");
}
}
public sealed class FooBaseMap : ClassMap<FooBase>
{
public FooBaseMap()
{
/*snip*/
HasMany(x => x. Children)
.AsBag().Cascade
.AllDeleteOrphan()
.KeyColumnNames.Add("ParentId")
.Inverse();
DiscriminateSubClassesOnColumn<int>("FooType")
.SubClass<Bar>(Types.Bar, m => m.Map(x => x.NickName))
}
}
As it turns out this exception is normally seen on polymorphic collections not specifying a where attribute. Not the case here. I have played around with making the FooBase not abstract, this makes no difference. Didn't think changing mapping strategies would cause any problems other than having to have nullable columns on the subclasses.
The thing thats confusing is that I am not specifying a subclass as this is a polymorphic parent and I am only attempting to access the base properties in this case Name.
Many thanks,
Ok I found the issue as always right after asking the question :(
Anyway it was the mapping using an Enum as the descriminator that was the issue.
DiscriminateSubClassesOnColumn<int>("FooType")
.SubClass<Bar>(Types.Bar, m => m.Map(x => x.NickName))
Types is an enum I used to allow me to not forget the ints used as the descriminator (reason for ints is they are faster than strings in db lookups).
This issue implies you can use Enums directly as descriminators since 3892 so I tried.
DiscriminateSubClassesOnColumn<Types>("FooType")
.SubClass<Bar>(Types.Bar, m => m.Map(x => x.NickName))
Didn't work so I tried
DiscriminateSubClassesOnColumn<Types>("FooType")
.SubClass<Bar>("Bar", m => m.Map(x => x.NickName))
Also didn't work so I finally settled for the below which does work.
DiscriminateSubClassesOnColumn<int>("FooType")
.SubClass<Bar>((int)Types.Bar, m => m.Map(x => x.NickName))
Related
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.
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 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