How to scale service layer methods - c#

We are at the design stage of developing an application.
We decided to implement DDD oriented design.
Our application has a service layer.
Each method in the service has its own task.
Sample
let's consider a user service.
This method gets all users
public User GetAll()
{
//codes
}
Now if I want to sort all users by name.
Option-1
Use another method
public User GetAllAndOrderByAsc()
Or
public User GetAllAndOrderByDesc()
Different method for each different situation.
it doesn't look good at all
Option-2
Continue query at Api or Application level
public IQueryable<User> GetAll()
{
//codes
}
Api or Application Level
var users = from u in _userService.GetAll()
select u;
switch (sortOrder)
{
case "name_desc":
users = users.OrderByDescending(u => u.Name);
break;
case "name_asc":
users = students.OrderBy(u => u.Name);
break;
default:
users = students.OrderBy(u => u.Name);
break;
}
The options that come to my mind are limited to these.
am I making a mistake somewhere or couldn't grasp the logic. would you help me?

You're not crazy: this is a common problem, and the options you've listed are legitimate, each with pros and cons, and commonly used in practice.
Depending on the context of your app, it's theoretically possible to expose the IQueryable<> all the way to the presentation layer (using OData if you're running a web service, e.g.). But I find that it's wiser to exercise a little more control over the API to avoid the possibility of having someone hit the endpoint with a request that basically downloads your entire database.
In your service layer, you want to think about what use cases you really expect people to use.
It's rare for someone to literally need to see all the users: that might only happen when it's part of an export process, and rather than returning a collection you might want to return an IObservable that pushes values out as they emerge in a stream from the database.
More often, you're going to want to get a page of users at a time to show in the UI. This will probably need to have sorting and searching applied to it as well.
You'll almost certainly want a way to get one user whose ID you already know (maybe one that was picked from the paged list).
You might also expect to need to query based on specific criteria. Maybe people should typically only see "Active" users. Maybe they should be thinking in terms of specific groupings of users.
In my experience, it has worked well to make my Service layer represent these Domain-level assumptions about how the application should think about things, and write interfaces that support the use cases you actually want to support, even if that means you often find yourself adding new Service methods to support new features. For example:
Task<IReadOnlyCollection<User>> GetPageOfActiveUsersAsync(
PagingOptions pagingOptions,
SortOptions sortOptions,
string searchTerm,
IReadOnlyCollection<int> organizationIds);
Task<User> GetActiveUserByUsernameAsync(string username);
Task<User> GetActiveUserByIdAsync(int userId);
IObservable<User> GetAllUsersForAuditExport();
The project I currently work on uses a class like this:
public class SortSettings
{
public string SortField { get; set; }
public SortDirection SortDirection { get; set; } = SortDirection.Asc;
}
Then, depending on what we're querying, we may use reflection, a switch statement, or some other mechanism to pick a SortExpression (Expression<Func<TSource, TResult>>), and then apply the sort like this:
query = sortOptions.SortDirection == SortDirection.Asc
? query.OrderBy(sortExpression)
: query.OrderByDescending(sortExpression);
That logic is fairly easy to extract out into a separate method if you find you're using it in a lot of places.
You'll likely find other common features like Searching and Paging require a similar approach.

Related

API Controller Design - How much business logic is too much logic? Checking for user in database before inserting a new record

I'm writing a REST API using ASP.NET Core & Entity Framework Core. My controllers so far have minimal logic, typically deferring to a service layer that handles the usual database operations.
My API will be consumed by a Discord bot, and I want to be able to automatically register users in my database when they issue a command that would essentially end up inserting a new record into one of the database tables:
[HttpPost]
public async Task<ActionResult<recordDto>> CreateRecord(CreateRecordDto recordDto)
{
var record = new Record
{
Name = recordDto.Name,
Alias = recordDto.Alias,
UserId = recordDto.UserId,
ServerId = recordDto.ServerId,
};
try
{
await _service.CreateRecord(record);
}
catch (status.ConflictException)
{
return Conflict();
}
catch (DbUpdateException)
{
throw new Exception("Error adding record.");
}
return CreatedAtAction("Test", new { id = record.Id }, record);
}
Prior to that, however I need to check whether the user is even present in the database. That's easy, as I have another service I can use to check for that condition. However I'm hesitant to place this logic into the controller as it would increase the dependencies my controller requires, and introduce more business logic which I understand the controller shouldn't be handling. Am I over thinking my design? Where exactly should this logic be handled?
Whilst I do not know what business this API is intended for, what you're showing and describing seems more like a CRUD API. I do not see any business logic here, unless you are automating a process where a business rule is something like "A user cannot be added twice".
But this doesn't take away from your question. You worry about too many dependencies and where the correct place is this logic should be handled.
It is important to understand why there are certain rules. Developers worry that if there are too many dependencies, a class has too many responsibilities and therefore becomes inflexible, difficult to test and difficult to change. This is a valid concern, but always ask yourself if it applies to your situation; will your API ever do anything else than this? Does it, therefore, need to be flexible? The adding of an extra layer does not come for free, so a simple design could be kept simple (even when breaking the "rules").
In your case I indeed think you are overthinking your design considering the dependencies. If I could give you a tip: handle the error handling somewhere else (in a filter attribute or something), so you don't have to copy that in every endpoint. But, only if you intend to have more than one!

C#: How to Apply Serialization in API Controller Level to Obtain Certain Columns

