Filter contents of lazy-loaded collection with NHibernate - nhibernate

I have a domain model that includes something like this:
public class Customer : EntityBase<Customer>, IAggregateRoot
{
public IList<Comment> Comments { get; set; }
}
public class Comment : EntityBase<Comment>
{
public User CreatedBy { get; set; }
public bool Private { get; set; }
}
I have a service layer through which I retrieve these entities, and among the arguments passed to that service layer is who the requesting user is.
What I'd like to do is be able to construct a DetachedCriteria in the service layer that would limit the Comment items returned for a given customer so the user isn't shown any comments that don't belong to them and are marked private.
I tried doing something like this:
criteria.CreateCriteria("Comments")
.Add(Restrictions.Or(Restrictions.Eq("Private", false),
Restrictions.And(Restrictions.Eq("Private", true),
Restrictions.Eq("CreatedBy.Id", requestingUser.Id))));
But this doesn't flow through to the lazy-loaded comments.
I'd prefer not to use a filter because that would require either interacting with the session (which isn't currently exposed to the service layer) or forcing my repository to know about user context (which seems like too much logic in what should be a dumb layer). The filter is a dirty solution for other reasons, too -- the logic that determines what is visible and what isn't is more detailed than just a private flag.
I don't want to use LINQ in the service layer to filter the collection because doing so would blow the whole lazy loading benefit in a really bad way. Lists of customers where the comments aren't relevant would cause a storm of database calls that would be very slow. I'd rather not use LINQ in my presentation layer (an MVC app) because it seems like the wrong place for it.
Any ideas whether this is possible using the DetachedCriteria? Any other ways to accomplish this?

