Does Queryability and Lazy Loading in C # blur Data Access vs Business Logic strings?

I am experiencing a philosophical architectural crisis in high school. I see very clear lines between what is considered client code (UI, Web Services, MVC, MVP, etc.) and the level of service. However, lines from the Service layer again become more blurry. And it all started with the ability to request code with Linq and the Lazy loading concept.

I created a business layer that consists of Contracts and Implementations. Then implementations may have dependencies on other Contracts, etc. This is handled through an IoC container with DI. There is one service that processes DataAccess, and all that it does is return UnitOfWork. This UnitOfWork creates a transaction when it expresses and commits data using the Commit method. [ View this article (Testability and Entity Framework 4.0) :

public interface IUnitOfWork : IDisposable { IRepository<T> GetRepository<T>() where T : class; void Commit(); } 

The repository is shared and works against two implementations (EF4 and InMemory DataStore). T consists of POCOs that are generated from a database schema or EF4 mappings. Testing capability is built into the repository project. We can use the in-memory implementation to validate results with anticipation.

 public interface IRepository<T> where T : class { IQueryable<T> Table { get; } void Add(T entity); void Remove(T entity); } 

While the data source is abstracted, IQueryable still gives me the ability to create queries anywhere in the Business logic. Here is an example.

 public interface IFoo { Bar[] GetAll(); } public class FooImpl : IFoo { IDataAccess _dataAccess; public FooImpl(IDataAccess dataAccess) { _dataAccess = dataAccess; } public Bar[] GetAll() { Bar[] output; using (var work = _dataAccess.DoWork()) { output = work.GetRepository<Bar>().Table.ToArray(); } return output; } } 

Now you can see how queries can become even more complex as you perform joins with complex filters.

So my questions are:

  • Does it matter that there is no clear distinction between BLL and DAL? .
  • Is the query data accessible or business logic beyond the repository level, which acts as an InMemory abstraction?

Addition: The more I think about it, perhaps the second question was the only one that should be asked.

+6
c # linq ioc-container lazy-loading separation-of-concerns
source share
3 answers

I think the best way to answer your questions is to take a step back and think about why it is recommended to distinguish between the levels of business logic and the level of access to data.

In my opinion, the reasons are simple: keep the business logic separate from the data level, because business logic is a value, the data level and business logic will have to change more or less independently over time, and the business logic should be readable, without detailed knowledge of what the entire level of data access does.

So, the litmus test for your gymnastics request comes down to the following:

  • Can you make changes to the data schema in your system without breaking a significant part of the business logic?
  • Is your business logic available to you and other C # developers?
+6
source share

1. Only if you care more about philosophy than about doing something useful. :)

2. I would say that this is business logic, because there is an abstraction between you. I would call this part of the DAL repository layer and everything that uses it is BL.

But yes, that too is vague. I do not think this is important. The point of using such patterns is to write clean, useful code that can be easily exchanged at the same time, and this goal is fulfilled anyway.

+1
source share

1. Does it matter that there is no clear distinction between BLL and DAL ?.

Of course, this is important! Any programmer using the Table property must understand the fork (database callback, query translation, object tracking). This also applies to programmers reading business logic classes.

2. The information requested by the user is considered as access to data or business logic, if behind the layer of the repository, which acts as an abstraction of InMemory?

Abstraction is a blanket in which we hide our problems.

If your abstraction is perfect, queries can be abstractly viewed as working with collections in memory, and therefore they are not data access.

However, abstraction leakage. If you want queries that make sense in the data world, you must make efforts to work on the abstraction and beyond. This extra effort (which defeats abstraction) creates a data access code.


Some examples:

 output = work.GetRepository<Bar>().Table.ToArray(); 

This code is (abstract) excellent. But in the data world, this leads to a scan of the entire table and (at least in general) dumb!


 badquery = work.GetRepository<Customer>().Table.Where(c => c.Name.Contains("Bob")).ToArray(); goodquery = work.GetRepository<Customer>().Table.Where(c => c.Name.StartsWith("Bob")).ToArray(); 

Goodquery is better than a bad query when there is an index on Customer.Name . But this fact is not available to us unless we raise the abstraction.


 badquery = work.GetRepository<Customer>().Table .GroupBy(c => c.Orders.Count()) .Select(g => new { TheCount = g.Key, TheCustomers = g.ToList() }).ToArray(); goodquery = work.GetRepository<Customer>().Table .Select(c => new {Customer = c, theCount = c.Orders.Count()) .ToArray() .GroupBy(x => x.theCount) .Select(g => new { TheCount = g.Key, TheCustomers = g.Select(x => x.Customer).ToList() }) .ToArray(); 

goodquery is better than a bad query, because badquery will query the group key database for each group (and, even worse, it is very unlikely that there is an index that will help c.Orders.Count() clients c.Orders.Count() ).


Testability is built into the repository project. We can use the InMemory implementation to validate results with anticipation.

Resist the illusion that your queries are checked if you actually run them in collections in memory. These queries are not subject to testing if the database is not used.

+1
source share

All Articles