如何正确使用 Unity 将 ConnectionString 传递给我的存储库类? [英] How do I correctly use Unity to pass a ConnectionString to my repository classes?

查看:16
本文介绍了如何正确使用 Unity 将 ConnectionString 传递给我的存储库类?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我刚刚开始使用 Microsoft 的 Unity Application Blocks Dependency Injection 库,但我已经不知所措了.

I've literally just started using the Unity Application Blocks Dependency Injection library from Microsoft, and I've come unstuck.

这是我的 IoC 类,它将处理我的具体类到它们的接口类型的实例化(所以我不必每次在我的控制器中想要一个存储库时都在 IoC 容器上调用 Resolve):

This is my IoC class that'll handle the instantiation of my concrete classes to their interface types (so I don't have to keep called Resolve on the IoC container each time I want a repository in my controller):

public class IoC
{
    public static void Intialise(UnityConfigurationSection section, string connectionString)
    {
        _connectionString = connectionString;
        _container = new UnityContainer();
        section.Configure(_container);
    }

    private static IUnityContainer _container;
    private static string _connectionString;

    public static IMovementRepository MovementRepository
    {
        get { return _container.Resolve<IMovementRepository>(); }
    }
}

所以,我的想法是从我的控制器,我可以做到以下几点:

So, the idea is that from my Controller, I can just do the following:

_repository = IoC.MovementRepository;

我目前收到错误:

例外是:InvalidOperationException - 类型无法构造字符串.你必须配置容器以提供此价值.

Exception is: InvalidOperationException - The type String cannot be constructed. You must configure the container to supply this value.

现在,我假设这是因为我的映射具体实现的构造函数需要一个字符串参数.具体类如下:

Now, I'm assuming this is because my mapped concrete implementation requires a single string parameter for its constructor. The concrete class is as follows:

public sealed class MovementRepository : Repository, IMovementRepository
{
    public MovementRepository(string connectionString) : base(connectionString) { }
}

继承自:

public abstract class Repository
{
    public Repository(string connectionString)
    {
        _connectionString = connectionString;
    }

    public virtual string ConnectionString
    {
        get { return _connectionString; }
    }
    private readonly string _connectionString;
}

现在,我这样做是否正确?我的松耦合类型的具体实现中不应该有构造函数吗?IE.我应该删除构造函数并将 ConnectionString 属性设为 Get/Set 以便我可以执行以下操作:

Now, am I doing this the correct way? Should I not have a constructor in my concrete implementation of a loosely coupled type? I.e. should I remove the constructor and just make the ConnectionString property a Get/Set so I can do the following:

public static IMovementRepository MovementRepository
{
   get
   {
      return _container.Resolve<IMovementRepository>(
         new ParameterOverrides
         {
            { 
               "ConnectionString", _connectionString 
            }
         }.OnType<IMovementRepository>() );
   }
}

所以,我基本上想知道如何以匹配 IoC 规则的正确方式将我的连接字符串获取到我的具体类型,并保持我的控制器和具体存储库松散耦合,以便我可以在以后轻松更改数据源.

So, I basically wish to know how to get my connection string to my concrete type in the correct way that matches the IoC rules and keeps my Controller and concrete repositories loosely coupled so I can easily change the DataSource at a later date.

编辑 09:52:

只是为了重复我所追求的.我想知道将 ConnectionString 或 IRepositoryConfiguration 对象(更喜欢这个想法,感谢 Mc)传递给 Unity 的具体类的正确方法.我不太关心我通过什么,而是我如何在保持松散耦合的情况下通过它.

Just to re-iterate what I'm after. I want to know the correct way to pass the ConnectionString or an IRepositoryConfiguration object (prefer that idea, thanks Mc) to a concrete class from Unity. I'm not too fussed on what I pass, just how I pass it whilst maintaining loose coupling.

推荐答案

可能最直接的方法是在统一配置中为映射类型设置一个构造函数部分,并且在构造函数部分内部有一个参数元素用于传入您在 Web 配置的 connectionStrings 部分中定义的连接字符串的名称值的连接字符串.

Probably the most straight forward way to do this is to set up a constructor section for your mapped type in the unity configuration and inside the constructor section have a parameter element for the connection string that passes in a name value for a connection string you have defined in the connectionStrings section of your web configuration.

在 Repository 类的构造函数代码中,有一些代码使用连接字符串的名称值从 connectionStrings 部分获取完整的连接字符串.

Inside your constructor code for the Repository class, have some code that uses the name value of the connection string to get the full connection string from the connectionStrings section.

这是一个使用 Unity 2.0 的示例

Here's an example for you using Unity 2.0

在您的 web.config 中,指定连接字符串和统一映射以将 IRepository 映射到 SqlRepository.根据您的其他问题,我们假设 IRepository 在您的模型项目中,SqlRepository 在您的 DAL 项目中.

In your web.config, specify the connection string and a mapping for unity to map an IRepository<T> to a SqlRepository<T>. Based on your other questions, we'll assume that IRepository<T> is in your model project and SqlRepository<T> is in your DAL project.

