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

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

Embed Size (px)

Citation preview

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