C# 在Web API应用程序中,我可以在什么时候截取URI参数并相应地路由调用?

C# 在Web API应用程序中,我可以在什么时候截取URI参数并相应地路由调用?,c#,castle-windsor,database-schema,lifecycle,asp.net-web-api-routing,C#,Castle Windsor,Database Schema,Lifecycle,Asp.net Web Api Routing,注意:这个问题确实有点类似于,但我想我可以用一种更简单、更具体的方式来回答 我正在使用Castle Windsor拦截传递给我的Web API应用程序的URI,以便向控制器的构造函数注册适当的具体类 我希望能够在URI上传递一个“站点号”,可能总是作为第一个或最后一个参数。看,对于42号场地,而不是 http://localhost:28642/api/platypi/GetAll ……它将是: http://localhost:28642/api/platypi/42/GetAll -或:

注意:这个问题确实有点类似于,但我想我可以用一种更简单、更具体的方式来回答

我正在使用Castle Windsor拦截传递给我的Web API应用程序的URI,以便向控制器的构造函数注册适当的具体类

我希望能够在URI上传递一个“站点号”,可能总是作为第一个或最后一个参数。看,对于42号场地,而不是

http://localhost:28642/api/platypi/GetAll
……它将是:

http://localhost:28642/api/platypi/42/GetAll
-或:

当我的Web API应用程序第一次“看到”/截取URI时,我想记下该站点编号,以便我可以分配所需的具体存储库,以便Castle Windsor注册。我希望能够做到这一点:

