Top Banner
Хватит писать инфраструктурный код Вадим Мартынов, безработный программист
26

Хватит писать инфраструктурный код

Jul 27, 2015

Download

Software

Vadim Martynov
Welcome message from author
This document is posted to help you gain knowledge. Please leave a comment to let me know what you think about it! Share it to your friends and learn new things together.
Transcript
Page 1: Хватит писать инфраструктурный код

Хватит писать инфраструктурный код

Вадим Мартынов, безработный программист

Page 2: Хватит писать инфраструктурный код

Цели и желания

○ DRY○ Тестируемость кода○ Quick start○ Минимизация кода приложения○ Прозрачность aka простота использования○ DI (применение IOC-контейнеров)

Page 3: Хватит писать инфраструктурный код

Типовые инфраструктурные задачи○ Доступ к хранилищу данных○ Хостинг wcf-сервисов и клиентский вызов wcf○ Клиентский вызов asp.net mvc web api○ Хранение настроек

Page 4: Хватит писать инфраструктурный код

Доступ к данным1. Есть проект с Entity framework (>= 5.0.0.0) code first.2. Вы любите IoC, но не любите бесконечные регистрации новых сущностей.3. В качестве контейнера используется Unity (или есть возможность потратить 10 минут на допиливание

исходников под свой контейнер).4. Перспектива написания однотипного кода почему-то отпугивает вас.

Page 5: Хватит писать инфраструктурный код

Доступ к данным при помощи

Rikrop.Core.Data

Page 6: Хватит писать инфраструктурный код

Доступ к данным: 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> {}

Page 7: Хватит писать инфраструктурный код

Доступ к данным: 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);

});

Page 8: Хватит писать инфраструктурный код

Доступ к данным: 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);

Page 9: Хватит писать инфраструктурный код

Доступ к данным: 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);

}

Page 10: Хватит писать инфраструктурный код

WCFВозможно ли писать и использовать клиент-серверный код так же просто, как локальный?

Page 11: Хватит писать инфраструктурный код

WCF. Ожидание и реальность

Page 12: Хватит писать инфраструктурный код

WCF: ожидание и реальностьДобавление нового wcf-сервиса в существующий хост

Ожидание1. Создать ServiceContract.

2. Реализовать контракт.

3. Вызвать новый сервис на клиенте.

Реальность1. Создать ServiceContract.

2. Реализовать контракт.

3. Добавить в хост wcf код, инициализирующий ServiceHost для нового сервиса.

4. Добавить конфигурацию сервиса на сервере и не клиенте.

5. Добавить ServiceReference.

6. Зарегистрировать вклиентском IoC-контейнере Proxy-класс нового сервиса.

7. Вызвать новый сервис на клиенте.

Page 13: Хватит писать инфраструктурный код

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" />

Page 14: Хватит писать инфраструктурный код

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)));

Page 15: Хватит писать инфраструктурный код

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())));

Page 16: Хватит писать инфраструктурный код

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

Page 17: Хватит писать инфраструктурный код

Asp.net mvc web api 2До чего многословное название, надеюсь, пользоваться им легче, чем произносить

Page 18: Хватит писать инфраструктурный код

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>(); }}

Page 19: Хватит писать инфраструктурный код

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())) );

Page 20: Хватит писать инфраструктурный код

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. Документация:

Page 21: Хватит писать инфраструктурный код

Что ещё: Собственные наработки

• Предупреждение UI о выполнении асинхронной операцииinterface IBusyServiceExecutor : IServiceExecutor { bool IsBusy {get; } }class SingleCallBusyServiceExecutorWithPopup : BusyServiceExecutorBase

• INPC • ILogger, ILoggerFactory и реализации ConsoleLogger/NLogger

Page 22: Хватит писать инфраструктурный код

Что ещё

Page 23: Хватит писать инфраструктурный код

Что ещё

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);}

Page 24: Хватит писать инфраструктурный код

Что ещё

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

Page 25: Хватит писать инфраструктурный код

Что ещё: 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