Как правильно связать типы по соглашению?
Проекты нацелены на мультиплатформу, поэтому я использую максимум кода в библиотеках классов, чтобы его можно было легко использовать повторно.
Архитектура основана на принципе Model-View-Presenter.
Структура проекта следующая:
Solution
-> Domain
-> Domain.Tests
-> MVP
-> MVP.Tests
-> Web
-> Web.Tests
-> Windows
-> Windows.Tests
MVP
Этот проект содержит докладчиков и мнения о проекте. Например:
public interface IApplicationView : IView, IHasUiHandler<IApplicationUiHandler> {
}
public class ApplicationPresenter : Presenter<IApplicationView>
, IApplicationUiHAndler {
public ApplicationPresenter(IApplicationView view) : base(view) {
View.Handler = this;
}
}
Windows
Этот проект содержит графический интерфейс WPF приложения и так называемый Composition Root. Например:
public class ApplicationWindow : Window, IApplicationView {
}
public class App : Application {
protected override void OnStartUp(StartupEventArgs e) {
IKernel kernel = new StandardKernel();
kernel.Bind(x => x
.FromThisAssembly()
.SelectAllClasses().EndingWith("Window")
.BindAllInterfaces().EndingWith("View") // Here's what I'd like to do.
);
}
}
Web
Этот проект содержит страницы графического интерфейса ASP.NET для приложения и так называемый корень композиции.
public class ApplicationPage : Page, IApplicationView {
}
public class MvcApplication : HttpApplication {
protected override void Application_Start() {
IKernel kernel = new StandardKernel();
kernel.Bind(x => x
.FromThisAssembly()
.SelectAllClasses().EndingWith("Page")
.BindAllInterfaces().EndingWith("View") // Here's what I'd like to do.
}
}
Ну, я думаю, вы поняли...
Я довольно новичок в Dependency Injection, и даже новее в связывании конвенций.
Я хотел бы знать, как настроить привязки, используя соглашения с Ninject.
Есть идеи, как связать эти представления с Windows (WPF) и Pages (Web)?
РЕДАКТИРОВАТЬ
После того, как я попробовал то, что предложил @BatteryBackupUnit, я полагаю, что моя проблема заключается в поиске сборок.
using (var kernel = new StandardKernel())
kernel.Bind(scanner => scanner
.From(AppDomain.CurrentDomain
.GetAssemblies()
.Where(a => (a.FullName.Contains("MyProject.MVP")
|| a.FullName.Contains("Windows"))
&& !a.FullName.Contains("Tests")))
.SelectAllClasses()
.EndingWith("Window")
.BindSelection((type, baseType) =>
type.GetInterfaces().Where(iface => iface.Name.EndsWith("View"))));
Как указывалось ранее, View
интерфейсы не расположены в той же сборке, что и Window
классы. Приведенный выше код, основанный на ответе @ BatteryBackupUnit, прекрасно работает.
1 ответ
Как насчет этого:
using FluentAssertions;
using Ninject;
using Ninject.Extensions.Conventions;
using System.Linq;
using Xunit;
public interface ISomeView { }
public interface ISomeOtherView { }
public interface INotEndingWithViewWord { }
public class SomePage : ISomeView, ISomeOtherView, INotEndingWithViewWord
{
}
public class Demo
{
[Fact]
public void Test()
{
using (var kernel = new StandardKernel())
{
kernel.Bind(x => x
.FromThisAssembly()
.SelectAllClasses()
.EndingWith("Page")
.BindSelection((type, baseType) =>
type.GetInterfaces()
.Where(iface => iface.Name.EndsWith("View"))));
kernel.Get<ISomeView>().Should().BeOfType<SomePage>();
kernel.Get<ISomeOtherView>().Should().BeOfType<SomePage>();
kernel.Invoking(x => x.Get<INotEndingWithViewWord>())
.ShouldThrow<ActivationException>();
}
}
}
Примечание: я использую пакеты nuget
- Ninject
- Ninject.Extensions.Conventions
- xunit.net
- FluentAssertions
из этих xunit.net и FluentAssertions только для запуска теста и не будут использоваться в производстве.
Или вы могли бы также использовать .BindWith<T : IBindingGenerator>
или же .BindUsingRegex(...)
,