vendredi 29 septembre 2017

C# Class Structure to Ensure Certain Filters Are Always Applied

So I'm currently working on a project with a team, and my team and I have come across a certain design scenario that we are trying to come up with a solution for.

Background Info of Current Project Implementation:

This problem involves three main projects in our solution: Repository, Models, and Services project. In case it isn't obvious, the purpose of each project is as follows. The Models project contains models of all the data we store in our database. The Repository project has one main database access class that uses generics to interact with different tables depending on the model passed in. Lastly the Services project contains classes that interface data between the front-end and repository, and in general each service class maps 1-to-1 to a model class. As one would expect, the build dependencies are: Repository relies on Models, and Services relies on both projects.

The Issue:

The current issue we are encountering is that we need a way to ensure that if a developer attempts to query or interact with a specific type of object (Call it ModelA), then we want to ensure that a specific set of filters is always included by default (and these filters are partially based on if a particular user has permissions to view certain objects in a list). A developer should be able to override this filter.

What we want to avoid doing is having an if clause in the repository classes that says "if you're updating this model type, add these filters".

Solutions we have thought of / considered:

One solution we are currently considering is having a function in ServiceA (the service corresponding to ModelA) that appends these filters to a given query, and then to make it so that if anyone requests for the db context of a model, they must pass in a function that manipulates filtering in some fashion (in other words, if they want to interact with ModelA, they would pass in the filter function from ServiceA). The issue with this solution is that a developer needs to always be aware that if they ever interact with ModelA, they must pass in the function from ServiceA. Also, because we don't want every model to enforce certain filter options, we would likely want this to be an optional parameter, which might then cause issues where developers simply forget to include this function when interacting with ModelA.

Another solution we considered is to have an attribute (let's call it DefaultFilterAttribute) on ModelA that stores a class type that should implement a particular interface (called IFilterProvider). ServiceA would implement this interface, and ModelA's attribute would be given ServiceA as a type. Then the repository methods can check if the entity passed in has a DefaultFilterAttribute on it, and then simply call the method implemented by the class attached to the attribute. Unfortunately, as some of you might have noticed, the way our project dependencies are currently set up, we can't really implement a solution like this.

So I'm wondering if there is a clean solution to this problem, or if potentially we are thinking about the problem and/or design pattern incorrectly, and should be taking a completely different approach.

Aucun commentaire:

Enregistrer un commentaire