Mapping the reverse of a ReferencesAny? - nhibernate

I have two entity classes (with a common interface, for easy automapping), where one has a generic reference to the other:
public interface IModelClass
{
Guid Id { get; set; }
}
class Order : IModelClass
{
public virtual Guid Id { get; set; }
public virtual ISet<Attachment> Attachments { get; set; }
}
class Attachment : IModelClass
{
public virtual Guid Id { get; set; }
public virtual IModelClass AttachedTo { get; set; }
}
I can define the generic reference with ReferencesAny:
mapping.ReferencesAny(x => x.AttachedTo)
.EntityTypeColumn("entity_type")
.EntityIdentifierColumn("entity_id")
.IdentityType<Guid>()
.MetaType<string>()
.AddMetaValue<Order>("ORDER")
.Cascade.None();
And this works fine, as long as I only work with attachments directly and don't try to do things from the Order side. What I want though, is to map the reverse of the relationship - i.e. what would normally be handled by:
mapping.HasMany(x => x.Attachments)
so that I can, say, add attachments to the Order's collection and get them persisted without having to manually go through and set up/save each of the attachments (and I'd have to start passing the session around to get the attachments committed in the same transaction). This doesn't work - it just works normally and creates an order_id column on the attachment table.
Is there some way to set up this mapping so that it 'works'?

mapping.HasMany(x => x.Attachments)
.KeyColumn("entity_id") // to give it the right column to use
.Where("entity_type = 'ORDER'") // makes sure that only attachments for the order are loaded
.Inverse()
.Cascade.AllDeleteOrphan(); // or .Cascade.All(); if Attachments should stay without

Related

Fluent Nhibernate: Cant delete an item with as HasMany property

