Upload
vadim-martynov
View
413
Download
5
Embed Size (px)
Citation preview
Хватит писать инфраструктурный код
Вадим Мартынов, безработный программист
Цели и желания
○ DRY○ Тестируемость кода○ Quick start○ Минимизация кода приложения○ Прозрачность aka простота использования○ DI (применение IOC-контейнеров)
Типовые инфраструктурные задачи○ Доступ к хранилищу данных○ Хостинг wcf-сервисов и клиентский вызов wcf○ Клиентский вызов asp.net mvc web api○ Хранение настроек
Доступ к данным1. Есть проект с Entity framework (>= 5.0.0.0) code first.2. Вы любите IoC, но не любите бесконечные регистрации новых сущностей.3. В качестве контейнера используется Unity (или есть возможность потратить 10 минут на допиливание
исходников под свой контейнер).4. Перспектива написания однотипного кода почему-то отпугивает вас.
Доступ к данным: Rikrop.Core.Data
Подготовка инфраструктуры:1. Для проектов с Entity: PM> Install-Package Rikrop.Core.Data
Для проекта с контекстом БД: PM> Install-Package Rikrop.Core.Data.Unity
2. container.RegisterRepositoryContext<MyDbContext>(); //container.RegisterRepositoryContext(s => new MyDbContext(s), "myConStr"); //container.RegisterRepositoryContext<MyDbContext>(
new PerRequestLifetimeManager());
container.RegisterRepositories(typeof(Department).Assembly);3. public class Department
: Entity<Int32>, IRetrievableEntity<Department, Int32> {} public class Employee
: DeactivatableEntity<Int32>, IRetrievableEntity<Employee,Int32> {}
Доступ к данным: Rikrop.Core.Data
Использование:var employeeRepository = container.Resolve<IRepository<Employee, int>>();
var employees = employeeRepository.Get(q =>
{
q = q.Filter(e => e.EmploymentDate >= new DateTime(2014, 9, 1))
.Include(e => e.Department, e => e.Department.Chief)
.OrderBy(e => e.Name);
if (excludeFired)
q = q.Filter(e => !e.Fired);
});
Доступ к данным: Rikrop.Core.Data
Расширяемость:1. public interface IPersonRepository : IDeactivatableRepository<Person, int>
{Person RecursiveLoad(Person parentCategory);
}
2. [Repository(typeof(IPersonRepository))] public class PersonRepository : DeactivatableRepository<Person, int>, IPersonRepository { public Person RecursiveLoad(Person parent) { Context.Entry(parent).Collection(d => d.Children).Load(); parent.Children.ForEach(x => RecursiveLoad(x)); return parent; } }
3. var personRepository = container.Resolve<IPersonRepository>(); var p = personRepository.RecursiveLoad(person);
Доступ к данным: Rikrop.Core.Data
Интерфейс:public interface IRepository<TEntity, in TId> : IRepository
where TEntity : class, IRetrievableEntity<TEntity, TId>, IEntity<TId>
{
TEntity Get(TId id);
IReadOnlyCollection<TEntity> Get(Action<RepositoryQuery<TEntity>> query);
IReadOnlyCollection<TEntity> GetAll();
//IQueryable<TEntity> GetQueryable();
TEntity Save(TEntity entity);
void Delete(TEntity entity);
}
WCFВозможно ли писать и использовать клиент-серверный код так же просто, как локальный?
WCF. Ожидание и реальность
WCF: ожидание и реальностьДобавление нового wcf-сервиса в существующий хост
Ожидание1. Создать ServiceContract.
2. Реализовать контракт.
3. Вызвать новый сервис на клиенте.
Реальность1. Создать ServiceContract.
2. Реализовать контракт.
3. Добавить в хост wcf код, инициализирующий ServiceHost для нового сервиса.
4. Добавить конфигурацию сервиса на сервере и не клиенте.
5. Добавить ServiceReference.
6. Зарегистрировать вклиентском IoC-контейнере Proxy-класс нового сервиса.
7. Вызвать новый сервис на клиенте.
WCF: ожидание и реальностьИзменение настроек (адреса сервера, типа привязки)
Ожидание1. В серверной регистрации изменить
строку с типом привязки или адресом.
2. В клиентской регистрации изменить строку с типом тип привязки или адресом.
Реальность<endpoint address="http://localhost:8500/LE/Services/Emission/EmissionReferencesService" binding="basicHttpBinding" bindingConfiguration="BasicHttpBinding_IEmissionReferencesService" contract="EmissionReferencesServiceReference.IEmissionReferencesService" name="BasicHttpBinding_IEmissionReferencesService" /><endpoint address="http://localhost:8500/LE/Services/Emission/EmissionService" binding="basicHttpBinding" bindingConfiguration="BasicHttpBinding_IEmissionService" contract="EmissionServiceReference.IEmissionService" name="BasicHttpBinding_IEmissionService" /><endpoint address="http://localhost:8500/LE/Services/InjurySafety/InjurySafetyReferencesService" behaviorConfiguration="InjurySafetyReferencesServiceBehavior" binding="basicHttpBinding" bindingConfiguration="BasicHttpBinding_IInjurySafetyReferencesService" contract="InjurySafetyReferencesServiceReference.IInjurySafetyReferencesService" name="BasicHttpBinding_IInjurySafetyReferencesService" /><endpoint address="http://localhost:8500/LE/Services/InjurySafety/WorkplaceInjurySafetyConditionsService" behaviorConfiguration="HugeDataTransferServiceBehavior" binding="basicHttpBinding" bindingConfiguration="BasicHttpBinding_IWorkplaceInjurySafetyConditionsService" contract="WorkplaceInjurySafetyConditionsServiceReference.IWorkplaceInjurySafetyConditionsService" name="BasicHttpBinding_IWorkplaceInjurySafetyConditionsService" /><endpoint address="http://localhost:8500/LE/Services/IndividualProtection/IndividualProtectionReferencesService" behaviorConfiguration="IndividualProtectionReferencesServiceBehavior" binding="basicHttpBinding" bindingConfiguration="BasicHttpBinding_IIndividualProtectionReferencesService" contract="IndividualProtectionServiceReference.IIndividualProtectionReferencesService" name="BasicHttpBinding_IIndividualProtectionReferencesService" />
WCF: Быстро. Прозрачно. Гибко
Подготовка инфраструктуры:1. Install-Package Rikrop.Core.Wcf.Unity
2. Пишем ServiceContract и их реализации
3. На сервере и клиенте добавляем одну строку регистрации в IoC (конфиги править не надо)
4. Поднимаем хосты с двух строк:var assembly = Assembly.GetExecutingAssembly();_serviceHostManager.StartServices(assembly);
5. На клиенте резолвим IServiceExecutor<TService>. Эта обёртка служит для вызова методов сервиса и скрывает работу с каналом.
6. Можно пользоватьсяvar articles = await _serviceExecutor.Execute(s => s.GetArticles(theme)));
WCF: Быстро. Прозрачно. Гибко
Расширяемость:container
.RegisterType<ISessionResolver<Session>, SessionResolver<Session>>()
.RegisterServerWcf(
o => o.RegisterServiceConnection(reg => reg.NetTcp(serviceIp, servicePort))
.RegisterServiceHostFactory(reg => reg.WithBehaviors()
.AddErrorHandlersBehavior(eReg => eReg.AddBusinessErrorHandler().AddLoggingErrorHandler(NLogger.CreateEventLogTarget()))
.AddDependencyInjectionBehavior()
.AddCustomBehavior<ActionPerformanceTrackingBehavior>()
.AddServiceAuthorizationBehavior(sReg => sReg.WithStandardAuthorizationManager()
.WithStandardSessionHeaderInfo(“TimeTracker", "SessionId")
.WithOperationContextSessionIdInitializer()
.WithSessionAuthStrategy<Session>()
.WithLoginMethod<ILoginService>(s => s.Login())
.WithOperationContextSessionIdResolver()
.WithInMemorySessionRepository()
.WithStandardSessionCopier())));
WCF: Быстро. Прозрачно. Гибко
http://habrahabr.ru/post/246961/
https://github.com/Vadimyan/Rikrop.WcfExample https://github.com/Vadimyan/Rikrop.WcfExample/tree/AuthorizationExample
https://github.com/rikrop/Rikrop.Core.Wcf https://github.com/rikrop/Rikrop.Core.Wcf.Unity
Asp.net mvc web api 2До чего многословное название, надеюсь, пользоваться им легче, чем произносить
Mvc web api. Вызов из клиента .net
Ожиданиеvar executor = container.Resolve<IServiceExecutor<IArticleController>>();
var articles = await executor.Execute(c => c.GetArticles(theme.Id));
var newArticleId = await executor.Execute(c => c.PostArticle(article));
await executor.Execute(c => c.Delete(newArticleId));
Реальностьusing (var client = new HttpClient()){ client.BaseAddress = new Uri("http://localhost:9000/"); client.DefaultRequestHeaders.Accept.Clear(); client.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));
HttpResponseMessage response = await client.GetAsync("api/articles/3");
if (response.IsSuccessStatusCode) { var article = await response.Content.ReadAsAsync<Article>(); }}
Mvc web api. Сбываем мечту
container .RegisterClientWebApi( o => o.RegisterServiceExecutor(reg=> reg.Standard() .WithErrorHandlers() .AddHttpStatusCodeToExceptionHandler()) .RegisterMultipartContainer() .RegisterConnection(reg => reg.Https(baseAddress) .WithBehaviors() .AddRouteResolverBehavior(x => x.AddReflectionResolver() .AddExternalResolver<RouteActionsMapper>()) .AddAuthorizationBehavior(x => x.WithBearerAuthStrategy() .WithInMemorySessionRepository())) );
Mvc web api. Готовые решения: WebApiProxy1. PM> Install-Package WebApiProxy.Csharp
2. Add configuration WebApiProxy.config:<proxy endpoint="http://myservice.net/api" />
3. Можно пользоватьсяusing (var client = new PeopleClient()){ var response = await client.GetAsync("Fanie"); var people = await response.Content.ReadAsAsync<Person[]>();}
4. Документация:
Что ещё: Собственные наработки
• Предупреждение UI о выполнении асинхронной операцииinterface IBusyServiceExecutor : IServiceExecutor { bool IsBusy {get; } }class SingleCallBusyServiceExecutorWithPopup : BusyServiceExecutorBase
• INPC • ILogger, ILoggerFactory и реализации ConsoleLogger/NLogger
Что ещё
Что ещё
1. public interface IWindowPositionProvider{ Rect Position { get; set; } bool IsMaximazed { get; set; } Task SaveSettings();}
2. public class SettingsWindowPositionProvider : IWindowPositionProvider {} public class XmlWindowPositionProvider : IWindowPositionProvider {}
3. public interface IWindowPositionProviderFactory{ Task<IWindowPositionProvider> Get(string key);}
Что ещё
4. public interface ISettingsProvider{ Task<TSettings> Get<TSettings>(); Task<TSettings> Get<TSettings>(string key); Task Save<TSettings>(TSettings settings); Task Save<TSettings>(TSettings settings, string key);}
5. Rikrop.Core.SettingsProvider.dllRikrop.Core.SettingsProvider.AppConfig.dllRikrop.Core.SettingsProvider.Database.EnityFramework.dllRikrop.Core.SettingsProvider.Xml.dll
6. https://docs.nuget.org/create/hosting-your-own-nuget-feeds https://www.myget.org/ https://www.nuget.org/packages/upload
Что ещё: AspNetBoilerplate• Documentation• Overall
• NLayer Architecture• Module System• Startup Configuration• Dependency Injection• Logging• Nuget packages• Change logs
• Domain layer• Entities• Repositories• Unit Of Work• Domain Events (EventBus)
• Application layer• Application Services• Data Transfer Objects• Validating Data Transfer Objects
• Presentation layer• Dynamic Web API Layer• Javascript API• Localization• Navigation• Handling exceptions• Embedded resource files
• [email protected]• http://www.rikrop.ru• https://www.nuget.org/packages?q=rikrop• https://github.com/rikrop