object == object instead of object.id == object.id potential problems - c#

I have inherited a very sloppy project and I am tasked with explaining why its bad. I've noticed all over the code they have done comparisons like this
(IQueryable).FirstOrDefault(x => x.Facility == facility && x.Carrier == shipCode.Carrier
in the example above x.Facility is from the database and facility is from shipment.facility which is mapped as a complex object in nhibernate.
I would have expected to see .FirstOrDefault(x => x.Facility.ID == facility.ID
At first I thought comparing the whole object might cause issues if the facility record was changed in the db then the facility stored in shipment would obviously be different.
After thinking about it more I realized that shipment.facility was populated from the id so it should match even if that facility record changed.
It still feels wrong to me I see this being very buggy and hard to track down? Is there somthing specifically wrong with comparing the entire object vs an id.

Expanding my previous comments into an answer:
I think this is actually good practice when the ORM allows it. I'm not experienced with NHibernate, so for the rest of the answer I'll assume that it does in fact implement equality in a sensible way (such as comparing primary keys). You should ensure this is the case, otherwise the code would be not only bad but potentially buggy.
As an analogy, forget about SQL for the moment, imagine you were dealing with a POCO which was not part of any ORM. You want to choose between the following:
Approach 1: IEquatable
public class Facility : IEquatable<Facility>
{
public int Id {get; private set;}
//The rest of the properties
public bool Equals(Facility other)
{
return other.Id == Id;
}
}
(You'd also want to override Object.Equals, but I'll exclude that for brevity)
Approach 2: IEqualityComparer
public class Facility
{
public int Id {get; private set;}
//The rest of the properties
}
public class FacilityIdsMatchEqualityComparer : IEqualityComparer<Facility>
{
public bool Equals(Facility x, Facility y)
{
return x.Id == y.Id;
}
}
(GetHashCode also excluded for brevity).
Which of the two approaches is better? Well, I'd say it's Approach 1 by a clear margin. It adheres to two important principles that Approach 2 doesn't:
Don't repeat yourself. In the second approach, any code anywhere trying to compare facilities would have to use the FacilityIdsMatchEqualityComparer. This fact, that particular equality comparison logic needs to be used, would be sprayed all over the solution, repeated every time you want to compare them.
Single responsibility principle. Not only does every class doing the comparison need to repeat code, but that code is taking on a responsibility that doesn't belong to the class. It should be up to the Facility to express the implementation of equality, not up to every class that wants to use it to say that it's done by comparing Ids. (Yes I realise that you could just make it, say, FacilityEqualityComparer so that the calling classes remain agnostic about how the equality comparison is done, but the purpose of this is as an analogy with the code in the OP, where the exact comparison logic is hard coded into every comparison)
So, bringing it back to the actual question, this analogy very closely mirrors the situation you have here. It's not quite as simple as the Facility implementing IEquatable, but the principle is exactly the same: the ORM is taking its own responsibility for how equality checking is implemented, rather than pushing that responsibility out to code using it.
This means that if I'm writing code, say outside of the data access layer, I can say "I want to check if these two objects are equal, so I'll write object1 == object2", rather than "I want to check if these two objects are equal, and these two objects are entities, which because of the way we implement our persistence means that when I check for equality that will be converted into a SQL query, so I need to write this check as if I were writing SQL, which means I need to compare their primary keys, which either by checking attributes or maybe through my knowledge of conventions in the data access layer, I know means comparing their Id properties. So I'll write object1.Id == object2.Id".
ORMs aren't perfect, and you can't always completely abstract away the underlying SQL database, but when you can, you should!

I am not sure what you are using but normally Entity Framework should give you an error like this if you compare the objects:
Unable to create a constant value of type '...'. Only primitive types
or enumeration types are supported in this context.
Because you are sending a class needs to be converted into SQL. In any case comparing objects is not ideal for SQL code if you are trying to send a query to SQL Server.

Related

How to implement entity validation across instances of the same entity

In our project we use DDD as architecture (clean architecture).
Let's say I have an entity called A. A has a property called B.
Now I want a validation that when a second entity A is created, that B must be unique over all instances of A in a store.
My idea was to implement a domain service for it, using the repository. The question then is if this domain service should implement the validation itself or just provide that data for it... (to be used in the interactor/usecase for validation).
Example code (code is kept simple):
public class A
{
public A(string b)
{
B = b;
}
public string B {get; private set;}
}
Let's say I have an entity called A. A has a property called B. Now I want a validation that when a second entity A is created, that B must be unique over all instances of A in a store.
The problem you are trying to solve is sometimes known as set validation.
The easy answer: you introduce an index, that tracks the mapping of each value B to the specific entity A that is allowed to own it.
Of course, that introduces contention; you'll need to mitigate the case where two different A's are being modified at the same time. The index, and all of the A's, become part of a single consistency boundary that needs to be managed. This is pretty much what happens when we are storing our entites in a single RDBMS -- we can introduce a constraint to ensure that there are no duplicates.
You can split that single consistency boundary into separate A entities, and also individual B->A entities. But now you have the possible problem of trying to modify two different consistency boundaries at the same time, and that introduces race conditions.
A third possibility is to relax the consistency constraint -- allow conflicts to be stored, and resolve them later. See, for example, Greg Young on warehouse systems and Udi Dahan on race conditions.
The usual answer from domain-driven-design is to push back really hard on that requirement, to make sure that it is real: what's the actual cost to the business if the constraint is violated?
Think airplane seat maps: obviously only one passenger should be sitting in a seat. But that doesn't mean it's a critical failure for the seat to be assigned to more than one person, because the human operators (gate agents) have ways of mitigating these problems. See also Greg Young's talk Stop Over Engineering.
I think a domain service is the option to take, take a look at this blog(blog.sapiensworks.com/post/2017/08/23/…) where the 'username must be unique' scenario is given, resembling my issue in the initial post.

How to overcome returning ID from a database insert method design challenge

It will probably be a long post but please bear with me. The basic idea is this:
public int InsertPersonAndGetPersonId(Person person){
_dbContext.Insert(person);
return person.PersonId;
}
The method above is simple but it violates clean programming principles.
It is against Command/Query separation in methods.
It does more than one job.
When I am evaluating different approaches, I usually list the pros and cons and choose the one that has the least cons and the least trade-offs. Therefore, honestly, the method above looks better than the alternatives listed below. But I still would like to get the opinions of SO community and perhaps I learn a new pattern that works best with this challenge.
Alternative #1
The one alternative is to have two methods. While the one is inserting new records, the other gets the lastly added personId from database. But this is not reliable unless you prevent database from accepting new person insertion between the time you insert a record and get its id from database.
You can even do filtering by the a property of Person (Name for instance) when getting the record from database but in addition to what I said above, there can also be more than one person who have the same name but different PersonIds.
It is also doing one more database trip. I am a pragmatic person and don't like to speculate about performance without actually measuring it. However, if I can prevent something with a simple change which will contribute to the performance even slightly, then I find it silly not to do it. Of course, while doing it, I also consider clean code practices.
Alternative #2
The other method can be to change InsertPersonAndGetPersonId is in and have something like following:
public class PersonRepository
{
private int _personId;
public void InsertPerson(Person person){
_dbContext.Insert(person);
_personId = person.PersonId;
}
public int GetLastPersonId
return _personId;
}
}
Even though I don't like the name of this method GetLastPersonId(), which may bring a different personId than expected but let's assume that it returns the id of person object. The reason it is bad, besides what I already said, it is modifying the state of the object, therefore have a side effect.
Alternative #3
We can simply have the method below:
public void InsertPerson(Person person){
_dbContext.Insert(person);
_personId = person.PersonId;
}
and since person is reference type, we can access person.PersonId like following:
var personRepository = new PersonRepository();
var person = new Person() {Name="Hello"};
personRepository.InsertPerson(person);
Console.WriteLine(person.PersonId);
Did I like it? No! It is way too hidden and unpredictable and you don't really know it unless you check the implementation detail and then we break the beauty of abstraction.
Alternative #4
We can use out like follows:
public void InsertPerson(Person person, out int personId){
_dbContext.Insert(person);
personId = person.PersonId;
}
But this looks more silly and cumbersome than the first one InsertPersonAndGetPersonId. If I am going to have to return something, then I would return it using return and make the signature more explicit for the developers. Also in fact, out and ref makes more sense when we need to return multiple values. For instance TryParse(), returns boolean but also you can get the parsed value using out or ref too.
Update
Due to a couple of comments, I decided to clarify my question a little bit more. What I am asking is how to get the PersonId without breaking the clean code principles. I am using EF and therefore getting the id from database is not a problem and in fact you can see it in my first example. Sorry for the confusion.
Have you considered raising an event? PersonCreatedEvent See also: http://blog.ploeh.dk/2014/08/11/cqs-versus-server-generated-ids/
Clearly you have problem in design. The fact that you return this PersonId means that you need it to identify the person in your domain logic (business logic if you will).
The question is: what database id has to do with person identity? Does it has any meaning in your domain?
If it doesn't then use some other means to identify Person in your domain logic. This would give you at least following benefits:
You will be able to create person with Factory, (if it is an entity and probably aggregate root), giving him meaningful to your domain logic identity.
You will be able to save newly created person with Repository and will not have to deal with it's database id in you domain logic.
Then you'll be able to write something like this:
var newPerson = PersonFactory.Create();
//do some work with person in domain logic
PersonRepository.Persist(newPerson);

Can I dynamically/on the fly create a class from an interface, and will nHibernate support this practice?

I’ve done some Googling but I have yet to find a solution, or even a definitive answer to my problem.
The problem is simple. I want to dynamically create a table per instance of a dynamically named/created object. Each table would then contain records that are specific to the object. I am aware that this is essentially an anti-pattern but these tables could theoretically become quite large so having all of the data in one table could lead to performance issues.
A more concrete example:
I have a base class/interface ACCOUNT which contains a collection of transactions. For each company that uses my software I create a new concrete version of the class, BOBS_SUB_SHOP_ACCOUNT or SAMS_GARAGE_ACCOUNT, etc. So the identifying value for the class is the class name, not a field within the class.
I am using C# and Fluent nHibernate.
So my questions are:
Does this make sense or do I need to clarify more? (or am I trying
to do something I REALLY shouldn’t?)
Does this pattern have a name?
Does nHibernate support this?
Do you know of any documentation on
the pattern I could read?
Edit: I thought about this a bit more and I realized that I don't REALLY need dynamic objects. All I need is a way to tie objects with some identifier to a table through NHibernate. For example:
//begin - just a brain dump
public class Account
{
public virtual string AccountName { get; set; }
public virtual IList Stuff { get; set; }
}
... somewhere else in code ...
//gets mapped to a table BobsGarageAccount (or something similar)
var BobsGarage = new Account{AccountName="BobsGarage"};
//gets mapped to a table StevesSubShop(or something similar)
var StevesSubShop = new Account{AccountName="StevesSubShop"};
//end
That should suffice for what i need, assuming NHibernate would allow it. I am trying to avoid a situation where one giant table would have the heck beat out of it if high volume occurred on the account tables. If all accounts were in one table... it could be ugly.
Thank you in advance.
Rather than creating a class on the fly, I would recommend a dynamic object. If you implement the right interfaces (one example is here, and in any case you can get there by inheriting from DynamicObject), you can write
dynamic bobsSubShopAccount = new DynamicAccount("BOBS_SUB_SHOP_ACCOUNT");
Console.WriteLine("Balance = {0}", bobsSubShopAccount.Balance);
in your client code. If you use the DLR to implement DynamicAccount, all these calls get intercepted at runtime and passed to your class at runtime. So, you could have the method
public override bool TryGetMember(GetMemberBinder binder, out object result)
{
if (DatabaseConnection.TryGetField(binder.Name, out result))
return true;
// Log the database failure here
result = null;
return false; // The attempt to get the member fails at runtime
}
to read the data from the database using the name of the member requested by client code.
I haven't used NHibernate, so I can't comment with any authority on how NHibernate will play with dynamic objects.
Those classes seem awfully smelly to me, and attempt to solve what amounts to be an actual storage layer issue, not a domain issue. Sharding is the term that you are looking for, essentially.
If you are truly worried about performance of the db, and your loads will be so large, perhaps you might look at partitioning the table instead? Your domain objects could easily handle creating the partition key, and you don't have to do crazy voodoo with NHibernate. This will also more easily permit you to not do nutty domain level things in case you change your persistence mechanisms later. You can create collection filters in your maps, or map readonly objects to a view. The latter option would be a bit smelly in the domain though.
If you absolutely insist on doing some voodoo you might want to look at NHibernate.Shards, it was intended for easy database sharding. I can't say what the current dev state and compatibility is, but it's an option.

DDD: Can a Value Object have lists inside them?

I'm not well versed in domain driven design and I've recently started created a domain model for a project. I still haven't decided on an ORM (though I will likely go with NHibernate) and I am currently trying to ensure that my Value Objects should be just that.
I have a few VOs that have almost no behavior other than to encapsulate "like" terms, for instance:
public class Referral {
public Case Case { get; set; } // this is the a reference to the aggregate root
public ReferralType ReferralType { get; set; } // this is an enum
public string ReferralTypeOther { get; set; }
} // etc, etc.
This particular class has a reference to "Case" which is two levels up, so if say I were going to access a Referral I could go: case.social.referral (Case, Social and Referral are all classes, there is a single Social inside a Case and there is a single Referral inside a Social). Now that I am looking at it as I type it, I don't think I need a Case in the Referral since it will be accessible through the Social entity, correct?
Now, there is no doubt in my mind this is something that should be a VO, and the method I plan to use to persist this to the database is to either have NHibernate assign it a surrogate identifier (which I am still not too clear on, if anyone could please elaborate on that too it would help me out, since I don't know if the surrogate identifier requires that I have an Id in my VO already or if it can operate without one) and/or a protected Id property that would not be exposed outside the Referral class (for the sole purpose of persisting to the DB).
Now on to my title question: Should a VO have a collection, (in my case a List) inside it? I can only think of this as a one-to-many relationship in the database but since there is no identity it didn't seem adequate to make the class an entity. Below is the code:
public class LivingSituation {
private IList<AdultAtHome> AdultsAtHome { get; set; }
public ResidingWith CurrentlyResidingWith { get; set } // this is an enum
} // etc, etc.
This class currently doesn't have an Id and the AdultsAtHome class just has intrinsic types (string, int). So I am not sure if this should be an entity or if it can remain as a VO and I just need to configure my ORM to use a 1:m relationship for this using their own tables and a private/protected Id field so that the ORM can persist to the DB.
Also, should I go with normalized tables for each of my classes, or not? I think I would only need to use a table per class when there is a possibility of having multiple instances of the class assigned to an entity or value object and/or there is the possibility of having collections 1:m relationships with some of those objects. I have no problem with using a single table for certain value objects that have intrinsic types but with nested types I think it would be advantageous to use normalized tables. Any suggestions on this as well?
Sorry for being so verbose with the multiple questions:
1) Do I need a surrogate identifier (with say NHibernate) for my value objects?
2) If #1 is yes, then does this need to be private/protected so that my value object "remains" a value object in concept?
3) Can a value object have other value objects (in say, a List) or would that constitute an entity? (I think the answer to this is no, but I'd prefer to be sure before I proceed further.)
4) Do I need a reference to the aggregate root from a value object that is a few levels down from the aggregate root? (I don't think I do, this is likely an oversight on my part when writing the model, anyone agree?)
5) Is it OK to use normalized tables for certain things (like nested types and/or types with collections as properties which would need their own tables anyway for the 1:m relationship) while having the ORM do the mapping for the simpler value objects to the same table that belongs to my entity?
Thanks again.
Take a look at the answers to related questions here and here
1) Yes - If you're storing VOs in their own table
2) If you can use a private/protected ID property, then great. Alternatively, you might use explicit interfaces to 'hide' the ID property.
But, reading into your question, are you suggesting that developers who see an ID property will automatically assume the object is an entity? If so, they need (re)training.
3) Yes it can, but with the following restrictions:
It should be quite rare
It should only reference other VOs
Also, consider this: VOs shouldn't stick around. Would it be easy/efficient to re-create the entire VO every time it's needed? If not, make it an Entity.
4) Depends on how you want to implement your Aggregate Locking. If you want to use Ayende's solution, the answer is yes. Otherwise, you would need a mechanism to traverse the object graph back to the Aggregate Root.
5) Yes. Don't forget that DDD is Persistence Ignorant (in an ideal world!).
However...
I believe Referral should be an Entity. Imagine these conversations:
Conversation 1:
Tom: "Hey Joe! Can you give me David Jone's referral?"
Joe: "Which one?"
Tom: "Sorry, I mean Referral No.123"
Conversation 2:
Tom: "Hey Joe! Can you give me David Jone's referral?"
Joe: "Which one?"
Tom: "I don't care - just give me any"
Conversation 1 suggests that Referral is an Entity, whereas conversation 2 suggests it's a VO.
One more thing: Does Referral.ReferralType change during it's lifetime (there's another hint that it should be an Entity)? If it doesn't change, consider using polyporphism and let NH handle it.
Hope that helps!

