I use NH 3.3.1.
Suppose simple classes:
public class TestBase
{
public virtual int Id { get; set; }
public virtual string A { get; set; }
}
public class Test : TestBase
{
public virtual string B { get; set; }
}
and mappings for them:
public sealed class TestBaseMap : ClassMap<TestBase>
{
public TestBaseMap()
{
this.Polymorphism.Explicit();
this.Id(a => a.Id).GeneratedBy.Identity();
this.Map(a => a.A);
}
}
public sealed class TestMap :SubclassMap<Test>
{
public TestMap()
{
this.Map(a => a.B);
}
}
Even with Polymorphism.Explicit() specified, NH still left joins Test when querying for TestBase.
var a = this.Session.Get<TestBase>(1);
I don't really need this joining 'cuz will have lots of subclasses.
I checked xml generated by fluent. it's ok, "explicit" clause is there. What am i doing wrong?
I guess that explicit polymorphism is only used in queries, not for session.Get. But I couldn't find any references for this.
Try to not query for the base class, but always have an concrete subclass (which is in most cases a better design anyway):
public abstract class TestBase
{
public virtual int Id { get; set; }
public virtual string A { get; set; }
}
public class TestA : TestBase
{
public virtual string B { get; set; }
}
public class TestB : TestBase
{
public virtual string B { get; set; }
}
var a = this.Session.Get<TestA>(1);
Ok. I've got it. As Stefan supposed, i made abstract TestBase. But since I really needed to query TestBase table without many left joins, i introduced stub class:
public class TestStub : TestBase
{
// nothing
}
This class is absolutely empty. Map:
public sealed class TestStubMap : SubclassMap<TestStub>
{
public TestStubMap()
{
this.Table("TestBase");
this.KeyColumn("Id");
}
}
Now I can query:
var a = this.Session.Get<TestStub>(1)
It produces only one join (TestBase join TestBase). So now i can get my TestBase from db without overhead. I don't like hacks, but if built-in logic doesn't work (polymorphism=explicit), what is left to do.
Related
When trying to map, I got this error:
Association references unmapped class: System.Object
My class:
public partial class MessageIdentifier
{
public virtual int ID { get; set; }
public virtual object Item { get; set; }
}
And the convention:
public class MyUsertypeConvention : IPropertyConvention
{
public void Apply(IPropertyInstance instance)
{
if (instance.Type.Name == "Object")
instance.CustomType<string>();
}
}
Kindly suggest how to map?
As a simple (quick, naive) solution - I would suggest to create and map real string property. And then let your setter and getter (or some AOP or listener) to do the "to/from string conversion":
public partial class MessageIdentifier
{
public virtual int ID { get; set; }
public virtual object Item
{
get { return ... my conversion from string; }
set { ItemString = ...my conversion to string; }
}
public virtual string ItemString { get; set; }
}
A smart and preferred (but a bit more challenging) is to create CustomType - which will hide that conversion and support REUSE. Check e.g. here
NHibernate Pitfalls: Custom Types and Detecting Changes
Creating and Testing a Custom NHibernate User Type
Not a satisfactory answer. It doesn't work with class that is generated from xsd by using XML. You can try the following:
public partial class MessageIdentifier
{
public virtual int ID { get; set; }
private object itemField;
public object Item
{
get { return this.itemField; }
set { this.itemField = value; }
}
}
I have the following database tables defined:
Club: Id, Name
Member: Id, Name
ClubMember: ClubId, MemberId
I have the following entity Classes defined:
public class Club() {
public virtual int Id { get; set; }
public virtual string Name { get; set; }
public virtual IList<Member> Members { get; set; }
}
public class Member() {
public virtual int Id { get; set; }
public virtual string Name { get; set; }
public virtual IList<Club> Clubs { get; set; }
}
I have the following overrides defined:
public class MemberOverride : IAutoMappingOverride<Member>
{
public void Override(AutoMapping<Member> mapping_)
{
mapping_
.HasManyToMany(x_ => x_.Clubs)
.ParentKeyColumn("MemberId")
.ChildKeyColumn("ClubId")
.Cascade.All()
.Table("ClubMembers");
}
}
public class ClubOverride : IAutoMappingOverride<Club>
{
public void Override(AutoMapping<Club> mapping_)
{
mapping_
.HasManyToMany(x_ => x_.Members)
.ParentKeyColumn("ClubId")
.ChildKeyColumn("MemberId")
.Inverse()
.Table("ClubMembers");
}
}
I can see from my overrides that the Inverse on the ClubOverride means you cannot do the following
session.Save(club.Members.Add(member));
but this works:
session.Save(member.Clubs.Add(club);
But it doesn't make logical sense. I want to be able to save either the club with members or member with clubs.
Am I trying to do something impossible with FluentNhibernate?
TIA
Yes, you're right, that's not possible. But it's not a question of FluentNhibernate, NHibernate works like that.
Only one side is the owner of the relation and on charge of adding elements.
From official documentation:
Changes made only to the inverse end of the association are not persisted. This means that NHibernate has two representations in memory for every bidirectional association, one link from A to B and another link from B to A. This is easier to understand if you think about the .NET object model and how we create a many-to-many relationship in C#:
You can create add or remove methods on your entities that will help accomplish this:
public class Club() {
public virtual int Id { get; set; }
public virtual string Name { get; set; }
private IList<Member> members;
public virtual IEnumerable<Member> Members { get { return members.Select(x => x); } }
public Club() {
members = new List<Member>();
}
public virtual void AddMember(Member member){
if (members.Contains(member))
return;
members.Add(user);
member.AddClub(this);
}
public virtual void RemoveMember(Member member){
if (!members.Contains(member))
return;
members.Remove(member);
member.RemoveClub(this);
}
}
I'm taking a stab at creating a Active Record implementation (I know about Castle's initiative, and it's very good) for another type of data provider (ESRIs geodatabases, using ESRIs .NET libraries) and I'm reaching something interesting.
I have a question nevertheless. I have my ActiveRecord classes which is like this:
public interface IActiveRecord<T> : IActiveRecord where T : class
{
T Create();
void Update(T record);
void Delete(T record);
}
public interface IActiveRecord
{
int ObjectId { get; set; }
bool Managed { get; }
bool IsValid { get; }
IObject EsriObject { get; set; }
IGeometry Geometry { get; set; }
void Save();
void Delete();
}
I have static Create methods, which go to DynamicProxy and generate me a proxy. But how I can enforce that the instance generated for a inheriting class is proxied too?
public class ActiveRecord<T> : IActiveRecord where T : IActiveRecord,new()
{
// protected constructors
public static T Create(IObject obj)
{
var record = Create();
record.EsriObject = obj;
return (T)record;
}
}
// inherited class
[Workspace(#"C:\teste.gdb")]
[Table("TB_PARCEL",Geometry=esriGeometryType.esriGeometryPolygon)]
public class Parcel : ActiveRecord<Parcel>,IParcel
{
[Field(4, "NM_PARCEL_ID", esriFieldType.esriFieldTypeString)]
public virtual string ParcelId { get; set; }
[Field(5, "NR_PARCEL_NO", esriFieldType.esriFieldTypeInteger)]
public virtual int StreetNumber { get; set; }
public virtual IOwner ParcelOwner { get; set; }
}
Take a look at the tests. The first three tests get intercepted as usual, but not the fourth test. I need A) prevent the user from instancing it's own classes (bad approach for the API in my opinion) or find a way to return from the inherited classes constructors the proxies.
[TestMethod]
public void ActiveRecordConstructor()
{
Parcel p1 = Parcel.Create();
Assert.IsFalse(p1.Managed);
Assert.AreEqual(null, p1.ParcelId);
Parcel p2 = Parcel.Create(2);
Assert.IsFalse(p2.Managed);
IObject fake = _repository.StrictMock<IObject>();
using (_repository.Record())
{
fake.Stub(x => x.get_Value(4)).Return("teste");
}
using (_repository.Playback())
{
Parcel p3 = Parcel.Create(fake);
Assert.IsTrue(p3.Managed);
Assert.AreEqual("teste", p3.ParcelId);
}
// this wont be intercepted
Parcel p4 = new Parcel();
Assert.IsFalse(p4.Managed);
Assert.IsNull(p4.ParcelId);
}
In short I need that whenever a user creates a new Class(), it returns a proxied object. Is that possible while allowing inheritance?
Thanks!
DynamicProxy cannot intercept calls to constructors. It has to control the creation of the object.
I have abstract class Vehicle and two classes that derive from: Car and ForkLift.
public abstract class Vehicle
{
public EngineBase Engine { get; set; }
}
public class Car : Vehicle
{
public GasEngine Engine { get; set; }
}
public class ForkLift : Vehicle
{
public ElectricEngine Engine { get; set; }
}
and Engine clasess:
public abstract class EngineBase
{
}
public class GasEngine : EngineBase
{
}
public class ElectricEngine : EngineBase
{
}
Engines are mapped with "table per class hierarchy". With Vehicles I want to use the same pattern.
How to map Engine class and derived with that Engine property?
How to do it with lazy-loading?
That code does not compile, which makes it improbable that you can map it.
Use a protected field in Vehicle and map it with an access strategy:
public abstract class Vehicle
{
protected Engine _engine;
}
In Fluent NHibernate this would be mapped:
References(x => x.Engine, "EngineId").Access.CamelCaseField(Prefix.Underscore);
Then the classes that extend Vehicle can cast it as needed:
public class Car : Vehicle
{
public GasEngine
{
get { return (GasEngine)_engine; }
set { _engine = Value; }
}
}
I've been trying for ages to find an example (because I can't get it to work myself) of the correct mapping for a one-to-many relationship on an abstract class of a table-per-subclass implementation, in fluent nHibernate.
An example below: I'm looking to map the list of Fines on the Debt abstract base class to the Fine class.
If anyone knows of any tutorial or example they've come across before please let me know.
public abstract class Entity
{
public int Id { get; set; }
}
public abstract class Debt : Entity
{
public decimal Balance { get; set; }
public IList<Fine> Fines { get; set; }
public Debt()
{
Fines = new List<Fine>();
}
}
public class CarLoan : Debt
{
}
public class CreditCard : Debt
{
}
public class LoanApplication : Entity
{
public IList<Debt> ExistingDebts { get; set; }
public LoanApplication()
{
ExistingDebts = new List<Debt>();
}
}
public class Fine
{
public Int64 Cash { get; set; }
}
Can you tell us where exactly you're having difficulty? What have you tried?
Obviously, you'll need to declare all of your members as virtual (I assume this was an oversight in the example).
Basically, though, it would look like this:
public DebtMap : ClassMap<Debt>
{
public DebtMap()
{
Id(x => x.Id);
HasMany(x => x.Fines);
}
}
public FineMap : ClassMap<Fine>
{
public FineMap()
{
Id(x => x.Id);
// map other members
}
}
public CarLoanMap : SubclassMap<CarLoan> { }
public CreditCardMap : SubclassMap<CreditCard> { }