私は文字通り、MicrosoftのUnity Application Blocks Dependency Injectionライブラリを使い始めたばかりで、行き詰まりました。
これは、具象クラスのインターフェイスタイプへのインスタンス化を処理するIoCクラスです(したがって、コントローラーにリポジトリが必要になるたびに、IoCコンテナーでResolveを呼び出す必要はありません)。
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>(); }
}
}
つまり、私のコントローラーから、次のことができるという考えです。
_repository = IoC.MovementRepository;
現在、次のエラーが発生しています。
例外は次のとおりです。InvalidOperationException-タイプStringを作成できません。この値を提供するようにコンテナーを構成する必要があります。
これは、マップされた具象実装がコンストラクターに単一の文字列パラメーターを必要とするためだと思います。具体的なクラスは次のとおりです。
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;
}
今、私はこれを正しい方法でやっていますか?疎結合型の具体的な実装にコンストラクターを含めるべきではありませんか?つまりコンストラクターを削除し、ConnectionStringプロパティをGet/Setにして、次のことができるようにする必要があります。
public static IMovementRepository MovementRepository
{
get
{
return _container.Resolve<IMovementRepository>(
new ParameterOverrides
{
{
"ConnectionString", _connectionString
}
}.OnType<IMovementRepository>() );
}
}
したがって、基本的には、IoCルールに一致し、コントローラーと具象リポジトリを疎結合に保ち、後日データソースを簡単に変更できるように、正しい方法で接続文字列を具象型に取得する方法を知りたいと思います。
編集09:52:
私が求めていることを繰り返します。 ConnectionStringまたはIRepositoryConfigurationオブジェクト(Mcに感謝します)をUnityの具象クラスに渡す正しい方法を知りたいです。疎結合を維持しながら、通過するものにあまり煩わされることはありません。
おそらくこれを行う最も簡単な方法は、ユニティ構成でマップされた型のコンストラクターセクションを設定することです。コンストラクターセクション内には、定義した接続文字列の名前値を渡す接続文字列のパラメーター要素があります。 Web構成のconnectionStringsセクションにあります。
Repositoryクラスのコンストラクターコード内に、接続文字列のname値を使用して、connectionStringsセクションから完全な接続文字列を取得するコードをいくつか含めます。
編集:
Unity2.0を使用する例を次に示します。
Web.configで、接続文字列とUnityのマッピングを指定して、IRepository<T>
をSqlRepository<T>
にマップします。他の質問に基づいて、IRepository<T>
がモデルプロジェクトにあり、SqlRepository<T>
がDALプロジェクトにあると想定します。
<?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<T>
インターフェイスについて説明します。この例では、LINQ to SQLを使用して、SQLデータベースからオブジェクトを返します。
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<T>
クラス
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がコントローラーを返すことができるようにします。このようにして、ユニティはコントローラーが持つ依存関係を注入します
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
は、ベースコントローラーまたはコントローラーでプロパティとして定義されている場合があります。
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<Customer>
のインスタンスを正しくインスタンス化し、これをコンストラクターに挿入します。 SqlRepository<T>
に使用されるconnectionString文字列は、Unity構成で設定され、型指定されたSqlRepository<T>
のコンストラクターに渡されます。
このためにUnityコンテナーを構成できます。
IUnityContainer container = new UnityContainer()
.RegisterType<IMovementRepository, MovementRepository>(
new InjectionConstructor("connectionstring goes here"));
xLMでは、おそらく次のようになります。
<type type="foo.IMovementRepository,foo" mapTo="foo.MovementRepository,foo">
<typeConfig extensionType="Microsoft.Practices.Unity.Configuration.TypeInjectionElement, Microsoft.Practices.Unity.Configuration">
<constructor>
<param name="connectionString" parameterType="System.String" >
<value value="conectionstring goes here" type="System.String"/>
</param>
</constructor>
</typeConfig>
</type>
または、mcaaltuntasが指摘するように接続文字列をラップします。
Unityは使用しませんでしたが、このような状況で構成オブジェクトを使用しました。たとえば、次のようにコードを書くことができます
class Configuration:IRepositoryConfiguration,IMailConfiguration
{
private string connectionString;
//IRepository configurations
public string ConnectionString
{
//read connection string from somewhere
get { return connectionString; }
}
//EMail configurations
public string Smtp
{
get { return smpt; }
}
}
interface IRepositoryConfiguration
{
string ConnectionString { get;}
}
public abstract class Repository
{
public Repository(IRepositoryConfiguration configuration)
{
_connectionString = configuration.ConnectionString;
}
public virtual string ConnectionString
{
get { return _connectionString; }
}
private readonly string _connectionString;
}
したがって、IRepositoryConfigurationを登録すると、Unityが構成オブジェクトを解決します。また、このアプローチでパラメータを簡単に追加できます。
具象クラス(抽象リポジトリとMovementRepository)にIRepositoryConfigurationオブジェクトを受け入れるコンストラクターがあっても大丈夫だと思います。それらはIMovementRepository。の実装の詳細と具体的な実装であるため、接続文字列を知る必要があります。
セッターインジェクションよりコンストラクターインジェクションの方が好きです。コンストラクターインジェクションは、より発見しやすいAPIにつながると思います。コンストラクタインジェクションでは、オブジェクトをインスタンス化すると同時に、オブジェクトが機能するために必要なものがわかりますが、セッターインジェクションでは、APIを使用するために設定するプロパティを学習する必要があります。詳細については、以下を参照してください コンストラクターveセッターインジェクション
別の方法を追加します:)
型を登録するときにコンストラクターに渡されるインジェクションパラメーターを使用できます。このようにして、コンストラクターインジェクションを引き続き使用できます。
class Repository : IRepository {
readonly string m_ConnectionString;
public Repository(string connectionString) {
ConnectionString = connectionString;
}
}
//When registering
container.RegisterType<IRepository, Repository>(new InjectionConstructor("connectionstring"));