gpt4 book ai didi

c# - 使用 MVC WebApi 2 在运行时更改数据库

转载 作者:太空宇宙 更新时间:2023-11-03 23:02:52 24 4
gpt4 key购买 nike

我想在运行时在 REST Api 中更改与数据库的连接。我想放置一个请求变量,让 Api 决定使用哪个连接字符串。

例如:我将值为“develop”的变量“dbid”放在请求 header 中,并将其发送到 Api。

Api 查看 header 并从 web.config 获取正确的连接字符串。

我有三层(数据、业务、api)。数据包含用于获取和设置数据的 EntityFramework。像这样:

public class WebsiteContext : IocDbContext, IWebsites
{
public DbSet<Website> Websites { get; set; }

public IEnumerable<Website> GetAll()
{
return Websites.ToList();
}
}

(IoCDbContext.cs)

public class IocDbContext : DbContext, IDbContext
{
public IocDbContext() : base("develop")
{
}

public void ChangeDatabase(string connectionString)
{
Database.Connection.ConnectionString= connectionString;
}
}

在业务中,我有一个类可以从数据层检索数据并执行一些逻辑操作(这里不需要,但对故事来说仍然有好处)。

public class Websites : IWebsites
{
private readonly Data.Interfaces.IWebsites _websiteContext;

#region Constructor

public Websites(Data.Interfaces.IWebsites websiteContext)
{
_websiteContext = websiteContext;
}

#endregion

#region IWebsites implementation


public IEnumerable<Website> GetWebsites()
{
List<Data.Objects.Website> websiteDtos = _websiteContext.GetAll().ToList();

return websiteDtos.Select(web => web.ToModel()).ToList();
}

#endregion
}

public static class WebsiteMapper
{
public static Website ToModel(this Data.Objects.Website value)
{
if (value == null)
return null;

return new Website
{
Id = value.Id,
Name = value.Name
};
}
}

最后但同样重要的是, Controller :

public class WebsiteController : ApiController
{
private readonly IWebsites _websites;

public WebsiteController(IWebsites websites)
{
_websites = websites;
}

public IEnumerable<Website> GetAll()
{
return _websites.GetWebsites().ToList();
}
}

我的 Unity 配置:

public static void RegisterComponents()
{
var container = new UnityContainer();

container.RegisterType<Business.Interfaces.IWebsites, Websites>();

container.RegisterType<IDbContext, IocDbContext>();
container.RegisterType<IWebsites, WebsiteContext>();

// e.g. container.RegisterType<ITestService, TestService>();

GlobalConfiguration.Configuration.DependencyResolver = new Unity.WebApi.UnityDependencyResolver(container);
DependencyResolver.SetResolver(new UnityDependencyResolver(container));
}

如您所见,默认情况下使用名称为“develop”的连接字符串。这将返回一个名为“网站”的网站。现在我将标题变量“dbid”更改为“live”。 api 应该看到这个并且应该得到与名称“live”相对应的连接字符串。最后一部分是我正在尝试的,但没有任何效果。

这个我试过了:

  • 将 session 添加到 webapi。这意味着我打破了 REST api 的无状态思想:未完成
  • 静态也行不通,因为每个人都可以获得相同的连接字符串,但它的用户特定
  • 谷歌,但大多数示例对我不起作用
  • 正在搜索 StackOverflow...请参阅上一点。

这让我发疯!应该有一种方法可以更改由请求 header 中的值给出的连接字符串,对吧?

最佳答案

我在创建的 Multi-Tenancy 应用程序中有相同的场景,我为每个租户使用不同的连接字符串。

您选择的实现并不重要,但您必须确定如何区分每个连接字符串的每个请求。在我的应用程序中,我创建了一个自定义路由值,并在 url 中使用它来区分每个请求。重要的是创建这种机制,它必须是您在 DI 框架中根据每个请求注册的第一件事。

例如(使用 Ninject):

private static void RegisterServicdes(IKernel kernel)
{
kernel.Bind<ISiteContext>().To<SiteContext>().InRequestScope();
kernel.Bind<IDbContextFactory>().To<DbContextFactory>().InRequestScope();
// register other services...
}

而不是你的 DbContext 的实现,我会改成这样,然后总是通过 DbContextFactory 创建你的 DbContext 实例。

public class IocDbContext : DbContext, IDbContext
{
public IocDbContext(string connectionStringType) : base(connectionStringType) { }
}

然后你需要创建一个你在创建你的DbContext时使用的DbContextFactory,并将上面的类作为依赖。或者您可以将依赖项带入您的服务,并将其传递到 DbContextFactory。

public interface IDbContextFactory
{
TestModel CreateContext();
}

public class DbContextFactory : IDbContextFactory
{
private string _siteType;
public DbContextFactory(ISiteContext siteContext)
{
_siteType = siteContext.Tenant;
}

public TestModel CreateContext()
{
return new TestModel(FormatConnectionStringBySiteType(_siteType));
}

// or you can use this if you pass the IMultiTenantHelper dependency into your service
public static TestModel CreateContext(string siteName)
{
return new TestModel(FormatConnectionStringBySiteType(siteName));
}

private static string FormatConnectionStringBySiteType(string siteType)
{
// format from web.config
string newConnectionString = @"data source={0};initial catalog={1};integrated security=True;MultipleActiveResultSets=True;App=EntityFramework";

if (siteType.Equals("a"))
{
return String.Format(newConnectionString, @"(LocalDb)\MSSQLLocalDB", "DbOne");
}
else
{
return String.Format(newConnectionString, @"(LocalDb)\MSSQLLocalDB", "DbTwo");
}
}
}

然后您可以在访问 DbContext 时像这样使用它:

public class DbAccess
{
private IDbContextFactory _dbContextFactory;
public DbAccess(IDbContextFactory dbContextFactory)
{
_dbContextFactory = dbContextFactory;
}

public void DoWork()
{
using (IocDbContext db = _dbContextFactory.CreateContext())
{
// use EF here...
}
}
}

ISiteContext 接口(interface)实现(使用路由)。

public interface ISiteContext
{
string Tenant { get; }
}

public class SiteContext : ISiteContext
{
private const string _routeId = "tenantId";

private string _tenant;
public string Tenant { get { return _tenant; } }

public SiteContext()
{
_tenant = GetTenantViaRoute();
}

private string GetTenantViaRoute()
{
var routedata = HttpContext.Current.Request.RequestContext.RouteData;

// Default Routing
if (routedata.Values[_routeId] != null)
{
return routedata.Values[_routeId].ToString().ToLower();
}

// Attribute Routing
if (routedata.Values.ContainsKey("MS_SubRoutes"))
{
var msSubRoutes = routedata.Values["MS_SubRoutes"] as IEnumerable<IHttpRouteData>;
if (msSubRoutes != null && msSubRoutes.Any())
{
var subRoute = msSubRoutes.FirstOrDefault();
if (subRoute != null && subRoute.Values.ContainsKey(_routeId))
{
return (string)subRoute.Values
.Where(x => x.Key.Equals(_routeId))
.Select(x => x.Value)
.Single();
}
}
}

return string.Empty;
}
}

API 操作:

[Route("api/{tenantId}/Values/Get")]
[HttpGet]
public IEnumerable<string> Get()
{

_testService.DoDatabaseWork();

return new string[] { "value1", "value2" };
}

关于c# - 使用 MVC WebApi 2 在运行时更改数据库,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/42464645/

24 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com