We currently have Repository Layer and App Service Layer.
Repo gets data from SQL Server database with Entity Framework.
App Service Layer Collects data, and does more things: send emails, parse flat files,
Repo Layer
public Task<Sales> GetBySalesId(int salesId)
{
var salesData = _context.Sales
.Include(c => c.Customer)
.FirstOrDefaultAsync(c => c.SalesId == salesId);
return salesData ;
}
Service Layer:
public async Task<SalesDto> GetSalesByIdAppService(int salesId)
{
var salesData = await _salesRepository.GetBySalesId(salesId);
var salesDto = _mapper.Map<SalesDto>(salesData);
return salesDto;
}
This is currently working fine. However tomorrow, one of my colleagues may require More columns, when they aren't needed in my specific portion of application.
Here two more Linq includes are added: However, I do not need Product or Employee.
New Repo Addition:
public Task<Sales> GetBySalesId(int salesId)
{
var salesData = _context.Sales
.Include(c => c.Customer)
.Include(c => c.ProductType)
.Include(c => c.Employee)
.FirstOrDefaultAsync(c => c.SalesId == salesId);
return salesData ;
}
Background One Suggestion is create Another Middle Domain Layer that everyone can utilize. In the API DTO Level, everyone can have separate DTOs, which only collects the only required class members from Domain. This would essentially entail creating Another layer, where DTO is a subset of the new "Domain" layer.
*Another suggestion, is to apply serialization only for the columns which are required. I keep hearing about this, however, how can this be done? Is it possible to application serialization to the Controller API without adding another layer? Does Newtonsoft have a tool, or any syntax in C# ?
API Controller
public async Task<ActionResult<SalesDto>> GetSalesBySalesId(string salesId)
{
var dto = await _service.GetBySalesId(salesId);
return Ok(dto);
}
JSON Ignore may not work , because we all share same DTO, and ignoring for one area, may be required for other part of application.
Decorate your members in class with [JsonIgnore] attribute which are NOT required in response. JsonIgnore is available in
System.Text.Json.Serialization namespace.
public class SalesDto
{
[JsonIgnore]
public string Customer { get; set; }
public string ProductType { get; set; }
public string Employee { get; set; }
}
Bind all properties of model with data and when you send it to UI, Customer property would not be available in response.
We should fetch all the data from database and the process that data in our presentation layer. GraphQL, could be a winner for this scenario but need to explore
OData as a Middle Domain Layer can be useful to support this type of requirement. It gives the caller some control over the shape of the Object Graph that is returned. The code to acheive this is too involved to include as a single POST, however these types of requirements are often better solved through implementing an architecture that is designed specifically to deal with them, rather than rolling your own quick fix.
You can also look at GraphQL that allows greater flexibility over the shape of the returned data, however that is a steeper learning curve for the server and client side.
The problem with just using JsonIgnore is that this is a permanent definition on your DTO, which would make it hard to use the same DTO definitions for different applications that might require a different shape/view of the data.
A solution to that problem is then to create a tightly-coupled branch of DTOs for each application that inherits from the base but overrides the properties by decorating their properties with JsonIgnore attributes.
You want to avoid tightly coupled scenarios where the UI enforces too much structure on your data model, this can will make it harder to maintain your data model and can lead to many other anti-patterns down the track.
OData allows a single set of DTOs that have a consistent structure in the backend and the client end, whilst allowing the client end to reduce/omit/ignore fields that it either does not know about yet or does not want transmitted.
The key here is that the Client now has (some) control over the graph, rather than the API needing to anticipate or tightly define the specific properties that each application MUST consume.
It has a rich standard convention based query language that means many off the shelf products may be able to integrate directly with your API
Some Pros for consideration:
Single Defintion of DTOs
Write once, defining the full structure of your data model that all applications will have access to.
Clients can specify the fields that they need through query projections.
Expose Business Logic through the API
This is a feature of all APIs / Service layers, however OData has a standard convention for supporting and documenting custom business Functions and Actions
Minimal DTOs over the wire
OData serializer can be configured to only transmit properties that have values across the wire
Callers can specify which fields to include in query results
You can configure the default properties and navigation links to send for resource requests when the client does not specify the projection.
PATCH (delta) Updates
OData has a simple mechanism to support partial updates to objects, you only send the changed properties over the wire for any object
Sure, this is really part of the previous point, but it is a really strong argument for using OData over a standard REST based API
You can easily inject business rules into the query validation pipeline (or the execution) to prevent updates to specific fields, this can include role based or user based security context rules.
.Net LINQ Support
Whilst the API is accessed via URL conventions, these conventions can be easily mapped to .Net LINQ queries on the client side via the ODataLib or Simple OData Client.
Not all possible LINQ functions and queries are supported, but there is enough there to get the job done.
This means you can easily support C# client side applications.
Versioning can be avoided
This becomes a Con as well, however additive changes to the data model can be easily absorbed into the runtime without having to publish a new version of the API.
All of the above points contribute to allow Additive changes to be freely supported by down level clients without interruptions.
It is possible to also map some old fields across to new definitions in cases where you want to rename fields, or you can provide default implementations for fields that have been removed.
The OData Model is a configuration that defines how URL requests are mapped or translated to endpoints on the API controllers, meaning there is a layer where you can change how client requests are mapped to
Key Cons to be aware of:
Performance - OData API uses HTTP protocols, so there is an inherent performance hit when compared to local DLL calls, windows services or RPC, even when the API and Application are located on the same machine
This performance hit can be minimised and is usually an acceptable overall cost
GraphQL and most other REST/HTTP based API architectures will have similar JSON over HTTP performance issues.
OData is still a Good fit for JSON over HTTP based remote API hosting scenarios, like javascript based clients.
Poor support for updating related objects
While PATCH is great for single objects, it doesn't work for nested object graphs, to support updates to nested objects you either need to manually keep a repository of changes on the client side and manually call path on each of the nested objects.
The OData protocol does have a provision for batching multiple queries so they can be executed as an atomic operation, but you still have to construct the individual updates.
There is a .Net based OData Client Generation Tool can be used to generate a client side repo to manage this for you.
How often are you expecting your client to send back a rich collection of objects to update in a single hit?
Is it a good idea to allow the client to do this, does an omitted field from the client mean we should set that property to null, does it mean we should delete that related data?
Consider creating actions on the API to execute operations that affect multiple records to keep your clients thin and to consolidate logic so that each of your client applications does not have to re-implement the complex logic.
Version Support
Versioning can be a long term issue if you want to allow destructive changes to your data model and DTOs. While the standard URL conventions support versioning the code to implement it is still complex
Versioning can be hard to implement in any APIs, however the ability to set default projections for each DTO and with the client able to control their own specific projections means that the OData model is more resilient to additive-only changes, such as adding more tables, or fields.
Additive changes can be implemented without interrupting client applications.
Fixed Schema
Although clients can request specific fields to be sent over the wire for the DTOs, including navigation properties, the client cannot easily request the data to come back in a totally different structure. Clients can only really request that certain fields are omitted from results.
There is support for $apply URL parameter to return the results of aggregate operations, but this doesn't provide full control over the shape of the results.
GraphQL does address this exact issue. Moving the mapping from the API to the client side, giving the client a more control over the schema.
the solution for this problem is very simple , you can use half generic query for this , WITHOUT changing anything in your DTO.
first let your repo function takes a generic include like this:
public Task<Sales> GetBySalesId(string salesId, Func<IQueryable<Sales>, IIncludableQueryable<Sales, object>> include = null)
{
var query = _context.Sales.Where(x => x.Id == salesId);
if (include != null)
query = include(query);
var salesData = query.FirstOrDefaultAsync();
return salesData;
}
this can be used in service layer like this:
public async Task<Sales> GetById(string salesId)
{
var result = await _yourRepo.GetBySalesId(salesId,
include: source => source
.Include(a => a.SOMETHING)
.Include(a => a.SOMETHING)
.ThenInclude(a => a.SOMETHING));
return result;
}
now to specialize the query result you can either do it based on your token (if you are using authorization in your api) or can create a number of service functions , call them based on a condition you recieve in the controller like an integer or something.
public async Task<Sales> test(string salesId)
{
Func<IQueryable<Sales>, IIncludableQueryable<Sales, object>> include = null;
if (UserRole == "YOU")
include = a => a.Include(a => a.SOMETHING);
else if (UserRole == "SomeoneElse")
include = a => a.Include(a => a.SOMETHING).ThenInclude(a=>a.SOMETHINGELSE);
var result = await _yourRepo.GetBySalesId(salesId,
include: include);
return result;
}
First of all
your logic is strange: you request DB return all columns and then take only few needed, it is inefficient. Imagine you have 20 columns...
var salesData = await _salesRepository.GetBySalesId(salesId);
var salesDto = _mapper.Map<SalesDto>(salesData);
Should a sales repository be able to include customers and products?
It is probably a holywar subject. Generally speaking, if your architecture wont allow you to switch from DB to file storage and from ASP.NET MVC to Console App, most likely it has design flaws (and it might be perfectly OK for your company current needs)
Summary
You need to make more service methods that do build results not just transferring data from Repo to caller as is.
For your case you need your service to cover more scenarios
AppService.GetSalesById(salesId)
AppService.GetSalesWithProductsById(sales)
AppService.GetSalesById(salesId, includeProducts, includeCustomers)
My personal preference is to change multiple parameters with Commands and make service methods return Results.
If your colleague were to add say 2 columns - it is easier to add them to existing result, if colleague is writing something new – its better to introduce new Method and result
Commands and results
Command stands for some situation and its variations, service looks nice are clean. This approach is time tested on one of my projects for last 10 years. We have switched database 3 times, several ORMs and 2 UIs. To be specific we use ICommand and IResult to make it super flexible.
API Controller
public async Task<ActionResult<SalesDto>> GetSalesBySalesId(string salesId)
{
UISalesTable dto = await (UISalesTable) _service.GetSales(new GetSalesForUICommand
{
SalesId = salesId,
IncludeProductType = true,
IncludeCustomer = false
});
return Ok(dto);
}
public async Task<ActionResult<MonthlySales>> GetSalesReport(string salesId)
{
MonthlySales dto = await (MonthlySales) _service.GetSales(new GetMonthlySalesReportCommand
{
SalesId = salesId,
// extra filters goes here
});
return Ok(dto);
}
Service layer
You make as many DTOs as there are results (it costs nothing)
public async Task<UISalesTable> GetSales(GetMonthlySalesReportCommand command)
{
UISalesTable result = new UISalesTable();
// good place to use Builder pattern
result.SalesByMonthes = .....;
TopProductsCalculator calc = new TopProductsCalculator();
result.TopProducts = calc.Calculate(command.FromDate, command.ToDate);
result.Etc = .....;
return result;
}
It all depends
Unfortunately, there is no recipe. It is always tradeoff between quality and time-to-market. Last years I preferer to keep things simple and even abandoned idea of repositories and now I work with DataContext directly because if I were to switch say to MongoDB, I would have to write every single repository method again and it happens few times in a life.

