I've grown accustomed in SQLAlchemy (Python) to map a relationship/collection with lazy="dynamic" which maps the property as a Query object instead of a populated list/collection (or Proxy for lazy loaded properties). This mapped property then allows you to further refine the query used to fetch the collection before doing so (apply an order, limit, filter, etc).
For example, in SQLAlchemy I can map a relationship like so:
class Post(Base):
...
class User(Base):
...
posts = relationship(Post, lazy="dynamic")
And then when I retrieve a user, I can apply an order on posts, or only retrieve the last 5, etc.
user = session.query(User).get(1)
# Fetch the last 5 posts by user 1
posts = user.posts.order_by(Post.create_date.desc()).limit(5).all()
http://docs.sqlalchemy.org/en/rel_0_7/orm/collections.html
I would love to find a way to do this using Fluent NHibernate, mapping the collection as a QueryOver or IQueryable (LINQ) such as:
public virtual QueryOver<Post> Posts {get; set;}
or
public virtual IQueryable<Post> Posts { get; set; }
and in the mappings do something like:
public class UserMap : ClassMap<User>
{
public UserMap()
{
...
HasMany(u => u.Posts).Fetch.Dynamic
}
}
Is this currently possible using Fluent NHibernate (or just NHibernate)?
It is possible using NHibernate, however it's not quite out of the box.
You would need to write your own collectionwrapper implementing IQueryable, inject it with your own CollectionFactory and delegate the query generation to the session which loaded the containing object.
Related
I'm building an Orchard CMS module, where I want to eager load data, but can't work out how to do this.
For example a Client has many Events, so I have a ClientRecord & EventRecord for these:
public class ClientRecord {
private IList<EventRecord> _eventRecords;
public virtual int Id { get; set; }
public virtual string Company { get; set; }
public virtual IList<EventRecord> EventRecords {
get { return _eventRecords ?? (_eventRecords = new List<EventRecord>()); }
set { _eventRecords = value; }
}
}
WhenI load a Client in my ClientController
var clientRecord = _clientRepository.Get(id);
and then display the Events in my View
<ul>
#foreach (var eventRecord in Model.EventRecords) {
<li>
#eventRecord.Name (#eventRecord.StartDate.ToShortDateString())
</li>
}
</ul>
the Events are displayed and MiniProfiler shows a separate query to lazy-load the Events.
I've tried putting an [Aggregate] attribute on the EventRecords collection in the ClientRecord, but this didn't have any effect.
I'm not that familiar with NHibernate, so hopefully this is something simple, but how do I specify I want to eager load the EventRecords when the ClientRecord is retrieved?
[EDIT]
In Orchard CMS the NHibernate mappings are created for you, based on the convention that the class is called xxxRecord and there is a database table in place with the same name.
So you don't (as far as I know) have a mapping file that you can specify this in. If I'm right about that, then the question is whether there is any way to specify you want eager loading in the query you use to retrieve the Client (rather like Entity Framework's "include" method).
You must specify eager loading in your nHibernate mapping file, using Fluent nhibernate this would look (something) like this:
HasMany(x => x.EventRecords).KeyColumn("CompanyId").Not.LazyLoad().Fetch.Select();
The Fetch.Select() will execute a second select statement to load all of the related Event records.
The Not.LazyLoad() tells nHibernate to execute this second select immediately, if you remove this execution would be deferred until the collection is accessed.
In response to your comments you can specify eager loading in your query using fetch also (LINQ example shown)
NHSession.Query<ClientRecord>().Fetch(c => c.EventRecords).ToList();
I'm trying to implement simple soft deletes in my application using Fluent NHibernate. All entities have a boolean flag IsDeleted, and delete operation only sets this property to true.
I'm struggling with querying more complex entities referencing each other, for example by having many-to-many relationship. Let's say I have Person entity, having a collection of Projects:
class Person : Entity {
public virtual IList<Project> Projects { get; set; }
}
class Project : Entity {
//some properties
}
Now imagine that Person p has Projects proj1 and proj2. If proj1 gets soft-deleted, we simply set its IsDeleted property to true. However, when I access p's projects, collection is automatically lazy-loaded with proj1 too, independently from its flag. Of course, I can always filter the collection, for example by Projects.Where(x => !x.Isdeleted), but this leads to repetitive code prone to bugs. I want to separate this kind of data juggling from my presentation layer.
I want to automatize this process by some global rule saying "load only entities with IsDeleted set to false", which applies to all queries and lazy-loaded collections.
What I have tried:
Override events, but I wasn't able to intercept all DB reads and filter all entities that are read.
Filters, which I couldn't get to work with lazy-loaded collections.
What would you recommend, what is the easiest way to implement soft deletes without code repetition and easily separable from presentation layer?
To complete #Rippo, off the top of my head, something like this should work:
public abstract class BaseEntity
{
public bool IsDeleted {get;set;}
}
public class SomeEntity : BaseEntity
{
....
}
public abstract class EntityMap<T>: ClassMap<T> where T:BaseEntity
{
public EntityMap()
{
Where(x=>!x.IsDeleted);
}
}
public class SomeEntityMap: EntityMap<SomeEntity>
{
...
}
I have a table logging web page hits. Something like: {VisitId, VisitorId, Url, Date}
(The visitor ID is a GUID stored in a cookie)
I would like to create a Visitor object that has a collection of Visit objects.
class Visitor {
public virtual Guid VisitorId { get; set; }
public virtual IList<Visit> Visits { get; set; }
}
Rather than add another table for Visitor, can NHibernate create this object just from the collection of Visits?
Ideally, I would like to write:
var visitor = session.Get<Visitor>(guidFromCookie)
And then be able to work with the Visits list and persist changes back to the DB.
(I'm using FluentNHibernate and NHibernate 3.0)
I'm new to NHibernate, but it seems the something should be possible using a custom IEntityPersister, or is this too low level and loads of work? Any suggestions would be appreciated.
When you say "create this object", do you mean retrieve? What is your reason for not having a visitor table? You could use the criteria API or hbm to load a list of visits by the guid if you don't want a visitor entity/table.
If you mapped Visitor and made it Lazy Loaded, you might be able to do this. You'd have to tell NHibernate that the table existed, even though it didn't. However, when you want to get the Visitor object (note that the only property mapped is the Id), then instead of using .Get(), use .Load() which will return an uninitialized proxy. So you'll have an entity, but it won't actually hit the database, so it will never know that the table doesn't exist.
public class VisitorMap : ClassMap<Visitor>
{
public VisitorMap()
{
Table("SomeNonExistentTable");
LazyLoad(); // should be the default anyway
Id(x => x.Id)
.GeneratedBy.Guid();
HasMany(x => x.Visits)
.AsList()
.Not.LazyLoad();
}
}
...and then...
var visitor = session.Load<Visitor>(guidFromCookie);
foreach(var visit in visitor.Visits)
{
// do wonderful things
}
I have a class which I would like to map as a component onto any table which contains it:
public class Time
{
public int Hours { get; set; }
public int Minutes { get; set; }
public int Seconds { get; set; }
}
I would like to store this class as a bigint in the database - the same as how TimeSpan is stored but my class has completely different behaviour so I decided to create my own.
I'm using FLH's automapper and have this class set as a component (other classes have Time as a property). I've got as far as creating an override but am not sure how to go about mapping it:
I gave it a try this way:
public class TimeMappingOverride : IAutoMappingOverride<Time>
{
public void Override(AutoMapping<Time> mapping)
{
mapping.Map(x => x.ToTimeSpan());
mapping.IgnoreProperty(x => x.Hours);
mapping.IgnoreProperty(x => x.Minutes);
mapping.IgnoreProperty(x => x.Seconds);
}
}
But got this error:
Unable to cast object of type 'System.Linq.Expressions.UnaryExpression' to type 'System.Linq.Expressions.MethodCallExpression'.
How should I go about this?
Details of components can be found here: http://wiki.fluentnhibernate.org/Fluent_mapping#Components
But first of all, you can't map a method.
Assuming you change ToTimeSpan() to a property AsTimeSpan, there are two ways to do it, only the harder of which will work for you because you are using automapping:
Create a ComponentMap<Time> -- once done, your existing mapping will just work. This is not compatible with automapping.
Declare the component mapping inline:
mapping.Component(x => x.AsTimeSpan, component => {
component.Map(Hours);
component.Map(Minutes);
component.Map(Seconds);
});
You'll have to do this every time, though.
Of course, this doesn't address "I would like to store this class as bigint…"
Are you saying you want to persist it as seconds only? If so, scratch everything at the top and again you have two options:
Implement NHibernate IUserType (ugh)
Create a private property or field that stores the value as seconds only, and wire only this up to NHibernate. The getters and setters of the pubic properties will have to convert to/from seconds.
I personally haven't worked with AutoMappings yet, but my suggestion would be to look into NHibernate's IUserType to change how a type is being persisted. I believe that's a cleaner way of defining your custom mapping of Time <-> bigint.
Reading the code above, Map(x => x.ToTimeSpan()) will not work as you cannot embed application-to-database transformation code into your mappings. Even if that would be possible, the declaration misses the transformation from the database to the application. A IUserType, on the other hand, can do custom transformations in the NullSafeGet and NullSafeSet methods.
I have a class called Entry. This class as a collection of strings called TopicsOfInterest. In my database, TopicsOfInterest is represented by a separate table since it is there is a one-to-many relationship between entries and their topics of interest. I'd like to use nhibernate to populate this collection, but since the table stores very little (only an entry id and a string), I was hoping I could somehow bypass the creation of a class to represent it and all that goes with (mappings, configuration, etc..)
Is this possible, and if so, how? I'm using Fluent Nhibernate, so something specific to that would be even more helpful.
public class Entry
{
private readonly IList<string> topicsOfInterest;
public Entry()
{
topicsOfInterest = new List<string>();
}
public virtual int Id { get; set; }
public virtual IEnumerable<string> TopicsOfInterest
{
get { return topicsOfInterest; }
}
}
public class EntryMapping : ClassMap<Entry>
{
public EntryMapping()
{
Id(entry => entry.Id);
HasMany(entry => entry.TopicsOfInterest)
.Table("TableName")
.AsList()
.Element("ColumnName")
.Cascade.All()
.Access.CamelCaseField();
}
}
I had a similar requirement to map a collection of floats.
I'm using Automapping to generate my entire relational model - you imply that you already have some tables, so this may not apply, unless you choose to switch to an Automapping approach.
Turns out that NHibernate will NOT Automap collections of basic types - you need an override.
See my answer to my own question How do you automap List or float[] with Fluent NHibernate?.
I've provided a lot of sample code - you should be able to substitute "string" for "float", and get it working. Note the gotchas in the explanatory text.