NHibernate Criteria Sorting from one-to-many relationship (list property) - nhibernate

I have an Effort entity and a Schedule entity.
The Schedule entity represents the page on wich Efforts are displayed.
Each Effort can have a separate Order on each Schedule page.
So the best DB structure for this case would look like this
Effort -> ScheduleOrder(with position) <- Schedule
so the one-to-many and many-to-one.
Now I need to sort Efforts on page usig NHibernate criteria.
So I need to use something like property that returns Position for each Schedule.
I can acheave this by using DB View or Property with customised Select query.
Anyone has an idea how to do sorting from one-to-many relationship in a better way?

with
class ScheduleOrder
{
public virtual Effort Effort { get; set; }
public virtual Schedule Schedule { get; set; }
public virtual int Position { get; set; }
}
var efforts = session.CreateCriteria<ScheduleOrder>()
.Add(Expression.Eq("Schedule", schedule))
.AddOrder(Order.Asc("Position"))
.SetFetchMode("Effort", FetchMode.Eager)
.List()
.Select(so => so.Effort);
or with
class Schedule
{
// LIst has a position
public virtual IList<Effort> Efforts { get; set; }
}
HasMany(x => x.Efforts)
.AsList("position");
// or
HasManyToMany(x => x.Efforts)
.AsList("position");
// schedule.Efforts have the right order

Related

Mapping the reverse of a ReferencesAny?

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

NHibernate (Fluent) Lazy Loading Not Working