ASP.NET Core Custom Policy Based Authorization - unclear

OK, Custom Policy Based Authorization in ASP.NET Core. I kinda of understood the idea of this new identity framework, but still not 100% clear what you can achieve with this. Assuming we have an Action in HomeController called List. This action will query and display a list of products from the database. The users that must access this list must be part of the Marketing division. Therefore in our policy we check if user has a claim called Division and the value of that is Marketing. If yes then he will be allowed to see the list otherwise not. We can decorate our action like this:
[Authorize(Policy = "ProductsAccess")]
public IActionResult List()
{
//do query and return the products view model
return View();
}
All good with this. It will work perfectly.
Scenario 1: What if I want to add the policy at the product level, and based on the policy the user will see only the products from his division. So marketing guy will see his products, R&D will see his and so forth. How can I achieve that? Can it be done with a policy? If yes how?
Scenario 2: What about access at the field level? Let's say maybe I want to hide certain fields? Example: all products will have certain columns that must be visible to Managers and hidden to the rest of users? Can this be done using custom policies? If yes how?
For Scenario 1 you can use resource based authorization.
In essence, you'd inject IAuthorizationService into your service or controller, then and have one or more authorization handlers which derive form AuthorizationHandler<TRequirement, TDocument> and then call
if(await _authorizationService.AuthorizeAsync(User, document, "MyPolicy"))
{
// Success, user has access to it
}
Downside: You have to fetch all products from database, then filter in memory, so it will work well for single documents or smaller data, where you don't need pagination. Pagination will break it, even on smaller data (i.e. if you request 50 products, but user don't have access to 40 of them, only 10 will be returned, despite the page size being 50).
an alternative will be possible with EF Core 2.0 (that's if you use EF Core as your ORM). You can add global filters, which will applied to all queries to a certain entity.
For more information, see Entity Framework Core 2.0 Announcement blog post:
public class BloggingContext : DbContext
{
public DbSet<Blog> Blogs { get; set; }
public DbSet<Post> Posts { get; set; }
public int TenantId {get; set; }
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.Entity<Post>()
.HasQueryFilter(p => !p.IsDeleted &&
p.TenantId == this.TenantId );
}
}
It may or may not be suitable for your case, it depends if you have a row-level data you can use (i.e. some kind of "resource owner" field).
Scenario 2 isn't possible out of the box with Identity as far as I know, you'd have to implement something on your own, but it's a very complex topic (if you ever worked with Dynamics CRM, you know what I mean).
Update
Just for an quick implementation, you can try to wrap your response around an ExpandoObject (that's what's used underlying when you use dynamic keyword) and the iterate over it, removing properties the user doesn't have access to before returning it from the controller action or write an Authorization filter, which will automatically do that for specific or all controllers.
For an rough idea (on how to construct/use the expando object), see my answer here.
I don't think policies were designed to solve the cases you have. I'm uncertain if it's possible, and even if it would, I feel like the code itself would suffer from the complexity and confusion it would bring. I wouldn't give my authorization filters that much responsibility.
As for your second scenario, you could just skip outputting certain data in your view based on the role, claim or whatever of the current user. Seems unnecessarily complex to solve that with policies.
Use policies for what it was made for, authorizing if the user is allowed to even run a method. Any differences to what is being returned? Handle it in the normal code flow.

