使用动态ConnString

时间:2018-10-10 02:04:14

标签: c# entity-framework dependency-injection unity-container

下面是一个简单但实​​用的示例,大致说明了我如何进行依赖注入。当我的DbContext连接字符串不是动态的时,这非常有用。即使已通过配置文件或其他方式将其传递到工厂,也没关系,只要一直保持相同即可。

我需要围绕如何对以下代码进行(理想的)较小修改,以允许在运行时动态确定连接字符串。

例如,说在View上,用户不仅可以选择要传递到控制器的Post方法中的老师,还可以选择学校。为简单起见,如果有两所学校具有完全相同的数据库结构,但是具有不同的连接字符串,如何将其从控制器下放到工厂?

我已经尝试过在方法之间传递值,但是这对于大型项目而言实际上是不可持续的,增加了出错的可能性,并且总体上来说,将某些东西从层传递到其他地方只是一团糟(除了违反SOLID)这样的图层。 ((如果需要,我可以添加我所做的不完全理想的尝试,为简洁起见,我将其省略了,因为这已经是一个相当长的问题,对代码示例和所有内容而言都是如此。)

控制器

public class HomeController : Controller
{
    private readonly IDataService _dataService;

    public HomeController(IDataService dataService)
    {
        _dataService = dataService;
    }

    public ActionResult Index()
    {

        var results = _dataService.GetTeachers();
        var model = new ViewModel
        {
            Teachers = results
        };
        return View(model);
    }

    [HttpPost]
    public ActionResult Index(ViewModel model)
    {
        var results = _dataService.GetCourses(model.Teacher);
        model.Courses = new List<string>(results);
        return View(model);
    }
}

服务

public class DataService : IDataService
{
    private readonly IDataRepo _dataRepo;

    public DataService(IDataRepo dataRepo)
    {
        _dataRepo = dataRepo;
    }

    public List<string> GetCourses(string teacherName)
    {
        return _dataRepo.GetCourses()
            .Where(c => c.Teacher.FirstName == teacherName)
            .Select(c => c.Name)
            .ToList();
    }

    public List<string> GetTeachers()
    {
        return _dataRepo.GetCourses()
            .Select(c => c.Teacher.FirstName)
            .ToList();
    }
}

存储库

public class DataRepo : IDataRepo
{
    private readonly SchoolContext _context;

    public DataRepo()
    {
        _context = ContextFactory.MakeContext();
    }

    public IEnumerable<Course> GetCourses()
    {
        return _context.Courses;
    }
}

上下文工厂

public static class ContextFactory
{
    public static SchoolContext MakeContext()
    {
        var connString =
            "connStringA";
        return new SchoolContext(connString);
    }
}

UnityConfig

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

        container.RegisterType<IDataService, DataService>();
        container.RegisterType<IDataRepo, DataRepo>();

        DependencyResolver.SetResolver(new UnityDependencyResolver(container));
    }

1 个答案:

答案 0 :(得分:1)

首先,您必须确定如何获取当前使用的连接字符串。通过URL吗?或使用当前用户或其他任何方式。

然后,创建另一个数据库,该数据库在连接字符串和您选择的方法(用户,URL ...)之间具有映射关系

最后,实现一种从数据库获取记录的方法。

因此,假设您将使用URL作为当前租户的标识符,则您的实体类应如下所示:

public class Tenant
{
   public string Url {get;set;}
   public string ConnectionString {get;set;}
}

代表获取当前租户的逻辑的接口:

public interface ICurrentTenantService
{
  Tenant GetCurrentTenant();
}

现在您将执行它

public class CurrentTenantService : ICurrentTenantService
{
  public Tenant GetCurrentTenant()
   {
      string currentUrl = HttpContext.Current.Url; //make sure to get only the base URL here
      return TenantDbContext.Tenants.FirstOrDefault(t=>t.Url == url); //TenantDbContext should be a class that has the Tenant entity
   }
}

现在,您必须像这样将上下文工厂连接到租户服务

public static class ContextFactory
{
    private readonly ICurrentTenantService currentTenantService;
   //Inject it in the constructor

    public static SchoolContext MakeContext()
    {
        var currentTenant= currentTenantService.GetCurrentTenant(); //Check for NULL

        return new SchoolContext(currentTenant.ConnectionString);
    }
}