开发者

Advice on filtering data and code reuse with Onion Architecture

开发者 https://www.devze.com 2023-03-18 18:52 出处:网络
Here are my questions and then I\'ll give you the background for them: I would prefer to use Method 2 as my application design, so is there a way to provide filtering like Method 1 without introduci

Here are my questions and then I'll give you the background for them:

  • I would prefer to use Method 2 as my application design, so is there a way to provide filtering like Method 1 without introducing references to non-business code and without allowing access to the database model in the Core project?

  • How do you handle code reuse? The namespaces for each object are something like Project.Core.Domain or Project.Core.Services, but if feels weird making the namespace something like CompanyName.Core.Domain when it is not stored in that project. Currently, I'm copying the source code files and renaming namespaces to handle this, but I'm wondering if there is an organizational way to handle this or something else I hadn't thought of?

Technologies I'm using:

  • ASP.NET MVC 3
  • Linq-to-SQL
  • StructureMap
  • Moq
  • MSTest

Method 1:


Here's how I used to setup my web projects:

Advice on filtering data and code reuse with Onion Architecture

The Data project would contain all repositories, and Linq data contexts. In a repository, I would return a collection of objects from the database using IQueryable.

public IQueryable<Document> List()
{
    return from d in db.Documents
           select d;
}

This allowed me to setup filters that were static methods. These were also stored in the Data project.

public static IQueryable<Document> SortByFCDN(this IQueryable<Document> query)
{
    return from d in query
           orderby d.ID
           select d;
}

In the service layer, the filter could be applied like this.

public IPagedList<Document> ListByFCDN(int page, IConfiguration configuration)
{
    return repository.List().SortByFCDN().ToPagedList(page, configuration.PageSize, configuration.ShowRange);
}

Therefore, the repository would only have to provide a ListAll method that returned all items as an IQueryable object and then the service layer would determine how to filter it down before returning the subset of data.

I like this approach and it made my repositories cleaner while leaving the bulk of the code in the services.

Method 2


Here's how I currently setup my web projects:

Advice on filtering data and code reuse with Onion Architecture

Using the Onion Architecture:

  • Core: Contains business domain model, all interfaces for the application, and the service class implementations.
  • Infrastructure: Contains the repository implementations, Linq data contexts, and mapping classes to map the Linq database model to the business model.

Since I'm separating my business code from database code, I do not wan开发者_如何学JAVAt to add references in the Core project to things like Linq to gain access to IQueryable. So, I've had to perform the filtering at the repository layer, map the database model to the domain model, and then return a collection of domain objects to the service layer. This could add additional methods into my repositories.


This is what I ended up doing:

1) Created a filtering enum object in the Core project.

public enum FilterType
{
    SortFCDN
}

2) In the service class (also within the Core project), do something like:

    public IPagedList<Document> ListByFCDN(int page)
    {
        Dictionary<FilterType, object> filters = new Dictionary<FilterType, object>();

        filters.Add(FilterType.SortFCDN, "");

        return repository.List(page, filters);
    }

3) In the repository (under the Infrastructure project):

    public IPagedList<Document> List(int page, Dictionary<FilterType, object> filters)
    {
        //Query all documents and map to the model.
        return (from d in db.DbDocuments
                select d).Filter(filters).Map(
                    page, 
                    configuration.Setting("DefaultPageSize", true).ToInt(), 
                    configuration.Setting("DefaultShowRange", true).ToInt());
    }

4) Create a filters class in the Infrastructure project:

public static class DocumentFilters
{
    public static IQueryable<DbDocument> Filter(this IQueryable<DbDocument> source, Dictionary<FilterType, object> filters)
    {
        foreach (KeyValuePair<FilterType, object> item in filters)
        {
            switch (item.Key)
            {
                case FilterType.SortFCDN:
                    source = source.SortFCDN();
                    break;
            }
        }

        return source;
    }

    public static IQueryable<DbDocument> SortFCDN(this IQueryable<DbDocument> source)
    {
        return from d in source
               orderby d.ID
               select d;
    }
}

The service layer (Core project) can then decide what filters to apply and pass those filters to the repository (Infrastructure project) before the query executes. Multiple filters can be applied as long as only one per FilterType is applied.

The filters dictionary can hold the type of filter and any value/object that needs to be passed into the filter. New filters can easily be added as well.

0

精彩评论

暂无评论...
验证码 换一张
取 消