<?xml version="1.0"?>
<configuration>
    <configSections>
        <section name="unity" type="Microsoft.Practices.Unity.Configuration.UnityConfigurationSection, Microsoft.Practices.Unity.Configuration" />
    </configSections>
    <connectionStrings>
        <add name="SqlConnection" connectionString="data source=(local)SQLEXPRESS;Integrated Security= SSPI; Initial Catalog= DatabaseName;" providerName="System.Data.SqlClient"/>
    </connectionStrings>
    <unity>
        <containers>
            <container>
                <types>
                    <type type="ModelProject.IRepository`1, ModelProject" mapTo="DALProject.SqlRepository`1, DALProject">
                        <constructor>
                            <param name="connectionString">
                                <value value="SqlConnection" />
                            </param>
                        </constructor>
                    </type>
                </types>
            </container>
        </containers>
  </unity>
</configuration>

现在是模型项目中的 IRepository 接口.在这个例子中,我还将使用 LINQ to SQL 从 SQL 数据库返回对象

Now for the IRepository<T> interface in the model project. In this example, I'm also going to be using LINQ to SQL to return objects from the SQL Database

namespace ModelProject
{
    /// <summary>
    /// Interface implemented by a Repository to return
    /// <see cref="IQueryable`T"/> collections of objects
    /// </summary>
    /// <typeparam name="T">Object type to return</typeparam>
    public interface IRepository<T>
    {
        IQueryable<T> Items { get; }
    }
}

以及DAL项目中的SQLRepository

namespace DALProject
{
    /// <summary>
    /// Generic class for returning an <see cref="IQueryable`T"/>
    /// collection of types
    /// </summary>
    /// <typeparam name="T">object type</typeparam>
    public class SqlRepository<T> : IRepository<T> where T : class
    {
        private Table<T> _table;

        public SqlRepository(string connectionString)
        {
            // use the connectionString argument value to get the
            // connection string from the <connectionStrings> section
            // in web.config
            string connection = ConfigurationManager.ConnectionStrings[connectionString].ConnectionString;

            _table = (new DataContext(connection)).GetTable<T>();
        }

        /// <summary>
        /// Gets an <see cref="IQueryable`T"/> collection of objects
        /// </summary>
        public IQueryable<T> Items
        {
            get { return _table; }
        }
    }
}

让我们也使用自定义控制器工厂来允许 unity 为我们返回控制器.这样,unity 将注入控制器具有的任何依赖项

Let's also use a custom controller factory to allow unity to return controllers for us. This way, unity will inject any dependencies that the controllers have

在 global.asax 中

In global.asax

namespace WebApplicationProject
{
    public class MvcApplication : System.Web.HttpApplication
    {
        public static void RegisterRoutes(RouteCollection routes)
        {
            // your routes
        }

        protected void Application_Start()
        {
            RegisterRoutes(RouteTable.Routes);
            ControllerBuilder.Current.SetControllerFactory(new UnityControllerFactory());
        }
    }

    public class UnityControllerFactory : DefaultControllerFactory
    {
        private IUnityContainer _container;

        public UnityControllerFactory()
        {
            _container = new UnityContainer();

            var controllerTypes = from t in Assembly.GetExecutingAssembly().GetTypes()
                                  where typeof(IController).IsAssignableFrom(t)
                                  select t;

            foreach (Type t in controllerTypes)
                _container.RegisterType(t, t.FullName);

            UnityConfigurationSection section = (UnityConfigurationSection)ConfigurationManager.GetSection("unity");

            section.Configure(_container);
        }

        protected override IController GetControllerInstance(RequestContext requestContext, Type controllerType)
        {
            // see http://stackoverflow.com/questions/1357485/asp-net-mvc2-preview-1-are-there-any-breaking-changes/1601706#1601706
            if (controllerType == null) { return null; }

            return (IController)_container.Resolve(controllerType);
        }
    }

}

这是一个控制器示例.PageSize 可以在基本控制器或作为属性的控制器上定义.

And Here's a controller example. PageSize might be defined on a base controller or on the controller as a property.

namespace WebApplicationProject.Controllers
{
    public class CustomersController : Controller
    {
        private IRepository<Customer> _customerRepository;
        public int PageSize { get; set; }

        public CustomersController() { }

        public CustomersController(IRepository<Customer> customerRepository)
        {
            this._customerRepository = customerRepository;
            // let's set it to 10 items per page.
            this.PageSize = 10; 
        }

        public ViewResult List(string customerType, int page)
        {
            var customerByType = (customerType == null) ?
                customerRepository.Items : customerRepository.Items.Where(x => x.CustomerType == customerType);

            int totalCustomers = customerByType.Count();
            ViewData["TotalPages"] = (int)Math.Ceiling((double)totalCustomers/ PageSize);
            ViewData["CurrentPage"] = page;
            ViewData["CustomerType"] = customerType;

            // get the right customers from the collection
            // based on page number and customer type.    
            return View(customerByType
                .Skip((page - 1) * PageSize)
                .Take(PageSize)
                .ToList()
            );
        }

    }
}

当调用客户列表控制器操作时,unity 将正确实例化控制器的 SqlRepository 实例并将其注入到构造函数中.用于 SqlRepository 的 connectionString 字符串在统一配置中设置,并被传递到类型化 SqlRepository 的构造函数中.

When the customers list controller action is invoked, unity will correctly instantiate an instance of SqlRepository<Customer> for the controller and inject this into the constructor. the connectionString string used for SqlRepository<T> is set in the unity configuration and is passed into the constructor for a typed SqlRepository<T>.

这篇关于如何正确使用 Unity 将 ConnectionString 传递给我的存储库类?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