NHibernate Query Strategies in Web Applications

Web application scripts usually have two requirements regarding data retrieval: 1. All requests must be additionally uploaded 2. There are many “filtering” requests that a user needs (for example, searching for a person by the name AND mail AND age) 3. Sorting requests that are implied some network of clients.

And, of course, the combined case: Filtered, sorted by request queries :)

These requirements can lead to a large number of repository methods in the data layer, each of which has many parameters. Are there any general patterns for a more dynamic behavior of this process (for example, automatically generated queries depending on the properties of a domain class)?

I know that repository methods should be clean and well defined. But at the moment, it seems to me that I write a lot of template code every time I add some form of filtering to the MVC view or some sortable swap table.

How do other developers deal with such requirements?

Thank you in advance

+6
source share
5 answers

The repositories, shared repositories, and repositories that IQueryable expose are very heated debates.

The bottom line is a repository that is shared or provides an IQueryable that is not a real repository at all, it's just an abstraction of your data level.

Now it’s not bad, but don’t call it a repository, call it what it is. Abstraction of the data level allows you to quickly insert something into your interface, where you read and write objects, without missing the data frame in the user interface. Of course, you can just enter ISession and do it.

 public interface IRepository<T> {} public class NHibernateRepository<T> : IRepository<T> { private ISession session; public NHibernateRepository(ISession session) { this.session = session; } T Get(object id) { return session.GetById<T>(id)); } IQueryable<T> Query { get { return session.Query<T>(); } } new NHibernateRepository<Customer>(session).Query().Where(customer => customer.Name == Fred); 

However, if you want to capture some reusable logic and provide a clear contract between your services or user interface and your data level, then Repository will be just that. You define clear methods that say what they extract and how. In addition, with the repository, you only want to expose the aggregate roots , these are the root objects that all your other data hangs on, these can be things like Customer and Supplier . You would not try to directly contact the address to download the client, and then request its address. You will download the list of suppliers based on what they offer, but you will not go through the “ItemsRepository”. My examples may not be the best, but they give you an idea.

 public class CustomerRepository { public Customer GetCustomerWithName(string name); } public class SupplierRepository { public IEnumerable<Supplier> GetSuppliersWhoStockItem(string itemName) } 

Finally, if your adventure person may want to take a look at CQRS , then here is a great object, but there are many examples.

The former implements faster, the latter gives a clearer reuse code, the third gives you separation between the user interface layer, but requires more work on the ground. It depends on what you need and what you want, and probably this order should be decided.

+7
source

In our project, we also used repositories, an entity repository, but I don’t like it. This can make a lot of problems when you write complex queries with a lot of interacting objects. I think this will be one common repository for basic operations, and all requests should be present with the request object template, something like a separate class for each request, although look at:

http://richarddingwall.name/2010/06/15/brownfield-cqrs-part-1-commands/

http://codebetter.com/gregyoung/2009/01/20/ddd-specification-or-query-object/

http://devlicio.us/blogs/casey/archive/2009/02/13/ddd-command-query-separation-as-an-architectural-concept.aspx

http://blog.jonathanoliver.com/2009/10/dddd-why-i-love-cqrs/

http://www.udidahan.com/2007/03/28/query-objects-vs-methods-on-a-repository/

+3
source

You use the repository to list the aggregate root. Quite often, your controllers work with an aggregated root, which you can filter, sort, etc. As needed.

Therefore, I use the repository in accordance with what has already been mentioned.

However, sometimes I have to work in more complex specifications, where using the aggregate root and / or a whole group of repositories is either painful, inefficient, or simply impossible. For example, you may need to run a large business report, or perhaps run a batch command.

In such cases, I also defined ICommand / IQuery , with the NH base, to take care of the plumbing material (as the general Repository does).

What I then do is make an interface that represents a contract for specification, exposing any elements that I may need to help me build the necessary parameters. Then I do the implementation of this specification, using NH as the basis, executing the specification, using any method that is most suitable (HQL statement, raw SQL, criteria, QueryOver ... whatever).

Here is a rough illustration of what I mean. Please note that I use an arbitrary ICommandProvider , which is some object that creates new instances of the command as needed (in case you need to issue several commands in one operation). I would register my teams using IoC and ask the provider to work with him to create instances of the teams.

 public interface ICommand { } public interface ICommandProvider { TCommand Create<TCommand>() where TCommand : ICommand; } public interface IQuery<TResult> : ICommand { TResult Execute(); } public class NhCommand : ICommand { // plumbing stuff here, like finding the current session } public class DelinquentAccountViewModel { public string AccountName { get; set; } public decimal Amount { get; set; } } public interface IDelinquentAccountsQuery : IQuery<IEnumerable<DelinquentAccountViewModel>> { void AmountGreaterThan(decimal amount); // you could define members for specifying sorting, etc. here } public class DelinquentAccountsQuery : NhCommand { public IEnumerable<DelinquentAccountViewModel> Execute() { // build HQL and execute results, resulting in a list of DelinquentAccountViewModels // using _amountGreaterThan as a parameter return null; } private Decimal _amountGreaterThan; public void AmountGreaterThan(Decimal amount) { _amountGreaterThan = amount; } } 

Use in the controller might be something like this:

 public class DelinquentAccountsController : Controller { protected ICommandProvider CommandProvider { get; private set; } public DelinquentAccountsController(ICommandProvider commandProvider) { CommandProvider = commandProvider; } public ActionResult Index(decimal amount) { var query = CommandProvider.Create<IDelinquentAccountsQuery>(); query.AmountGreaterThan(amount); return View(query.Execute()); } } 

Nothing is said that you cannot perform all data access with a command / query, but this works more than I need. I believe that the standard repository method (using LINQ against NHibernate) handles 95% or so access to the data that my applications require.

+3
source

You can get your repository to provide IQueryable and let ActionMethod figure out what to display. For instance:

  public System.Linq.IQueryable<Models.MyModel> Query() { return mSession.Query<Models.MyModel>(); } 
+1
source

Imho orm is already enough abstraction. Instead, you do not need repositories. You need an abstraction if you are going to change orm on the fly with some settings. Where, Skip, Take, OrderBy, etc. Are orm agnostics and can be used by exposing IQueryable. But some functions are specific to orm (for example, the vetch include selection), and they make the repositories really ugly (a million methods or methods with a million parameters)

I usually just create extension methods

 public static class QueryExtensions { public static IQueryable<T> Published(this IQueryable<T> pages) where T : IPage { return pages.Where(p => p.State == PageState.Public && p.Published <= DateTime.UtcNow); } public static IQueryable<T> By(this IQueryable<T> pages, User author) where T : IPage { return pages.Where(p => p.Author == author); } public static IEnumerable<Foo> AdvancedThing(this ISession session, string text) { // I get all the power of NHibernate :) return session.CreateQuery("...").SetString("text", text).List<Foo>(); } } 

and use ISession directly in action methods

 var posts = session.Query<Post>().By(user).Published().FetchMany(p => p.Tags).ToList(); 
+1
source

Source: https://habr.com/ru/post/911397/


All Articles