我还有一个 ASP.NET 的MVC架构问题,我正在慢慢到达那里并将一些东西拼凑在一起,但我仍然试图解决一些缺失的链接。我当前的项目结构如下,我对此感到满意(尽管任何建设性的意见都会很棒),我还没有使用 IoC/DI,但会在某个时候使用,目前只是更大的鱼要炸!.
我的问题是我在 AppName.Core.Services 命名空间中有域级业务逻辑,这很棒,但我有一些逻辑需要与检查应用程序缓存等内容的 HttpContext 进行交互。一个非常简单的例子是这样的代码:
public int? GetCurrentClientId()
{
int clientId = null;
if (HttpContext.Current.Application["ClientId"] != null)
{
// Use value from application cache if one exists
clientId = (int)HttpContext.Current.Application["ClientId"];
}
else
{
// Lookup using host name of site we are browsing
string hostName = HttpContext.Current.Request.UserHostName.ToLower();
UnitOfWork _unitOfWork = new UnitOfWork();
Client client = _unitOfWork.ClientRepository.GetSingle(x => x.HostName.ToLower() == hostName || x.LocalHostNames.ToLower().Contains(hostName));
if (client != null) clientId = client.Id;
}
return clientId;
}
现在我不能把所有这些放在AppName.Core.Services中,因为它的HttpContext感知,所以我把它分解为以下代码,它可以位于AppName.Core.Services中,但我不确定的是我的逻辑去哪里,它与HttpContext等交互,如果需要,可以调用它。我不希望它在我的控制器中,但我现在想知道它的最佳位置,什么命名空间和约定等。
public int? GetClientIdFromHostName(string hostName)
{
UnitOfWork _unitOfWork = new UnitOfWork();
Client client = _unitOfWork.ClientRepository.GetSingle(x => x.HostName.ToLower() == hostName || x.LocalHostNames.ToLower().Contains(hostName));
if (client != null) return client.Id;
else return null;
}
我之前对这个主题的研究,以及我之前在这里提出的一个问题,指向我从控制器访问我的所有服务......但是如果我不能在我的服务中使用基于 HttpContext 的逻辑,它可以去哪里?
ASP.NET MVC - 服务层,每个控制器操作中的单个或多个服务?
项目结构:
AppName.Core (for all my domain objects, DAL, domain services etc... independant and not aware of web specifics HttpContext etc)
> Data
> Data > DataContext.cs
> Data > UnitOfWork.cs
> Entities
> Entities > User.cs
> Entities > Client.cs etc etc
> Migrations
> Migrations > (all the EF code first migrations bits)
> Repository
> Repository > UserRepository.cs
> Repository > ClientRepository.cs
> Repository > GenericRepository.cs etc etc
> Services
> Services > ClientService.cs etc etc
AppName.Web (for all my compiled HttpContext aware code, ViewModels etc... references AppName.Core only)
> AutoMapper
> AutoMapper > Configuration.cs
> Controllers
> Controllers > UserController.cs
> Controllers > ClientController.cs etc etc
> Helpers
> Helpers > HtmlHelpers.cs
> ViewModels
> ViewModels > UserViewModel.cs
> ViewModels > ClientViewModel.cs etc etc
AppName (the ASP.NET MVC website project, no compiled code, Images/Css/JavaScripts etc... references AppName.Web only)
> Content
> Content > Images
> Content > Styles
> Scripts
> Views
如果服务是从 MVC 应用中实例化的,则可以考虑创建接口 + 包装器类并将实例传递给服务。 像这样:
interface IContext
{
int? ClientID { get; }
}
class ContextWrapper : IContext
{
private IHttpContext Context { get; set; }
public ContextWrapper (IHttpContext context)
{
Context = context;
}
int? ClientID
{
get
{
return Context.Current.Application["ClientId"] != null
? (int?)HttpContext.Current.Application["ClientId"]
: null;
}
}
}
class YourService
{
public YourService(IContext context)
{
// store the reference and use in your methods as needed
}
}
这允许您不会从服务类直接依赖 System.Web。 它还很好地设置了单元测试,因为它使用 IoC,并且可以通过构造函数注入轻松挂接 DI。
您可以创建一个帮助程序方法
,如下所示:public static TResult GetFromCacheOrSource<TResult>(string cacheIndex, Func<TResult> sourceMethod)
{
TResult result = HttpContext.Current.Application[cacheIndex] as TResult;
if (result == null)
{
// If there's no value in the cache go to the source
result = sourceMethod();
}
return result;
}
然后,您可以在控制器中执行以下操作:
int? clientId = Helper.GetFromCacheOrSource<int?>("ClientId", () => clientService.GetClientIdFromHostname(hostname));