DDD: entity's collection and repositories

Suppose I have
public class Product: Entity
{
public IList<Item> Items { get; set; }
}
Suppose I want to find an item with max something... I can add the method Product.GetMaxItemSmth() and do it with Linq (from i in Items select i.smth).Max()) or with a manual loop or whatever. Now, the problem is that this will load the full collection into memory.
The correct solution will be to do a specific DB query, but domain entities do not have access to repositories, right? So either I do
productRepository.GetMaxItemSmth(product)
(which is ugly, no?), or even if entities have access to repositories, I use IProductRepository from entity
product.GetMaxItemSmth() { return Service.GetRepository<IProductRepository>().GetMaxItemSmth(); }
which is also ugly and is a duplication of code. I can even go fancy and do an extension
public static IList<Item> GetMaxItemSmth(this Product product)
{
return Service.GetRepository<IProductRepository>().GetMaxItemSmth();
}
which is better only because it doesn't really clutter the entity with repository... but still does method duplication.
Now, this is the problem of whether to use product.GetMaxItemSmth() or productRepository.GetMaxItemSmth(product)... again. Did I miss something in DDD? What is the correct way here? Just use productRepository.GetMaxItemSmth(product)? Is this what everyone uses and are happy with?
I just don't feel it is right... if I can't access a product's Items from the product itself, why do I need this collection in Product at all??? And then, can Product do anything useful if it can't use specific queries and access its collections without performance hits?
Of course, I can use a less efficient way and never mind, and when it's slow I'll inject repository calls into entities as an optimization... but even this doesn't sound right, does it?
One thing to mention, maybe it's not quite DDD... but I need IList in Product in order to get my DB schema generated with Fluent NHibernate. Feel free to answer in pure DDD context, though.
UPDATE: a very interesting option is described here: http://devlicio.us/blogs/billy_mccafferty/archive/2007/12/03/custom-collections-with-nhibernate-part-i-the-basics.aspx, not only to deal with DB-related collection queries, but also can help with collection access control.
Having an Items collection and having GetXXX() methods are both correct.
To be pure, your Entities shouldn't have direct access to Repositories. However, they can have an indirect reference via a Query Specification. Check out page 229 of Eric Evans' book. Something like this:
public class Product
{
public IList<Item> Items {get;}
public int GetMaxItemSmth()
{
return new ProductItemQuerySpecifications().GetMaxSomething(this);
}
}
public class ProductItemQuerySpecifications()
{
public int GetMaxSomething(product)
{
var respository = MyContainer.Resolve<IProductRespository>();
return respository.GetMaxSomething(product);
}
}
How you get a reference to the Repository is your choice (DI, Service Locator, etc). Whilst this removes the direct reference between Entity and Respository, it doesn't reduce the LoC.
Generally, I'd only introduce it early if I knew that the number of GetXXX() methods will cause problems in the future. Otherwise, I'd leave it for a future refactoring exercise.
I believe in terms of DDD, whenever you are having problems like this, you should first ask yourself if your entity was designed properly.
If you say that Product has a list of Items. You are saying that Items is a part of the Product aggregate. That means that, if you perform data changes on the Product, you are changing the items too. In this case, your Product and it's items are required to be transactionally consistent. That means that changes to one or another should always cascade over the entire Product aggregate, and the change should be ATOMIC. Meaning that, if you changed the Product's name and the name of one of it's Items and if the database commit of the Item's name works, but fails on the Product's name, the Item's name should be rolled back.
This is the fact that Aggregates should represent consistency boundaries, not compositional convenience.
If it does not make sense in your domain to require changes on Items and changes on the Product to be transactionally consistent, then Product should not hold a reference to the Items.
You are still allowed to model the relationship between Product and items, you just shouldn't have a direct reference. Instead, you want to have an indirect reference, that is, Product will have a list of Item Ids.
The choice between having a direct reference and an indirect reference should be based first on the question of transactional consistency. Once you have answered that, if it seemed that you needed the transactional consistency, you must then further ask if it could lead to scalability and performance issues.
If you have too many items for too many products, this could scale and perform badly. In that case, you should consider eventual consistency. This is when you still only have an indirect reference from Product to items, but with some other mechanism, you guarantee that at some future point in time (hopefully as soon as possible), the Product and the Items will be in a consistent state. The example would be that, as Items balances are changed, the Products total balance increases, while each item is being one by one altered, the Product might not exactly have the right Total Balance, but as soon as all items will have finished changing, the Product will update itself to reflect the new Total Balance and thus return to a consistent state.
That last choice is harder to make, you have to determine if it is acceptable to have eventual consistency in order to avoid the scalability and performance problems, or if the cost is too high and you'd rather have transactional consistency and live with the scalability and performance issues.
Now, once you have indirect references to Items, how do you perform GetMaxItemSmth()?
In this case, I believe the best way is to use the double dispatch pattern. You create an ItemProcessor class:
public class ItemProcessor
{
private readonly IItemRepository _itemRepo;
public ItemProcessor(IItemRepository itemRepo)
{
_itemRepo = itemRepo;
}
public Item GetMaxItemSmth(Product product)
{
// Here you are free to implement the logic as performant as possible, or as slowly
// as you want.
// Slow version
//Item maxItem = _itemRepo.GetById(product.Items[0]);
//for(int i = 1; i < product.Items.Length; i++)
//{
// Item item = _itemRepo.GetById(product.Items[i]);
// if(item > maxItem) maxItem = item;
//}
//Fast version
Item maxItem = _itemRepo.GetMaxItemSmth();
return maxItem;
}
}
And it's corresponding interface:
public interface IItemProcessor
{
Item GetMaxItemSmth(Product product);
}
Which will be responsible for performing the logic you need that involves working with both your Product data and other related entities data. Or this could host any kind of complicated logic that spans multiple entities and don't quite fit in on any one entity per say, because of how it requires data that span multiple entities.
Than, on your Product entity you add:
public class Product
{
private List<string> _items; // indirect reference to the Items Product is associated with
public List<string> Items
{
get
{
return _items;
}
}
public Product(List<string> items)
{
_items = items;
}
public Item GetMaxItemSmth(IItemProcessor itemProcessor)
{
return itemProcessor.GetMaxItemSmth(this);
}
}
NOTE:
If you only need to query the Max items and get a value back, not an Entity, you should bypass this method altogether. Create an IFinder that has a GetMaxItemSmth that returns your specialised read model. It's ok to have a separate model only for querying, and a set of Finder classes that perform specialized queries to retrieve such specialized read model. As you must remember, Aggregates only exist for the purpose of data change. Repositories only work on Aggregates. Therefore, if no data change, no need for either Aggregates or Repositories.
(Disclaimer, I am just starting to get a grasp on DDD. or at least believe doing it :) )
I will second Mark on this one and emphasize 2 point that took me some times to realize.
Think about your object in term of aggregates, which lead to
The point is that either you load the children together with the parent or you load them separately
The difficult part is to think about the aggregate for your problem at hand and not to focus the DB structure supporting it.
An example that emphasizes this point i customer.Orders. Do you really need all the orders of your customer for adding a new order? usually not. what if she has 1 millin of them?
You might need something like OutstandingAmount or AmountBuyedLastMonth in order to fulfill some scenarios like "AcceptNewOrder" or ApplyCustomerCareProgram.
Is the product the real aggregate root for your sceanrio?
What if Product is not an Aggregate Root?
i.e. are you going to manipulate the item or the product?
If it is the product, do you need the ItemWithMaxSomething or do you need MaxSomethingOfItemsInProduct?
Another myth: PI means You don't need to think about the DB
Given that you really need the item with maxSomething in your scenario, then you need to know what it means in terms of database operation in order to choose the right implementation, either through a service or a property.
For example if a product has a huge number of items, a solution might be to have the ID of the Item recorded with the product in the db instead of iterating over the all list.
The difficult part for me in DDD is to define the right aggregates. I feel more and more that if I need to rely on lazy loading then I might have overseen some context boundary.
hope this helps :)
I think that this is a difficult question that has no hard and fast answer.
A key to one answer is to analyze Aggregates and Associations as discussed in Domain-Driven Design. The point is that either you load the children together with the parent or you load them separately.
When you load them together with the parent (Product in your example), the parent controls all access to the children, including retrieval and write operations. A corrolary to this is that there must be no repository for the children - data access is managed by the parent's repository.
So to answer one of your questions: "why do I need this collection in Product at all?" Maybe you don't, but if you do, that would mean that Items would always be loaded when you load a Product. You could implement a Max method that would simply find the Max by looking over all Items in the list. That may not be the most performant implementation, but that would be the way to do it if Product was an Aggregate Root.
What if Product is not an Aggregate Root? Well, the first thing to do is to remove the Items property from Product. You will then need some sort of Service that can retrieve the Items associated with the Product. Such a Service could also have a GetMaxItemSmth method.
Something like this:
public class ProductService
{
private readonly IItemRepository itemRepository;
public ProductService (IItemRepository itemRepository)
{
this.itemRepository = itemRepository;
}
public IEnumerable<Item> GetMaxItemSmth(Product product)
{
var max = this.itemRepository.GetMaxItemSmth(product);
// Do something interesting here
return max;
}
}
That is pretty close to your extension method, but with the notable difference that the repository should be an instance injected into the Service. Static stuff is never good for modeling purposes.
As it stands here, the ProductService is a pretty thin wrapper around the Repository itself, so it may be redundant. Often, however, it turns out to be a good place to add other interesting behavior, as I have tried to hint at with my code comment.
Another way you can solve this problem is to track it all in the aggregate root. If Product and Item are both part of the same aggregate, with Product being the root, then all access to the Items is controlled via Product. So in your AddItem method, compare the new Item to the current max item and replace it if need be. Maintain it where it's needed within Product so you don't have to run the SQL query at all. This is one reason why defining aggregates promotes encapsulation.
Remember that NHibernate is a mapper between the database and your objects. Your issue appears to me that your object model is not a viable relational model, and that's ok, but you need to embrace that.
Why not map another collection to your Product entity that uses the power of your relational model to load in an efficient manner. Am I right in assuming that the logic to select this special collection is not rocket science and could easily be implemented in filtered NHibernate mapped collection?
I know my answer has been vague, but I only understand your question in general terms. My point is that you will have problems if you treat your relational database in an object oriented manner. Tools like NHibernate exist to bridge the gap between them, not to treat them in the same way. Feel free to ask me to clarify any points I didn't make clear.
You can now do that with NHibernate 5 directly without specific code !
It won't load the whole collection into memory.
See https://github.com/nhibernate/nhibernate-core/blob/master/releasenotes.txt
Build 5.0.0
=============================
** Highlights
...
* Entities collections can be queried with .AsQueryable() Linq extension without being fully loaded.
...

Categories