Accessing Database Entities from Controller [closed]

Closed. This question is opinion-based. It is not currently accepting answers.
Want to improve this question? Update the question so it can be answered with facts and citations by editing this post.
Closed 9 years ago.
Improve this question
tl;dr
In a good design. Should accessing the database be handled in a separate business logic layer (in an asp.net MVC model), or is it OK to pass IQueryables or DbContext objects to a controller?
Why? What are the pros and cons of each?
I'm building an ASP.NET MVC application in C#. It uses EntityFramework as an ORM.
Let's simplify this scenario a bit.
I have a database table with cute fluffy kittens. Each kitten has a kitten image link, kitten fluffiness index, kitten name and kitten id. These map to an EF generated POCO called Kitten. I might use this class in other projects and not just the asp.net MVC project.
I have a KittenController which should fetch the latest fluffy kittens at /Kittens. It may contain some logic selecting the kitten, but not too much logic. I've been arguing with a friend about how to implement this, I won't disclose sides :)
Option 1: db in the controller:
public ActionResult Kittens() // some parameters might be here
{
using(var db = new KittenEntities()){ // db can also be injected,
var result = db.Kittens // this explicit query is here
.Where(kitten=>kitten.fluffiness > 10)
.Select(kitten=>new {
Name=kitten.name,
Url=kitten.imageUrl
}).Take(10);
return Json(result,JsonRequestBehavior.AllowGet);
}
}
Option 2: Separate model
public class Kitten{
public string Name {get; set; }
public string Url {get; set; }
private Kitten(){
_fluffiness = fluffinessIndex;
}
public static IEnumerable<Kitten> GetLatestKittens(int fluffinessIndex=10){
using(var db = new KittenEntities()){ //connection can also be injected
return db.Kittens.Where(kitten=>kitten.fluffiness > 10)
.Select(entity=>new Kitten(entity.name,entity.imageUrl))
.Take(10).ToList();
}
} // it's static for simplicity here, in fact it's probably also an object method
// Also, in practice it might be a service in a services directory creating the
// Objects and fetching them from the DB, and just the kitten MVC _type_ here
}
//----Then the controller:
public ActionResult Kittens() // some parameters might be here
{
return Json(Kittens.GetLatestKittens(10),JsonRequestBehavior.AllowGet);
}
Notes: GetLatestKittens is unlikely to be used elsewhere in the code but it might. It's possible to use the constructor of Kitten instead of a static building method and changing the class for Kittens. Basically it's supposed to be a layer above the database entities so the controller does not have to be aware of the actual database, the mapper, or entity framework.
What are some pros and cons for each design?
Is there a clear winner? Why?
Note: Of course, alternative approaches are very valued as answers too.
Clarification 1: This is not a trivial application in practice. This is an application with tens of controllers and thousands of lines of code, and the entities are not only used here but in tens of other C# projects. The example here is a reduced test case.
The second approach is superior. Let's try a lame analogy:
You enter a pizza shop and walk over to the counter. "Welcome to McPizza Maestro Double Deluxe, may I take your order?" the pimpled cashier asks you, the void in his eyes threatening to lure you in. "Yeah I'll have one large pizza with olives". "Okay", the cashier replies and his voice croaks in the middle of the "o" sound. He yells towards the kitchen "One Jimmy Carter!"
And then, after waiting for a bit, you get a large pizza with olives. Did you notice anything peculiar? The cashier didn't say "Take some dough, spin it round like it's Christmas time, pour some cheese and tomato sauce, sprinkle olives and put in an oven for about 8 minutes!" Come to think of it, that's not peculiar at all. The cashier is simply a gateway between two worlds: The customer who wants the pizza, and the cook who makes the pizza. For all the cashier knows, the cook gets his pizza from aliens or slices them from Jimmy Carter (he's a dwindling resource, people).
That is your situation. Your cashier isn't dumb. He knows how to make pizza. That doesn't mean he should be making pizza, or telling someone how to make pizza. That's the cook's job. As other answers (notably Florian Margaine's and Madara Uchiha's) illustrated, there is a separation of responsibilities. The model might not do much, it might be just one function call, it might be even one line - but that doesn't matter, because the controller doesn't care.
Now, let's say the owners decide that pizzas are just a fad (blasphemy!) and you switch over to something more contemporary, a fancy burger joint. Let's review what happens:
You enter a fancy burger joint and walk over to the counter. "Welcome to Le Burger Maestro Double Deluxe, may I take your order?" "yeah, I'll have one large hamburger with olives". "Okay", and he turns to the kitchen, "One Jimmy Carter!"
And then, you get a large hamburger with olives (ew).
Option 1 and 2 are bit extreme and like the choice between the devil and the deep blue sea but if I had to choose between the two I would prefer option 1.
First of all, option 2 will throw a runtime exception because Entity Framework does not support to project into an entity (Select(e => new Kitten(...)) and it does not allow to use a constructor with parameters in a projection. Now, this note seems a bit pedantic in this context, but by projecting into the entity and returning a Kitten (or an enumeration of Kittens) you are hiding the real problem with that approach.
Obviously, your method returns two properties of the entity that you want to use in your view - the kitten's name and imageUrl. Because these are only a selection of all Kitten properties returning a (half-filled) Kitten entity would not be appropriate. So, what type to actually return from this method?
You could return object (or IEnumerable<object>) (that's how I understand your comment about the "object method") which is fine if you pass the result into Json(...) to be processed in Javascript later. But you would lose all compile time type information and I doubt that an object result type is useful for anything else.
You could return some named type that just contains the two properties - maybe called "KittensListDto".
Now, this is only one method for one view - the view to list kittens. Then you have a details view to display a single kitten, then an edit view and then a delete confirm view maybe. Four views for an existing Kitten entity, each of which needs possibly different properties and each of which would need a separate method and projection and a different DTO type. The same for the Dog entity and for 100 entities more in the project and you get perhaps 400 methods and 400 return types.
And most likely not a single one will be ever reused at any other place than this specific view. Why would you want to Take 10 kittens with just name and imageUrl anywhere a second time? Do you have a second kittens list view? If so, it will have a reason and the queries are only identical by accident and now and if one changes the other one does not necessarily, otherwise the list view is not properly "reused" and should not exist twice. Or is the same list used by an Excel export maybe? But perhaps the Excel users want to have 1000 kittens tomorrow, while the view should still display only 10. Or the view should display the kitten's Age tomorrow, but the Excel users don't want to have that because their Excel macros would not run correctly anymore with that change. Just because two pieces of code are identical they don't have to be factored out into a common reusable component if they are in a different context or have different semantics. You better leave it a GetLatestKittensForListView and GetLatestKittensForExcelExport. Or you better don't have such methods in your service layer at all.
In the light of these considerations an excursion to a Pizza shop as an analogy why the first approach is superior :)
"Welcome to BigPizza, the custom Pizza shop, may I take your order?" "Well, I'd like to have a Pizza with olives, but tomato sauce on top and cheese at the bottom and bake it in the oven for 90 minutes until it's black and hard like a flat rock of granite." "OK, Sir, custom Pizzas are our profession, we'll make it."
The cashier goes to the kitchen. "There is a psycho at the counter, he wants to have a Pizza with... it's a rock of granite with ... wait ... we need to have a name first", he tells the cook.
"No!", the cook screams, "not again! You know we tried that already." He takes a stack of paper with 400 pages, "here we have rock of granite from 2005, but... it didn't have olives, but paprica instead... or here is top tomato ... but the customer wanted it baked only half a minute." "Maybe we should call it TopTomatoGraniteRockSpecial?" "But it doesn't take the cheese at the bottom into account..." The cashier: "That's what Special is supposed to express." "But having the Pizza rock formed like a pyramid would be special as well", the cook replies. "Hmmm ... it is difficult...", the desparate cashier says.
"IS MY PIZZA ALREADY IN THE OVEN?", suddenly it shouts through the kitchen door. "Let's stop this discussion, just tell me how to make this Pizza, we are not going to have such a Pizza a second time", the cook decides. "OK, it's a Pizza with olives, but tomato sauce on top and cheese at the bottom and bake it in the oven for 90 minutes until it's black and hard like a flat rock of granite."
If option 1 violates a separation of concerns principle by using a database context in the view layer the option 2 violates the same principle by having presentation centric query logic in the service or business layer. From a technical viewpoint it does not but it will end up with a service layer that is anything else than "reusable" outside of the presentation layer. And it has much higher development and maintenance costs because for every required piece of data in a controller action you have to create services, methods and return types.
Now, there actually might be queries or query parts that are reused often and that's why I think that option 1 is almost as extreme as option 2 - for example a Where clause by the key (will be probably used in details, edit and delete confirm view), filtering out "soft deleted" entities, filtering by a tenant in a multi-tenant architecture or disabling change tracking, etc. For such really repetetive query logic I could imagine that extracting this into a service or repository layer (but maybe only reusable extensions methods) might make sense, like
public IQueryable<Kitten> GetKittens()
{
return context.Kittens.AsNoTracking().Where(k => !k.IsDeleted);
}
Anything else that follows after - like projecting properties - is view specific and I would not like to have it in this layer. In order to make this approach possible IQueryable<T> must be exposed from the service/repository. It does not mean that the select must be directly in the controller action. Especially fat and complex projections (that maybe join other entities by navigation properties, perform groupings, etc.) could be moved into extension methods of IQueryable<T> that are collected in other files, directories or even another project, but still a project that is an appendix to the presentation layer and much closer to it than to the service layer. An action could then look like this:
public ActionResult Kittens()
{
var result = kittenService.GetKittens()
.Where(kitten => kitten.fluffiness > 10)
.OrderBy(kitten => kitten.name)
.Select(kitten => new {
Name=kitten.name,
Url=kitten.imageUrl
})
.Take(10);
return Json(result,JsonRequestBehavior.AllowGet);
}
Or like this:
public ActionResult Kittens()
{
var result = kittenService.GetKittens()
.ToKittenListViewModel(10, 10);
return Json(result,JsonRequestBehavior.AllowGet);
}
With ToKittenListViewModel() being:
public static IEnumerable<object> ToKittenListViewModel(
this IQueryable<Kitten> kittens, int minFluffiness, int pageItems)
{
return kittens
.Where(kitten => kitten.fluffiness > minFluffiness)
.OrderBy(kitten => kitten.name)
.Select(kitten => new {
Name = kitten.name,
Url = kitten.imageUrl
})
.Take(pageItems)
.AsEnumerable()
.Cast<object>();
}
That's just a basic idea and a sketch that another solution could be in the middle between option 1 and 2.
Well, it all depends on the overall architecture and requirements and all what I wrote above might be useless and wrong. Do you have to consider that the ORM or data access technology could be changed in future? Could there be a physical boundary between controller and database, is the controller disconnected from the context and do the data need to be fetched via a web service for example in future? This would require a very different approach which would more lean towards option 2.
Such an architecture is so different that - in my opinion - you simply can't say "maybe" or "not now, but possibly it could be a requirement in future, or possibly it won't". This is something that the project's stakeholders have to define before you can proceed with architectural decisions as it will increase development costs dramatically and it will we wasted money in development and maintenance if the "maybe" turns out to never become reality.
I was talking only about queries or GET requests in a web app which have rarely something that I would call "business logic" at all. POST requests and modifying data are a whole different story. If it is forbidden that an order can be changed after it is invoiced for example this is a general "business rule" that normally applies no matter which view or web service or background process or whatever tries to change an order. I would definitely put such a check for the order status into a business service or any common component and never into a controller.
There might be an argument against using IQueryable<T> in a controller action because it is coupled to LINQ-to-Entities and it will make unit tests difficult. But what is a unit test going to test in a controller action that doesn't contain any business logic, that gets parameters passed in that usually come from a view via model binding or routing - not covered by the unit test - that uses a mocked repository/service returning IEnumerable<T> - database query and access is not tested - and that returns a View - correct rendering of the view is not tested?
This is the key phrase there:
I might use this class in other projects and not just the asp.net MVC project.
A controller is HTTP-centric. It is only there to handle HTTP requests. If you want to use your model in any other project, i.e. your business logic, you can't have any logic in the controllers. You must be able to take off your model, put it somewhere else, and all your business logic still works.
So, no, don't access your database from your controller. It kills any possible reuse you might ever get.
Do you really want to rewrite all your db/linq requests in all your projects when you can have simple methods that you reuse?
Another thing: your function in option 1 has two responsibilities: it fetches the result from a mapper object and it displays it. That's too many responsibilities. There is an "and" in the list of responsibilities. Your option 2 only has one responsibility: being the link between the model and the view.
I'm not sure about how ASP.NET or C# does things. But I do know MVC.
In MVC, you separate your application into two major layers: The Presentational layer (which contains the Controller and View), and the Model layer (which contains... the Model).
The point is to separate the 3 major responsibilities in the application:
The application logic, handling request, user input, etc. That's the Controller.
The presentation logic, handling templating, display, formats. That's the View.
The business logic or "heavy logic", handling basically everything else. That's your actual application basically, where everything your application is supposed to do gets done. This part handles domain objects that represents the information structures of the application, it handles the mapping of those objects into permanent storage (be it session, database or files).
As you can see, database handling is found on the Model, and it has several advantages:
The controller is less tied to the model. Because "the work" gets done in the Model, should you want to change your controller, you'll be able to do so more easily if your database handling is in the Model.
You gain more flexibility. In the case where you want to change your mapping scheme (I want to switch to Postgres from MySQL), I only need to change it once (in the base Mapper definition).
For more information, see the excellent answer here: How should a model be structured in MVC?
I prefer the second approach. It at least separates between controller and business logic. It is still a little bit hard to unit test (may be I'm not good at mocking).
I personally prefer the following approach. Main reason is it is easy to unit testing for each layer - presentation, business logic, data access. Besides, you can see that approach in a lot of open source projects.
namespace MyProject.Web.Controllers
{
public class MyController : Controller
{
private readonly IKittenService _kittenService ;
public MyController(IKittenService kittenService)
{
_kittenService = kittenService;
}
public ActionResult Kittens()
{
// var result = _kittenService.GetLatestKittens(10);
// Return something.
}
}
}
namespace MyProject.Domain.Kittens
{
public class Kitten
{
public string Name {get; set; }
public string Url {get; set; }
}
}
namespace MyProject.Services.KittenService
{
public interface IKittenService
{
IEnumerable<Kitten> GetLatestKittens(int fluffinessIndex=10);
}
}
namespace MyProject.Services.KittenService
{
public class KittenService : IKittenService
{
public IEnumerable<Kitten> GetLatestKittens(int fluffinessIndex=10)
{
using(var db = new KittenEntities())
{
return db.Kittens // this explicit query is here
.Where(kitten=>kitten.fluffiness > 10)
.Select(kitten=>new {
Name=kitten.name,
Url=kitten.imageUrl
}).Take(10);
}
}
}
}
#Win has the idea I'd more or less follow.
Have the Presentation just presents.
The Controller simply acts as a bridge, it does nothing really, it is the middle man. Should be easy to test.
The DAL is the hardest part. Some like to separate it out on a web service, I have done so for a project once. That way you can also have the DAL act as an API for others (internally or externally) to consume - so WCF or WebAPI comes to mind.
That way your DAL is completely independent of your web server. If someone hacks your server, the DAL is probably still secure.
It's up to you I guess.
Single Responsibility Principle. Each of your classes should have one and only one reason to change. #Zirak gives a good example of how each person has a single reponsibility in the chain of events.
Let's look at the hypothetical test case you have provided.
public ActionResult Kittens() // some parameters might be here
{
using(var db = new KittenEntities()){ // db can also be injected,
var result = db.Kittens // this explicit query is here
.Where(kitten=>kitten.fluffiness > 10)
.Select(kitten=>new {
Name=kitten.name,
Url=kitten.imageUrl
}).Take(10);
return Json(result,JsonRequestBehavior.AllowGet);
}
}
With a service layer in between, it might look something like this.
public ActionResult Kittens() // some parameters might be here
{
using(var service = new KittenService())
{
var result = service.GetFluffyKittens();
return Json(result,JsonRequestBehavior.AllowGet);
}
}
public class KittenService : IDisposable
{
public IEnumerable<Kitten> GetFluffyKittens()
{
using(var db = new KittenEntities()){ // db can also be injected,
return db.Kittens // this explicit query is here
.Where(kitten=>kitten.fluffiness > 10)
.Select(kitten=>new {
Name=kitten.name,
Url=kitten.imageUrl
}).Take(10);
}
}
}
With a few more imaginary controller classes, you can see how this would be much easier to reuse. That's great! We have code reuse, but there's even more benefit. Lets say for example, our Kitten website is taking off like crazy, everyone wants to look at fluffy kittens, so we need to partition our database (shard). The constructor for all our db calls needs to be injected with the connection to the proper database. With our controller based EF code, we would have to change the controllers because of a DATABASE issue.
Clearly that means that our controllers are now dependant upon database concerns. They now have too many reasons to change, which can potentially lead to accidental bugs in the code and needing to retest code that is unrelated to that change.
With a service, we could do the following, while the controllers are protected from that change.
public class KittenService : IDisposable
{
public IEnumerable<Kitten> GetFluffyKittens()
{
using(var db = GetDbContextForFuffyKittens()){ // db can also be injected,
return db.Kittens // this explicit query is here
.Where(kitten=>kitten.fluffiness > 10)
.Select(kitten=>new {
Name=kitten.name,
Url=kitten.imageUrl
}).Take(10);
}
}
protected KittenEntities GetDbContextForFuffyKittens(){
// ... code to determine the least used shard and get connection string ...
var connectionString = GetShardThatIsntBusy();
return new KittensEntities(connectionString);
}
}
The key here is to isolate changes from reaching other parts of your code. You should be testing anything that is affected by a change in code, so you want to isolate changes from one another. This has the side effect of keeping your code DRY, so you end up with more flexible and reusable classes and services.
Separating the classes also allows you to centralize behavior that would have either been difficult or repetitive before. Think about logging errors in your data access. In the first method, you would need logging everywhere. With a layer in between you can easily insert some logging logic.
public class KittenService : IDisposable
{
public IEnumerable<Kitten> GetFluffyKittens()
{
Func<IEnumerable<Kitten>> func = () => {
using(var db = GetDbContextForFuffyKittens()){ // db can also be injected,
return db.Kittens // this explicit query is here
.Where(kitten=>kitten.fluffiness > 10)
.Select(kitten=>new {
Name=kitten.name,
Url=kitten.imageUrl
}).Take(10);
}
};
return this.Execute(func);
}
protected KittenEntities GetDbContextForFuffyKittens(){
// ... code to determine the least used shard and get connection string ...
var connectionString = GetShardThatIsntBusy();
return new KittensEntities(connectionString);
}
protected T Execute(Func<T> func){
try
{
return func();
}
catch(Exception ex){
Logging.Log(ex);
throw ex;
}
}
}
Either way is not so good for testing. Use dependency injection to get the DI container to create the db context and inject it into the controller constructor.
EDIT: a little more on testing
If you can test you can see if you application works per spec before you publish.
If you can't test easily you won't write your test.
from that chat room:
Okay, so on a trivial application you write it and it doesn't change very much,
but on a non trivial application you get these nasty things called dependencies, which when you change one breaks a lot of shit, so you use Dependency injection to inject a repo that you can fake, and then you can write unit tests in order to make sure your code doesn't
If I had (note: really had) to chose between the 2 given options, I'd say 1 for simplicity, but I don't recommend using it since it's hard to maintain and causes a lot of duplicate code.
A controller should contain as less business logic as possible. It should only delegate data access, map it to a ViewModel and pass it to the View.
If you want to abstract data access away from your controller (which is a good thing), you might want to create a service layer containing a method like GetLatestKittens(int fluffinessIndex).
I don't recommend placing data access logic in your POCO either, this doesn't allow you to switch to another ORM (NHibernate for example) and reuse the same POCO's.

Securing/omitting selected properties of a domain entity in NHibernate (subclass, projection, ?)

Consider the following simplified scenario:
public class Person
{
public string Name { get; set; }
public int Age { get; set; }
// restricted
public string SocialSecurityNumber { get; set; }
// restricted
public string MothersMaidenName { get; set; }
}
So, in the application, many users can view Person data. Some users can view all; other users can view only Name and Age.
Having the UI display only authorized data is easy enough on the client side, but I really don't want to even send that data to the client.
I've tried to achieve this by creating a FullPerson : BasicPerson hierarchy (table-per-class-hierarchy). I used two implementations of a StaffRepository to get the desired type, but the necessary casting fails at runtime because of the NH proxies. Of course in the RDBMS any given row in the People table can represent a FullPerson or a BasicPerson, and giving them both the same discriminator value does not work either.
I considered mapping only FullPerson and using AliasToBean result transformer to filter down to BasicPerson, but I understand this to be a one-way street, whereas I want the full benefit of entity management and lazy loading (though the example above doesn't include collections) in the session.
Another thought I had was to wrap up all the restricted fields into a class and add this as a property. My concerns with this approach are several:
It compromises my domain model,
I'd have to declare the property as a collection (always of 1) in order to have it load lazily, and
I'm not even sure how I'd prevent that lazy collection from loading.
All this feels wrong. Is there a known approach to achieve the desired result?
clarification:
This in an intranet-only desktop application; the session lives on the client. While I can certainly create an intermediate service layer, I would have to give up lazy loading and change tracking, which I'd really like to keep in place.
First, let me say that I do not think it is NHibernate's responsibility to handle security, and data redaction based on same. I think you're overcomplicating this by trying to put it in the data access layer.
I would insert a layer into the service or controller that receives this data request from the client (which shouldn't be the Repository itself) and will perform the data redaction based on user permissions. So, you'd perform the full query to the DB, and then based on user permissions, the service layer would clear out fields of the result set before returning that result set over the service connection. It is not the absolute most performant solution, but is both more secure and more performant than sending all the data to the client and having the client software "censor" it. The Ethernet connection between DB and service layers of the server architecture can handle far more bandwidth than the Internet connection between service layer and client, and in a remote client app you generally have very little control over what the client does with the data; you could be talking to a hacked copy of your software, or a workalike, that doesn't give two flips about user security.
If network bandwidth between service and DB is of high importance, or if a LOT of information is restricted, Linq2NH should be smart enough to let you specify what should or shouldn't be included in a query results using a select list:
if(!user.CanSeeRestrictedFields)
var results = from p as Repository.AsQueryable<Person>()
//rest of Linq statement
select new Person {
Name = p.Name,
Age = p.Age
};
else
var results = from p as Repository.AsQueryable<Person>()
//rest of Linq statement
select new Person {
Name = p.Name,
Age = p.Age,
SocialSecurityNumber = p.SocialSecurityNumber,
MothersMaidenName = p.MothersMaidenName
};
I do not know if Linq2NH is smart enough to parse conditional operators into SQL; I doubt it, but on the off-chance it's possible, you can specify conditional operators in the initializer for the SSN and MMN fields based on whether the user has rights to see them, allowing you to combine these two queries.
I would leave your domain model alone, and instead use Automapper to map to specific DTOs based on the security level of the current user (or whatever criteria you use to determine access to the specific properties). This should be done in some sort of service layer that sits between your UI and your repositories.
Edit:
Based upon your requirements of keeping lazy-loading and change tracking in place, perhaps using the proxy pattern to wrap your domain object is a viable alternative? You could wrap your original domain model in a proxy that performed your security checks on each given property. I believe CSLA.NET uses a method like this for field-level security, so it may be worth browsing the source to get some inspiration. You could perhaps take this one step further and use interfaces implemented by your proxy that only exposed the properties that the user had access to.

Categories

Resources