Having the entity itself expose a different set of values for a collection property based on some external value does not seem correct to me.
This would be better handled, either as a call to your repository service directly, or via the entity itself, by creating a method to do this specifically.
To fit in best with your current model though, I would have the call that you currently make to get the the entities return a viewmodel rather than just the entities;
public class PostForUser
{
public Post Post {get; set;}
public User User {get; set;}
public IList<Comment> Comments}
}
And then in your service method (I am making some guesses here)
public PostForUser GetPost(int postId, User requestingUser){
...
}
You would then create and populate the PostForUser view model in the most efficient way, perhaps by the detached criteria, or by a single query and a DistinctRootEntity Transformer (you can leave the actual comments property to lazy load, as you probably won't use it)

Related

REST URL handling with DTOs

In my project I have a model with different DTOs:
public class Employee{
public Guid Id {get; set;}
public string Name {get; set;}
public IEnumerable<Cookie> Cookies {get; set;}
}
public class EmployeeIndex :Dto{
public Guid Key{get; set;}
public string Value {get; set;}
}
public class Cookie {
public Guid Id {get; set;}
public string Secret {get; set;}
}
public class CookiePublic :Dto{
public Guid Id{get; set;}
}
Now I would like to keep my API as clean as possible. So I have the following Routes:
POST /employees
GET /employees
GET /employees/index //--> Conflict (should return list of EmployeeIndex
GET /employees/:id
PUT /employees/:id
DELETE /employees/:id
GET /employees/:id/cookies // --> returns List of Cookies
GET /employees/:id/cookies/:cookieId
GET /employees/:id/coookies/public // --> Conflict (should return list of CookiePublic)
But now I need a route for returning also the CookiePublic-DTO. Where can I put that url respectively how does the routes should look like?
Or how would the route should look like to return the employee-index-Dto?
Because /employees/:id/cookies/public and employees/index results in a duplicate url conflict.
How can this get solved in REST?
REST doesn't care what spelling conventions you use for your resource identifiers.
It also doesn't really have any sense of "conflict" as you describe here. The fact that there is ambiguity about the identify of your conflicting resources is an artifact of your routing implementation, not REST.
In other words
/employees/index
/employees/12345
/employees/67890
as far as REST is concerned, these are three different resources. The fact that they want to be implemented via two routes is an implementation detail.
From a REST perspective, there's no particular reason that the hierarchy of path segments needs to match the domain hierarchy (the "resource model" is not the "domain model" is not the "data model").
So you could consider
/employees/12345
/index/employees
/employees/12345/cookies
/cookies/123
/public-cookies/123
The machines absolutely don't care whether the structure of the URI matches the domain semantics. They care a little bit about some general purpose things (the ability to use relative references to compute other resource identifiers, the ability to use general purpose URI templates to compute other resource identifiers, the ability to use web forms as URI templates, etc).
So we use the extra degrees of freedom allowed by the machines indifference to choose a design that makes life easier for the humans we care about (customers looking at a browser history, operators reviewing an access log, technical writers trying to document the API, etc).

Is it possible to use NHibernate Filters to filter through references?

Contrived example, but let's say I have a these entities:
public class Root
{
public virtual Customer Customer { get; set; }
}
public class Customer
{
public virtual CustomerData Data { get; set; }
}
public class CustomerData
{
public virtual string FooName { get; set; }
}
Now, let's say I want to create a filter for Root based on the value of FooName. Intuitively, I tried this in my FooMap class. Using Fluent mappings.
ApplyFilter("FooNameFilter", "Customer.Data.FooName in (:argument)");
This doesn't work. A SqlClient.SqlException is thrown stating The multi-part identifier "Customer.Data.FooName" could not be bound.
Is there a way to make filters work this way, or am I forced to move that logic into all Query<Root>()s instead?
What could be working, is to move the filter to the CustomerData object if possible, or to create "more sophisticated SQL condition" applied on the Customer mapping. But it is about pure SQL, no references. How do the filters work?
The filters are the same as the where clause, but could be adjusted in a runtime. The extract from documentation 18.1. NHibernate filters
NHibernate adds the ability to pre-define filter criteria and attach
those filters at both a class and a collection level. A filter
criteria is the ability to define a restriction clause very similiar
to the existing "where" attribute available on the class and various
collection elements. Except these filter conditions can be
parameterized. The application can then make the decision at runtime
whether given filters should be enabled and what their parameter
values should be. Filters can be used like database views, but
parameterized inside the application.
The definition of the where:
where (optional) specify an arbitrary SQL WHERE condition to be used when retrieving objects of this class
Other words, these settings act as "add on" to our mapping. They are extending it (both where and filter) with more SQL balast. The filter could be shared among many mappings and applied to all queries inside one session, but it must target the column:
condition=":myFilterParam = MY_FILTERED_COLUMN"

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.

Is it possible to use Linq-SQL without drag-and-drop?

If i want to use Linq-SQL i also have to drag the DB Table unto the designer surface to create the entity classes.
I always like full control in my application and do not like the classes created by dotnet.
Is it possible to provide this connection between Linq and the DB using my own Data Access Layer Entity classes?
How can i get it done?
You can write your own classes very easily using Linq-to-SQL - just involves painting your classes with some Attributes.
For Example, this is a very simple table I have in one of my projects, and it works with Linq-to-SQL just fine:
[Table(Name = "Categories")]
public class Category : IDataErrorInfo
{
[Column(IsPrimaryKey = true, IsDbGenerated = true, AutoSync = AutoSync.OnInsert)]
public int Id { get; set; }
[Column] public string Name { get; set; }
[Column] public string ExtensionString { get; set; }
}
The code was very easy, especially if you make your property names line up with your table names (you don't have to).
Then you just need a Repository to connect to the DB:
class CategoryRepository : ICategoryRepository
{
private Table<Category> categoryTable;
public CategoryRepository(string connectionString)
{
categoryTable = (new DataContext(connectionString)).GetTable<Category>();
}
}
Of course there is more to it, but this shows you the very basics and it is not hard to do once you understand it. This way you have 100% control over your classes and you can still take advantage of Linq-to-SQL.
I learned this approach from Pro ASP.NET MVC Framework, an awesome book.
If you want to see more, all of my Linq-to-SQL classes were written from scratch on one of my projects you can browse here.
To avoid drag & drop you can take a look at SqlMetal.exe.
However, it sounds like you really are requesting Persistence Ignorance, and I'm not sure that this is possible with L2S - it certainly isn't possible with LINQ to Entities until .NET 4...
I once wrote a blog post on using SqlMetal.exe and subsequently modifying the generated schema - perhaps you will find it useful, although it has a different underlying motivation.
I've got a couple tutorials up on CodeProject that walk through how to do this, including how to handle the relationships (M:M, 1:M, M:1) in an OO way and keep them in synch as you make updates:
A LINQ Tutorial: Mapping Tables to Objects
A LINQ Tutorial: Adding/Updating/Deleting Data