NHibernate Domain Model - Adding Child Objects to Collections - nhibernate

I'm still learning here and have a question about child collections. I have an aggregate root called Audio, which has a collection of AudioDownloads.
The downloads are records of each IP address which downloads the audio, i don't want to have duplicate records of the same IP for each Audio.
In my domain i have the following function:
public virtual void Add(AudioDownload download)
{
if (!AudioDownloads.Contains(download)) {
TotalDownloads++;
AudioDownloads.Add(download);
}
}
And this is how i am calling the Add function:
var download = new AudioDownload();
audio.Add(download);
This is returning all downloads from the database for this Audio (which chould be thousands!), also it's still adding the download even though one already exists.
I'm using S#arp with the DomainSignature approach for comparing my entities.
Here is my Domain:
public class AudioDownload : Entity, ITenantSpecific
{
public AudioDownload() { DateAdded = DateTime.Now; }
[DomainSignature]
public virtual Audio Audio { get; set; }
[DomainSignature]
public virtual string Ip { get; set; }
public virtual DateTime DateAdded { get; set; }
}
My question is...even if i can get AudioDownloads not to add duplicate entries, should i be doing it this way at all?
Thank you very much!
Paul

I expect that most ways to do this will always lead you to query all downloads from the database, which is probably not what you want.
Another approach that might be cheaper is just to have a unique key in the database defined based on AudioId and Ip. If you then insert a record that duplicates these you will get an exception from NHibernate telling you a unique key was violated: handle that exception gracefully (i.e. don't show it as an error, load the existing AudioDownload and use that in future) and you will have achieved your goal, I believe.
When you use this approach do not check whether the download is already contained in the collection, since that would still trigger loading of all records.
On the other hand: would it not be interesting to see that something was downloaded from the same Ip multiple times?

Related

Repository pattern, ViewModel and ORMs

With Repository pattern and ViewModels, how do you build queries against the database if you don't want the raw database objects to leak outside the repository? How do I actually create queries without loading ALL the database in memory and using LINQ to Objects? I can't expose IQueryable to the rest of the app.
For example, with EF I have a bunch of POCOs with several properties that match db fields, but also some stuff to work around enums not being directly support (for now) as well as foreign key IDs to prevent N+1 and easier querying and so on. I don't want them to leak out to the rest of the application, I want the application to just see a normal object graph.
public class DbUser
{
public int Id { get; set; }
public string Name { get set; }
public int GroupId { get; set; }
public DbGroup Group { get; set; }
public ICollection<DbComment> { get; set; }
}
public class User
{
public int Id { get; set; }
public string Name { get set; }
public Group Group { get; set; }
public ICollection<Comment> { get; set; }
}
The problem here is my repository will internally use EF for the querying (and in-memory stuff when unit testing). But how do I implement IQueryable<User> FindAll()? I can't just do return dbContext.Users.Select(u => new User(u)), as in that case I lose all possible query ability; it'll just load the whole user collection in memory, convert all the types to User from DbUser and then build LINQ queries on the in-memory collection - that is horribly inefficient.
I can't just build queries in the repository. On some pages I have queries that select a few fields, but also calculate some complex stuff from other related objects, filter them based on the result (for example count of comments with positive score), but I also need that back in the application. I could select all objects used to get the complex stuff and return them to the application (but not as db entities) but that would mean select a LOT of data.
Basically how do I prevent the database entities from polluting the rest of the application with their cruft and hacks, while still maintaining the ability to build queries outside of the repository?
CQRS (Command Query Responsibility Segregation) solves this problem. You have the 'real' model , the Domain model, with all the business rules and all that, and a 'query-ony' model which basically is a simple poco (which can be used directly by Views) that will be returned by a specialised query only repository.
The peristence model (EF entities) are used only to 'talk' with the db, the repos always returns or deals with domain/ application objects. Basically, you have to map the EF entities to the Domain ones (and viceversa when saving). In this way, you'll have separated models each with its own purpose.

How to force RIA Services to include a sub-entity in one Query method but not another

Here's the scenario:
I've got an association between "Groups" and "Users, represented by a "UserGroupAssignment" object.
public class UserGroupAssignment
{
[Key]
public virtual long Id { get; set; }
[Association("UserAssignmentToUser", "UserId", "Id", IsForeignKey = true)]
public virtual User { get; set; }
[Association("UserAssignmentToGroup", "GroupId", "Id", IsForeignKey = true)]
public virtual Group { get; set; }
public virtual bool IsPrimary { get; set; }
public virtual DateTime? ValidFrom { get; set; }
public virtual DateTime? ValidTo { get; set; }
}
I have two business logic methods, GetUserAssignmentsForGroups and GetGroupAssignmentsForUsers that I return the assignments with the User and Group properties populated respectively. i.e. GetUserAssignmentsForGroup takes a GroupId and returns the assignments for that Group with the User property populated.
What I want is to expose those two methods as domain query methods like so:
[Query]
public IQueryable<UserGroupAssignment> GetAssignmentsForGroupWithUsers(long groupId)
{
return this.businessLogic.GetUserAssignmentsForGroups(groupId);
}
[Query]
public IQueryable<UserGroupAssignment> GetAssignmentsForUserWithGroups(long userId)
{
return this.businessLogic.GetGroupAssignmentsForUsers(userId)
}
My problem is that whilst the business logic methods return the correctly populated Assignments via NHibernate, RIA Services is NOT passing the sub-entities (User or Group) across the wire.
I don't want to use [Include] attributes on the User or Group properties of the UserAssignment class, as I want to minimise the payload over the wire - I don't want to send the group over when I'm only interested in the User of each UserAssignment, for example.
So my question is this:
How do I tell RIA services to
explicitly include User sub-entities
in one domain query method and Group
sub-entities in the other?
Remember, I'm using NHibernate at the back end and custom query methods in the RIA Services, so can't use the EF-style include in the client query.
Thanks
Joel
you should apply the [Include] attribute in the metadata class. then create one domain service method for fetching data without properties included, and a separate method for fetching data with properties included.
You might find this thread helpful in understanding how [Include] attribute works.
Old question, but still interesting. Did you find a solution ?
As far as I know of WCF RIA Architecture it isn't so easy.
An easy and dirty way could be to override the Query method, force the enumeration of the IQueryable being returned (I guess you're using LINQ to nHibernate, in which case, good luck) then examine the HttpContext (you're using WCF RiaServices so you MUST have aspNetCompatibility turned on) and set to null the reference that you don't want to send over the wire (User or Group).
Anyway this way FORCE you to use the [IncludeAttribute]. However I don't see any reasonable route that avoid its use, and this way allow you to send the entity over the wire just when you need to.
IMO I belive that in order to totally avoid the use of [Include] you must rollout your own serializer serverside and deserializer clientside or change the UserGroupAssignment entity so that the user property become a string containing the serialized User (or Group) that you decide to valorize or not according your method.
Please let us knows if you already found a solution, the question is interesting.

NHibernate insert/lookup performance

I have several XML files and each file contains data of ‘root objects’ which I parse using Linq to XML and then create actual root objects which I persist using NHibernate and the sharp architecture repository. I have started to optimise the data insert and manage to add 30000 objects in about 1 hour and 40 minutes to the database. However, this is still too slow.
I think one bottle neck is the lookup of objects in the database which requires IO. Objects have to be looked up for reuse.
The root object has several authors:
public virtual IList<Author> Authors { get; set; }
Authors have this structure:
public class Author : Entity
{
public virtual Initials Initials { get; set; }
public virtual ForeName ForeName { get; set; }
public virtual LastName LastName { get; set; }
}
I have achieved a great speed up by using a typed Id (something I wouldn't normally do):
public class LastName : EntityWithTypedId<string>, IHasAssignedId<string>
{
public LastName()
{
}
public LastName(string Id)
{
SetAssignedIdTo(Id);
}
public virtual void SetAssignedIdTo(string assignedId)
{
Id = assignedId;
}
}
Which I look up (and potentially create) like this:
LastName LastName = LastNameRepository.Get(TLastName);
if (LastName == null)
{
LastName = LastNameRepository.Save(new LastName(TLastName));
LastNameRepository.DbContext.CommitChanges();
}
Author.LastName = LastName;
I am looking authors up like this:
propertyValues = new Dictionary<string, object>();
propertyValues.Add("Initials", Author.Initials);
propertyValues.Add("ForeName", Author.ForeName);
propertyValues.Add("LastName", Author.LastName);
Author TAuthor = AuthorRepository.FindOne(propertyValues);
if (TAuthor == null)
{
AuthorRepository.SaveOrUpdate(Author);
AuthorRepository.DbContext.CommitChanges();
Root.Authors.Add(Author);
}
else
{
Root.Authors.Add(TAuthor);
}
Can I improve this? Should I use stored procedures/HQL/pure SQL/ICriteria instead to perform the lookup? Could I use some form of caching to speed up the lookup and reduce IO? The CommitChanges seems to be necessary or should I wrap everything into a transaction?
I already flush my session etc. every 10 root objects.
Any feedback would be very much welcome. Many thanks in advance.
Best wishes,
Christian
In all honesty I would say that you shouldn't even be using SA/NHibernate for something like this. It's a bulk data import from XML - an ETL tool like SSIS would be a better choice. Even a hand-cranked process on the DB server would work better - step 1, load XML to a table, step 2, do the UPSERT. Incidentally, SQL 2008 introduced the MERGE command for UPSERT operations, which might be of use.
I would also agree with Dan's comment - is it really necessary to treat initials, forename and surname as separate entities? Treating them as simple strings would boost performance. What in your domain model specifies that they are entities in their own right?
If you really must continue using SA/NHibernate, have a read of this:
http://www.lostechies.com/blogs/jimmy_bogard/archive/2010/06/24/bulk-processing-with-nhibernate.aspx
The suggestion in Jimmy's blog about batching SELECTs should help quite a lot. If you plan to process a batch of 250 records at once, do all the SELECTs as a single NH command, process all the data, then do all the updates as another single batch (which I believe your use of EntityWithTypedId and the adonet.batch_size config setting will help achieve)
Finally - regarding the statement "which I parse using Linq to XML" - is that really the best way of doing it? I'm guessing that it might be, given the size of your input file, but are you aware of the approach of simply deserializing the XML file into an object graph? SO won't let me post the link to a page describing this, because I haven't earned enough reputation yet - but if you want to read up on it, Google "don't parse that xml" and the first article will explain it.
Hope this helps.
Jon
The first thing I would do is simplify the Authors entity as I don't think you need the Initials, ForeName, and LastName objects as separate entities. I think using plain strings would be more efficient:
public class Author : Entity
{
public virtual string Initials { get; set; }
public virtual string ForeName { get; set; }
public virtual string LastName { get; set; }
}

How do I serialize/deserialize a NHibernate entity that has references to other objects?

I have two NHibernate-managed entities that have a bi-directional one-to-many relationship:
public class Storage
{
public virtual string Name { get; set; }
public virtual IList<Box> Boxes { get; set; }
}
public class Box
{
public virtual string Box { get; set; }
[DoNotSerialize] public virtual Storage ParentStorage { get; set; }
}
A Storage can contain many Boxes, and a Box always belongs in a Storage. I want to edit a Box's name, so I send it to the client using JSON. Note that I don't serialize ParentStorage because I'm not changing which storage it's in.
The client edits the name and sends the Box back as JSON. The server deserializes it back into a Box entity.
Problem is, the ParentStorage property is null. When I try to save the Box to the database, it updates the name, but also removes the relationship to the Storage.
How do I properly serialize and deserialize an entity like a Box, while keeping the JSON data size to a minimum?
I would recommend you send a DTO to the client for display purposes (and it should contain the unique database ID). Then send the boxId + the new name back up to the server from the client (there is no need to send the entire DTO back). The server can do a database lookup using the ID to get the box object, update the name field to the one sent from the client, and save the box to the database.
There is no need in this scenario to serialize an NHibernate object, that just adds a lot of complexity and no value.
I would guess that ParentStorage is null because it is being lazily loaded. Either configuring it to be fetched eagerly or forcing a read by calling the getter before serialization may help make sure the Storage is serialized, and depending on your ID scheme this may work (I don't know for sure).
The gains from serialization in this case seem minimal, and may have unexpected consequences as the Box and Storage classes evolve. It seems simpler to send up a single string for the name, load the Box, set the string, and save that object. Then you don't have to worry as much about the optimizations Hibernate does underneath.

NHibernate Attributes Mapping List

I'm a new NHibernate developer. I'm using attributes and not map files and I have configured the application to create the tables automatically.
I Have two classes , Group and User.
Withing the Group class I have a list of users
public class Group
{
[NHibernate.Mapping.Attributes.Id(Name = "GroupId")]
[NHibernate.Mapping.Attributes.Generator(Class = "guid")]
public virtual Guid GroupId { get; set; }
// What Attributes do I place here
public virtual List<User> Users { get; set; }
}
I can't find the right attributes so that there will be two tables that have one to many relation.
Can anyone help?
Thanks,
Ronny
[ManyToMany], [OneToMany] or [ManyToOne] (those linked docs are fairly useless though) depending on how you want it setup. Probably [OneToMany], and then the same on a User.
You could avoid the pain by using the Fluent NHibernate library instead, if you haven't already tried it.