public class RepositoriesInstaller : IWindsorInstaller
{
    public void Install(IWindsorContainer container, IConfigurationStore store)
    {
        if (siteNum == 42)
        {
            container.Register(
            Component.For<IDepartmentRepository>().ImplementedBy<DepartmentRepository42>().LifestylePerWebRequest(),
                Component.For<IInventoryItemRepository>().ImplementedBy<InventoryItemRepository42>().LifestylePerWebRequest(),
            . . .
        }
        else if (siteNum = 77)
        {
            container.Register(
        Component.For<IDepartmentRepository>().ImplementedBy<DepartmentRepository77>().LifestylePerWebRequest(),
                Component.For<IInventoryItemRepository>().ImplementedBy<InventoryItemRepository77>().LifestylePerWebRequest(),
            . . .
        }
…成为:

public DepartmentsController(IDepartmentRepository deptsRepository)
{
    if (deptsRepository == null)
    {
        throw new ArgumentNullException("deptsRepository");
    }
    _deptsRepository = deptsRepository(siteNum);
}
…还是


我仍然有一个问题,在Castle Windsor/控制器获取传入URI之前,我在哪里拦截它,这样我就可以为全局/siteNum变量设置适当的值?

我会更改DepartmentRepository()上的构造函数来传递site num,并使用它来获取connectionstring。然后在您的webconfig中为每个站点创建一个connectionstring。

您可以使用许多扩展点来实现这一点,我个人使用这个扩展点也可以获得类似的结果

通过扩展
IModelBinder
以下内容创建自定义模型绑定器:

public class SiteManagerModelBinder : IModelBinder
    {
        #region IModelBinder Members

        public object BindModel(ControllerContext controllerContext, ModelBindingContext bindingContext)
        {
            if (bindingContext.Model != null)
            {
                throw new InvalidOperationException("Cannot update instances");
            }

            // Apply your condition to determine if site number is in Url.
            if (controllerContext.RouteData.Values['siteNum']!=null)
            {
               // probably want to resolve this from container just hard coding as example, assumption is that SiteManager, does the repository bits for you.
               return new SiteManager((int)controllerContext.RouteData.Values['siteNum']);
            }

            return null;
        }

        #endregion
    }
public class RepositoryFactorySelector : DefaultTypedFactoryComponentSelector
{
    protected override string GetComponentName(MethodInfo method, object[] arguments)
    {
        return (method.Name.EndsWith("ById") && arguments.Length >= 1 && arguments[0] is int)
                   ? string.Format("Repository{0}", arguments[0]) 
                   : base.GetComponentName(method, arguments);
    }
}
public class RepositoryInstaller : IWindsorInstaller
{
    #region IWindsorInstaller Members

    public void Install(IWindsorContainer container, IConfigurationStore store)
    {
        container.Register(Classes.FromThisAssembly().BasedOn<Repository>().Configure(c=>c.Named(c.Implementation.Name)));

        container.Register(
            Component.For<RepositoryFactorySelector, ITypedFactoryComponentSelector>().LifestyleSingleton(),
            Component.For<IRepositoryFactory>().AsFactory(c => c.SelectedWith<RepositoryFactorySelector>()));
    }

    #endregion
}
好的,现在我们需要注册新的ModelBinder:

    protected void Application_Start()
    { 
        ModelBinders.Binders.Add(typeof(SiteManager), new SiteManagerModelBinder ());
好的,现在在我们的控制器中,我们所做的就是添加SiteManager作为任何操作的参数,它将由我们的ModelBinder填充

public class DepartmentsController: Controller {

    public ActionResult AnyAction(SiteManager siteManager, int whateverElse, ViewModel model)
    {

    }

}

这里有一种不同的方式,你可以尝试使用温莎城堡,这可能会更好:

创建一个存储库工厂,如下所示:

public interface IRepositoryFactory
{
    Repository CreateRepositoryById(int id);
}
创建一个组件选择器,根据以下名称选择正确的存储库:

public class SiteManagerModelBinder : IModelBinder
    {
        #region IModelBinder Members

        public object BindModel(ControllerContext controllerContext, ModelBindingContext bindingContext)
        {
            if (bindingContext.Model != null)
            {
                throw new InvalidOperationException("Cannot update instances");
            }

            // Apply your condition to determine if site number is in Url.
            if (controllerContext.RouteData.Values['siteNum']!=null)
            {
               // probably want to resolve this from container just hard coding as example, assumption is that SiteManager, does the repository bits for you.
               return new SiteManager((int)controllerContext.RouteData.Values['siteNum']);
            }

            return null;
        }

        #endregion
    }
public class RepositoryFactorySelector : DefaultTypedFactoryComponentSelector
{
    protected override string GetComponentName(MethodInfo method, object[] arguments)
    {
        return (method.Name.EndsWith("ById") && arguments.Length >= 1 && arguments[0] is int)
                   ? string.Format("Repository{0}", arguments[0]) 
                   : base.GetComponentName(method, arguments);
    }
}
public class RepositoryInstaller : IWindsorInstaller
{
    #region IWindsorInstaller Members

    public void Install(IWindsorContainer container, IConfigurationStore store)
    {
        container.Register(Classes.FromThisAssembly().BasedOn<Repository>().Configure(c=>c.Named(c.Implementation.Name)));

        container.Register(
            Component.For<RepositoryFactorySelector, ITypedFactoryComponentSelector>().LifestyleSingleton(),
            Component.For<IRepositoryFactory>().AsFactory(c => c.SelectedWith<RepositoryFactorySelector>()));
    }

    #endregion
}
按如下方式注册您的存储库:

public class SiteManagerModelBinder : IModelBinder
    {
        #region IModelBinder Members

        public object BindModel(ControllerContext controllerContext, ModelBindingContext bindingContext)
        {
            if (bindingContext.Model != null)
            {
                throw new InvalidOperationException("Cannot update instances");
            }

            // Apply your condition to determine if site number is in Url.
            if (controllerContext.RouteData.Values['siteNum']!=null)
            {
               // probably want to resolve this from container just hard coding as example, assumption is that SiteManager, does the repository bits for you.
               return new SiteManager((int)controllerContext.RouteData.Values['siteNum']);
            }

            return null;
        }

        #endregion
    }
public class RepositoryFactorySelector : DefaultTypedFactoryComponentSelector
{
    protected override string GetComponentName(MethodInfo method, object[] arguments)
    {
        return (method.Name.EndsWith("ById") && arguments.Length >= 1 && arguments[0] is int)
                   ? string.Format("Repository{0}", arguments[0]) 
                   : base.GetComponentName(method, arguments);
    }
}
public class RepositoryInstaller : IWindsorInstaller
{
    #region IWindsorInstaller Members

    public void Install(IWindsorContainer container, IConfigurationStore store)
    {
        container.Register(Classes.FromThisAssembly().BasedOn<Repository>().Configure(c=>c.Named(c.Implementation.Name)));

        container.Register(
            Component.For<RepositoryFactorySelector, ITypedFactoryComponentSelector>().LifestyleSingleton(),
            Component.For<IRepositoryFactory>().AsFactory(c => c.SelectedWith<RepositoryFactorySelector>()));
    }

    #endregion
}
请参考my global.asax.cs:

public class MvcApplication : System.Web.HttpApplication
{
    protected void Application_Start()
    {
        AreaRegistration.RegisterAllAreas();

        WebApiConfig.Register(GlobalConfiguration.Configuration);
        FilterConfig.RegisterGlobalFilters(GlobalFilters.Filters);
        RouteConfig.RegisterRoutes(RouteTable.Routes);
        BundleConfig.RegisterBundles(BundleTable.Bundles);

        Container = new WindsorContainer();
        Container.AddFacility<TypedFactoryFacility>(); 

        Container.Install(FromAssembly.This()); 

        var controllerFactory = new WindsorControllerFactory(Container.Kernel);
        ControllerBuilder.Current.SetControllerFactory(controllerFactory);
    }

    public WindsorContainer Container;
}

更新了我的问题并回答了您的回答。看起来很有希望,但这是一个仅适用于基本/通用MVC的解决方案,还是也适用于Web API,因为我的控制器实现了ApicController而不是Controller:public class DepartmentsController:ApiControllerNo,URI在应用程序\u start中不可访问,这正是您注册应用程序范围的东西的地方。我不太确定web API,但据我所知,它与largley一样,并通过相同的管道。尝试一下,让我知道,我可以想出其他方法来实现解决方案。有几种方法可以做到这一点,如果愿意,您可以直接用SiteRepository替换SiteManager,但是中间有一个额外的业务层总是好的。没关系,您只需要注册它。它甚至可以在不同的组件中。这一切看起来都很聪明,但我没有“观点”;这是Web API,不是MVC。