Limit collection to retrieve only recent entries f

2019-02-25 00:44发布

问题:

The User entity can have thousands of UserOperations. Sometimes I don't want to retrieve (for readonly entity) all of them but only "the recent 10 OR not completed".

public class SimpleForm
{
    public class User : EntityBase
    {
        //  ...

        private ISet<UserOperation> _recentOperations = new HashedSet<UserOperation>();
        public virtual ISet<UserOperation> RecentOperations { get { return _recentOperations; } set { _recentOperations = value; } }
    }
}

So how can I specify it? I think I could use mapping overrides?

I understand I could make this with a seperate query but can it be done by entity mapping?

Also I wonder if there is a possibility to do the some for non-readonly entity where I can modify the collection of operations?

UPDATE

I tried to use

DateTime dateTime = (DateTime.UtcNow - TimeSpan.FromDays(15));
mapping.HasMany(x => x.RecentOperations)
       .Where(x => x.EndedAt == null || x.EndedAt < dateTime);

but it says "Unable to convert expression to SQL".

I replaced it with

mapping.HasMany(x => x.RecentOperations)
                .Where(x => x.EndedAt == null);

and now it throws null reference exception inside

в FluentNHibernate.Utils.ExpressionToSql.Convert(Object value) в FluentNHibernate.Utils.ExpressionToSql.Convert(ConstantExpression expression) в FluentNHibernate.Utils.ExpressionToSql.Convert[T](Expression`1 expression, UnaryExpression body)

回答1:

There are 2 general ways how to filter mapped collections.

The first is a bit rigid, fixed, in a mapping defined where="" clause:

  • 6.2. Mapping a Collection (...in fluent .Where(bool expr) or .Where(Sql statement string)

The second and maybe really suitable in this scenario, is dynamic version called filter:

  • 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 implementation in fluent would look like this:

public class RecentFilter : FilterDefinition
{
    public RecentFilter()
    {
        WithName("RecentFilter")
            .WithCondition("( :EndedAtDate IS NULL OR EndedAt < :EndedAtDate )")
            .AddParameter("EndedAtDate",NHibernate.NHibernateUtil.DateTime);
    }
}

this is the filter, and here is its usage in a fluent mapping:

mapping
   .HasMany(x => x.RecentOperations)
   ...
   .ApplyFilter<RecentFilter>();

In runtime, we can turn filter on/off on the ISession level:

session.EnableFilter("RecentFilter")
       .SetParameter("EndedAtDate",DateTime.Now.AddDays(-15));

See also:

  • property filter with fluent nHibernate automapping
  • Syntax to define a NHibernate Filter with Fluent Nhibernate?
  • Is it possible to use NHibernate Filters to filter through references?