I dont normally deal with data like this but I thought id give it a try. As it turned out I failed :/ and am not sure how to proceed.
I have a database object track:
public virtual string Type { get; set; }
public virtual IList<DateTypeTrack> TrackDates { get; set; }
With a mapping file:
Table("Tracks");
Map(x => x.Type).Not.Nullable();
HasMany(x => x.TrackDates).KeyColumn("TrackID").Cascade.All();
The DateTypeTrack Object looks like this:
public virtual DateType DateType { get; set; }
public virtual Track Track { get; set; }
public virtual int Days { get; set; }
With a mapping file like this:
Table("DateTypeTracks");
References(x => x.DateType, "DateTypeID").Not.Nullable();
References(x => x.Track, "TrackID").Not.Nullable();
Map(x => x.Days).Not.Nullable();
If its necessary, Ill post the DateType code aswell, but I dont think its needed.
And am trying to write a delete method in my service layer that is pretty simple:
public void PushDelete(int id)
{
Track track = _tracks.Get(id);
try
{
_tracks.BeginTransaction();
_tracks.Delete(track);
_tracks.CommitTransaction();
}
catch (Exception)
{
_tracks.RollbackTransaction();
throw;
}
}
I keep getting an error:
could not delete collection: [TSE.Domain.DatabaseObjects.Track.TrackDates#12][SQL: UPDATE DateTypeTracks SET TrackID = null WHERE TrackID = #p0]
I dont know why its trying to do the update at the end, but I suppose that is what is causing the issue. What sort of recourse do I have?
Thanks.
since the DateTypeTrack already cares for the association between the two entities you should mark the HasMany as Inverse to tell NH that the hasmany does not maintain it (the Update)
HasMany(x => x.TrackDates).KeyColumn("TrackID").Cascade.All().Inverse();

Automapper and NHibernate lazy loading

I am struggling with this issue:
I have a list of NHibernate objects called "Project". These objects contain a lazy - loaded list of "Branches". I am trying to pass a list of Projects to a WCF service so I am using AutoMapper to transform them to flat objects.
The problem is that even though the destination objects called "ProjectContract" does not contain a list of Branches, Automapper still invokes this collection and a lot of queries are made to the database because NHibernate fires the lazy - loading and loads the Branches collection for each project.
Here are the classes and the mapping:
public class Project
{
public virtual int ID
{
get;
set;
}
public virtual string Name { get; set; }
public virtual string Description { get; set; }
public virtual IList<Branch> Branches { get; set; }
}
[DataContract]
public class ProjectContract
{
[DataMember]
public virtual int ID
{
get;
set;
}
[DataMember]
public virtual string Name { get; set; }
[DataMember]
public virtual string Description { get; set; }
}
public class ProjectMappings : Profile
{
protected override void Configure()
{
Mapper.CreateMap<Project, ProjectContract>();
}
}
My question is: Is there a way to tell AutoMapper to not touch the "Branches" collection because I don't care about it and that is a proxy that will trigger many database calls?
I temporarily fixed this with MaxDepth(0), but there are other entities where I have collections that I want to transfer, and collections that I don't want to be touched, like this one. In that case, MaxDepth(0) will not work.
Thank you,
Cosmin
Yes, The AutoMapper Ignore function.
Mapper.CreateMap<Source, Destination>()
.ForMember(dest => dest.SomeValuefff, opt => opt.Ignore());

Fluent NHibernate Adding and Updating problem : References

Im fairly n00bish when it comes to fluent nhibernate but i have an unexpected error in one of my repositories.
I have a datatype CostCode
public class CostCode
{
public virtual int Id { get; set; }
public virtual String CostCodeCode { get; set; }
public virtual Company Company { get; set; }
public virtual DateTime CreatedDate { get; set; }
public virtual String CreatedBy { get; set; }
public virtual DateTime ModifiedDate { get; set; }
public virtual String ModifiedBy { get; set; }
}
and here is the mapping
public sealed class CostCodeMap : ClassMap<CostCode>
{
/**
* #breif Mapping Constructor
*/
public CostCodeMap()
{
Id(Reveal.Member<CostCode>("Id"));
Map(x => x.CostCodeCode).Not.Nullable();
References(x => x.Company, "CompanyId").Cascade.All();
Map(x => x.CreatedDate).Not.Nullable();
Map(x => x.CreatedBy).Not.Nullable();
Map(x => x.ModifiedDate).Not.Nullable();
Map(x => x.ModifiedBy).Not.Nullable();
}
}
When i try to update this, i get an error "identifier of an instance of Domain.DataTypes.Company was altered from 1 to 8"
Now i think its the way that i set up the mapping, and possibly how my repository is handling the updates/adds.
I have a drop down list that controls the id of the company, and when im adding/updating i set the property company to whatever is in the database for the id that it has been updated to.
var companyRepository= new CompanyRepository(_session);
temp.Company = companyRepository.GetCompanyById(temp.Company.Id);
_session.Update(c);
Can anyone give me a hint/solution to help me on my way? Looking through related problems here, the problem could be anything.
Ok, I will just throw this out... I bet what is happening is you are setting temp.Company.Id by changing the Id, then you use the repo to go fetch that company using the changed Id. NHibernate will track that you changed the Id on the other company however. Use a temp var to store that new company id, dont change the id of the other company.
I'm not 100% sure, but it really looks like maybe there is a bug here:
temp.Company = ...(temp.Company.Id);
I would figure you'd actually be pulling that from an incoming parameter.
Also, you can avoid a database hit here by using Session.Load():
temp.Company = _session.Load<Company>(passedInCompanyId);

Mapping a backing field, that has a different type from the respective property, using Fluent NHibernate

I need to persist this class on database using Fluent NHibernate:
public class RaccoonCity
{
public virtual int Id { get; private set; }
public virtual DateTime InfectionStart { get; private set; }
private IList<Zombie> _zombies = new List<Zombie>();
public virtual IEnumerable<Zombie> Zombies
{
get { return _zombies; }
}
protected RaccoonCity()
{}
public RaccoonCity(DateTime startMonth)
{
InfectionStart = startMonth;
}
public virtual void AddZombie(Zombie z)
{
_zombies.Add(z);
}
}
The property has type IEnumerable to indicate that you shouldn´t use it to insert new items. The backing field is of IList to make it easy to insert new items from the own class.
Zombie is a simple class:
public class Zombie
{
public virtual int Id { get; private set; }
public virtual string FormerName { get; set; }
public virtual DateTime Infected { get; set; }
}
The map is the following:
public class RaccoonCityMap: ClassMap<RaccoonCity>
{
public RaccoonCityMap()
{
Id(x => x.Id);
Map(x => x.InfectionStart);
HasMany(x => x.Zombies)
.Access.CamelCaseField(Prefix.Underscore)
.Inverse()
.Cascade.All();
}
}
When I test this, the data is inserted in database, but the zombie´s foreign keys are empty, and the RaccoonCity instance has zero items on Zombies list.
You are declaring the relationship as Inverse, which means the Zombie and not the RacoonCity is responsible for maintaining the relationship.
Either add the corresponding reference to zombie and set it on the AddZombie method, or remove the Inverse (in that case, you'll see an INSERT with a null FK followed by an update).
Suggested reading: http://nhibernate.info/doc/nh/en/index.html#collections-onetomany
Found a post about it: https://web.archive.org/web/20090831052429/http://blogs.hibernatingrhinos.com/nhibernate/archive/2008/08/15/a-fluent-interface-to-nhibernate-part-3-mapping.aspx
I had to implement the method
HasManyComponent by myself since it
was missing in the actual trunk of the
framework. That is, it was not
possible to map a collection of value
objects. But it has not been that hard
since the source base is really nice.
My changes will probably be integrated
into the framework soon.
And this one:
http://nhforge.org/blogs/nhibernate/archive/2008/09/06/a-fluent-interface-to-nhibernate-part-3-mapping-relations.aspx

How to use Fluent NHibernate in N-Tier application?

I'm trying to adopt Fluent NHibernate with my project, currently I can get data from database, when I'm at application server, data is include its PK but when I return this data (as List) to client all of its PK is loose.
How can I fixed this problem?
Update
My POCO class is below: PKs are CountryCd and CityCd
public class coCity
{
public virtual string CountryCd { get; private set; }
public virtual string CityCd { get; private set; }
public virtual string CityNameTH { get; set; }
public virtual string CityNameEN { get; set; }
public virtual int DeliveryLeadTime { get; set; }
public virtual string CreateBy { get; set; }
public virtual DateTime CreateDate { get; set; }
public virtual string UpdateBy { get; set; }
public virtual DateTime UpdateDate { get; set; }
public override bool Equals(object obj)
{
return this.GetHashCode().Equals(obj.GetHashCode());
}
public override int GetHashCode()
{
return (this.CountryCd + this.CityCd).GetHashCode();
}
}
Mapping class:
public class coCityMap : ClassMap<coCity>
{
public coCityMap()
{
Table("coCity"); // this is optional
CompositeId()
.KeyProperty(x => x.CountryCd)
.KeyProperty(x => x.CityCd);
Map(x => x.CityNameTH);
Map(x => x.CityNameEN);
Map(x => x.DeliveryLeadTime);
Map(x => x.CreateBy);
Map(x => x.CreateDate);
Map(x => x.UpdateBy);
Map(x => x.UpdateDate);
}
}
Source code to get data at application server
public List<coCity> GetTest()
{
List<coCity> result = new List<coCity>();
var sessionFactory = CreateSessionFactory();
using (var session = sessionFactory.OpenSession())
{
result = (List<coCity>)session.CreateCriteria(typeof(coCity)).List<coCity>();
}
return result;
}
When its still at application server data is retrieve correctly as image below
alt text http://img138.imageshack.us/img138/1071/serverside.png
However when this data transit back to client side all of its PKs is loose like below.
alt text http://img203.imageshack.us/img203/1664/clientside.png
First of all, this isn't a problem with Fluent NHibernate so:
Serializable must be used on your POCO's when you serialize them.
(from your comment) NHibernate keeps a reference of the object retrieved from the database to a cache (1-st level cache). While you serialize this 'managed' object the output of the serialization is an unmanaged object. Nhibernate does not detect that a an object exists in the db just because you set an value in a newly constructed object. You must get the object from the database and update its properties and call Update() or you work with pure sql with the object that returned from the client (yikes!).
Note that is irrelevant with this question: your Equals() implementation is really bad as it doesn't take into account types and depends only on GetHashCode value. If all your classes have this implementation you could run into trouble.
I think the problem is with that private setter on the PK's properties. Try changing that to public.
Either way, mark your entity with Serializable
A few comments:
As a general recomendation when using nhibernate is to avoid composite Ids. Create on your model a surrogate Id that is an identity column and enforce uniqueness of CityCd and CountryCd somewhere else
When passing data around client/server tiers, consider using DTOs to avoid some commong LazyInitializationExceptions problems.