Making an object accessible by Service Layer without passing as Parameter in MVC4 App

我只是一个虾纸丫 提交于 2019-12-01 11:56:21

问题


I'm building a multi-tenant MVC app where there's a single app pool and single database. I have a Tenant table, and each of my models has a TenantId identified.

Each Tenant has a string "Url" that identifies the full URL used to access that tenant's data.

I can access this from my BaseController with the following (rough approximation):

HttpRequest request = HttpContext.Current.Request;
Uri requestUrl = request.Url;
_tenant = _tenantService.GetTenantByUrl(requestUrl);

Now, I'm at a point where I need to pass the Tenant into the service layer to perform business logic. One way I can do this is to go across every single method across all services (~200 methods) and add a Tenant parameter. I'd have to touch every call to the service layer, and every service layer method. This would work, but it's tedious and muddles the code.

For example, one of my methods before:

    public void DeleteUserById(int userId)
    {
        using (var db = CreateContext())
        {
            var user = db.Users.FirstOrDefault(u => u.UserId.Equals(userId));
            InternalDeleteUser(db, user);
        }
    }

After (if I pass in the Tenant):

    public void DeleteUserById(Tenant tenant, int userId)
    {
        using (var db = CreateContext())
        {
            var user = tenant.Users.FirstOrDefault(u => u.UserId.Equals(userId));
            InternalDeleteUser(db, user);
        }
    }

What I'm trying to achieve (by setting the tenant from my BaseController, one layer up):

    public void DeleteUserById(int userId)
    {
        using (var db = CreateContext())
        {
            var user = _tenant.Users.FirstOrDefault(u => u.UserId.Equals(userId));
            InternalDeleteUser(db, user);
        }
    }

Is there any way I can use my BaseService (all other services inherit from this) or some other pattern to define the Tenant from the Controller, and have the service methods pick it up, without passing it as a parameter to each one? This way I need only touch the base controller (or maybe even global.asax), and nothing else.

Put simply: How can I make an object accessible to all services by defining it from an MVC controller, without passing it directly to the service?


回答1:


I guess what you´re saying about having a base service (see Layer Supertype) makes sense. That base class will have a dependency on an interface defined in the same service layer (e.g. IUserSession, IContext or whatever) and that interface will have a method or property that will return your Tenant.

The implementation of this interface will reside in your web application and it will do something as what you described, obtaining the data from the HttpContext.

If you have a background process, console application or whatever that does not run on a web context, you will have a different implementation that will create the Tenant based on any other criteria that you want.

So to summarize, you will have in your service layer:

abstract class BaseService 
{    
    protected IContext Context {get; private set;}

    public BaseService(IContext context)
    {
            Context = context;
    }
}

public interface IContext
{
    Tenant GetTenant();
}

Then in your web layer you´ll have:

public IWebContext : IContext
{
    public Tenant GetTenant()
    {
        //your code to return create the tenant based on the url.
    }
}

Hope this helps.




回答2:


I have the same 'problem' since I'm building a multi tenant app as well. However, I solved it quite simple, IMO: every repository/service has defined a TenantId property, that must be set when that service is used. TenantId is a value object and it will throw if null.

Now, the point is any of the repos/services can be used outside the request, for example in a background thread or app. I am using a message driven approach so any required info (like tenant id) is part of the message and thus available for the consumer of the service (the message handler). Another benefit is testability.

I advice against coupling your service to a request specific object like HttpContext, Session or Cache.



来源:https://stackoverflow.com/questions/19756369/making-an-object-accessible-by-service-layer-without-passing-as-parameter-in-mvc

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!