I am attempting to use NHibernate to generate a model for a very odd database. The tables themselves have primary keys for show only, all the actual relationships are on unique columns. For example, a product table with a product id primary key and a unique product name column. Another table, demand, has a product name column and that defines the relationship. I know this situation isn't ideal but it's out of my control.
At any rate, I was able to use Fluent NHibrenate to map product to demand, but I cannot seem to get the entity to lazy-load.
public class Demand
{
public virtual DemandId { get; set; }
public virtual Product { get; set; }
}
public class DemandMap : ClassMap<Demand>
{
public DemandMap()
{
this.Table("Demand");
this.LazyLoad();
this.Id(x => x.DemandId);
this.References(x => x.Product).PropertyRef(x => x.ProductName).LazyLoad();
}
}
Does anyone have any insight into why lazy loading is not working? I know it is not because I can see the product being fetched along with the demand in the SQL profiler.
My idea (Maybe you can try use "HasMany" there is example but you can read something about this):
First class
public class Demand
{
public virtual int DemandId { get; set; }
public virtual int Product { get; set; }
public virtual IEnumerable<NewClass> Name {get; set;}
}
this.HasMany(x=> x.Product).Column("Product_id").not.nullable;
Second class
public class NewClass
{
public virtual Demand Product_id {get; set;}
}
this.References(x => x.Product).Column("product_id).not.nullable

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

Entity Framework Code First Class with parent and children of same type as it's own class

I have a class of Content which should be able to have a parentId for inheritance but also I want it to have a list of child content which is nothing to do with this inheritance tree.
I basically wanted a link table as ChildContentRelationship with Id's for parentContent and childContent in it and the Content class would have a list of ChildContentRelationship.
This has caused a lot of errors.
Here's waht I sort of want to do
public class Content
{
public int Id { get; set; }
public int? ParentContentId { get; set; }
public virtual Content ParentContent { get; set; }
public string Name { get; set; }
public int ContentTypeId { get; set; }
public virtual ContentType ContentType { get; set; }
public virtual ICollection<Property> Properties { get; set; }
public virtual ICollection<ChildContentRelationship> ChildContent { get; set; }
}
How would I set this up in EF?
I am not sure if I understand your model correctly. Let's discuss the options.
For a moment I omit this additional entity ChildContentRelationship and I assume the ChildContent collection is of type ICollection<Content>.
Option 1:
I assume that ParentContent is the inverse property of ChildContent. It would mean that if you have a Content with Id = x and this Content has a ChildContent with Id = y then the ChildContents ParentContentId must always be x. This would only be a single association and ParentContent and ChildContent are the endpoints of this same association.
The mapping for this relationship can be created either with data annotations ...
[InverseProperty("ParentContent")]
public virtual ICollection<Content> ChildContent { get; set; }
... or with Fluent API:
modelBuilder.Entity<Content>()
.HasOptional(c => c.ParentContent)
.WithMany(c => c.ChildContent)
.HasForeignKey(c => c.ParentContentId);
I think this is not what you want ("...has nothing to do with..."). Consider renaming your navigation properties though. If someone reads Parent... and Child... he will very likely assume they build a pair of navigation properties for the same relationship.
Option 2:
ParentContent is not the inverse property of ChildContent which would mean that you actually have two independent relationships and the second endpoint of both relationships is not exposed in your model class.
The mapping for ParentContent would look like this:
modelBuilder.Entity<Content>()
.HasOptional(c => c.ParentContent)
.WithMany()
.HasForeignKey(c => c.ParentContentId);
WithMany() without parameters indicates that the second endpoint is not a property in your model class, especially it is not ChildContent.
Now, the question remains: What kind of relationship does ChildContent belong to? Is it a one-to-many or is it a many-to-many relationship?
Option 2a
If a Content refers to other ChildContents and there can't be a second Content which would refer to the same ChildContents (the children of a Content are unique, so to speak) then you have a one-to-many relationship. (This is similar to a relationship between an order and order items: An order item can only belong to one specific order.)
The mapping for ChildContent would look like this:
modelBuilder.Entity<Content>()
.HasMany(c => c.ChildContent)
.WithOptional(); // or WithRequired()
You will have an additional foreign key column in the Content table in your database which belongs to this association but doesn't have a corresponding FK property in the entity class.
Option 2b
If many Contents can refer to the same ChildContents then you have a many-to-many relationship. (This is similar to a relationship between a user and roles: There can be many users within the same role and a user can have many roles.)
The mapping for ChildContent would look like this:
modelBuilder.Entity<Content>()
.HasMany(c => c.ChildContent)
.WithMany()
.Map(x =>
{
x.MapLeftKey("ParentId");
x.MapRightKey("ChildId");
x.ToTable("ChildContentRelationships");
});
This mapping will create a join table ChildContentRelationships in the database but you don't need a corresponding entity for this table.
Option 2c
Only in the case that the many-to-many relationship has more properties in addition to the two keys (ParentId and ChildId) (for example something like CreationDate or RelationshipType or...) you would have to introduce a new entity ChildContentRelationship into your model:
public class ChildContentRelationship
{
[Key, Column(Order = 0)]
public int ParentId { get; set; }
[Key, Column(Order = 1)]
public int ChildId { get; set; }
public Content Parent { get; set; }
public Content Child { get; set; }
public DateTime CreationDate { get; set; }
public string RelationshipType { get; set; }
}
Now your Content class would have a collection of ChildContentRelationships:
public virtual ICollection<ChildContentRelationship> ChildContent
{ get; set; }
And you have two one-to-many relationships:
modelBuilder.Entity<ChildContentRelationship>()
.HasRequired(ccr => ccr.Parent)
.WithMany(c => c.ChildContent)
.HasForeignKey(ccr => ccr.ParentId);
modelBuilder.Entity<ChildContentRelationship>()
.HasRequired(ccr => ccr.Child)
.WithMany()
.HasForeignKey(ccr => ccr.ChildId);
I believe that you want either option 2a or 2b, but I am not sure.

Fluent nHibernate some kind of flat table

i have one problem (obviously :) )
Is it possible to make dynamic queries in nHibernate in that way...
I have many tables (let we say: User, City, Country, Continet,...) is it possible to flaten this data so i do not need to know joins between this tables (get continent for user, without making join user.city, city.country, coutry.continent)?
The point is i want to some kind flatten data, so user can dynamically select data on user interface without knowing data model behind application?
It will be great that someone gave me at least idea how to make this, or if it's possible...
One example on web is GoogleAnalytics Custom reports (you can drag dimensions and metrics on UI and get results)
You said you're using Fluent NHibernate, which means that, assuming your domain model is structured correctly, you should not need to use any joins.
"Flattening" the data is a UI concern, not a database concern, so you shouldn't flatten your data model or simplify it for the UI unless you absolutely have to.
Let's assume you have the following entities:
public class User
{
public virtual string Name { get; set; }
public virtual City City { get; set; }
}
public class City
{
public virtual string Name { get; set; }
public virtual Country { get; set; }
}
public class Country
{
public virtual string Name { get; set; }
}
If you want to filter users by a certain country, the LINQ query for this (assuming NHibernate 3) would be:
var country = session.Single<Country>(x => x.Name == "Africa");
session.Query<User>().Where(x => x.City.Country == country);