Część z nas jest mniej lub bardziej leniwa. Części może przeszkadzać taki zapis, a części nie.
Szczególnie część, gdzie powtarzają się różnego rodzaju serwisy i repozytoria (@7-@14) oraz (@21-@27). Co czynić, jak zrobić to samo za mniej? Konwencje i autoskanowanie assembly’ów. Jak? Poniżej prosta ściągawka: Continue reading
dependency injection
Autofac update – dynamiczna zmiana implementacji

1: [HttpGet]
2: public ActionResult Register()
3: {
4: var viewmodel = new RegisterViewModel();
5: return this.View("Register", viewmodel);
6: }
7:
8: [HttpPost]
9: public ActionResult Register(RegisterDto dto)
10: {
11: this.accountService.Register(dto);
12: return this.RedirectToAction("Index");
13: }
1: public void Register(RegisterDto dto)
2: {
3: WebSecurity.CreateUserAndAccount(
4: dto.Email,
5: dto.Password);
6: }
1: public class RegisterDto
2: {
3: public string Email { get; set; }
4: public string Password { get; set; }
5: }
.csharpcode, .csharpcode pre
{
font-size: small;
color: black;
font-family: Consolas, “Courier New”, Courier, Monospace;
background-color: #ffffff;
/*white-space: pre;*/
}
.csharpcode pre { margin: 0em; }
.csharpcode .rem { color: #008000; }
.csharpcode .kwrd { color: #0000ff; }
.csharpcode .str { color: #a31515; }
.csharpcode .op { color: #0000c0; }
.csharpcode .preproc { color: #cc6633; }
.csharpcode .asp { background-color: #ffff00; }
.csharpcode .html { color: #800000; }
.csharpcode .attr { color: #ff0000; }
.csharpcode .alt
{
background-color: #f4f4f4;
width: 100%;
margin: 0em;
}
.csharpcode .lnum { color: #606060; }
1: [HttpGet]
2: public ActionResult NewUsers()
3: {
4: this.testService.CreateNewUsers();
5: return this.RedirectToAction("Index");
6: }
1: public void CreateNewUsers()
2: {
3: var dto = new RegisterDto
4: {
5: Email = Faker.InternetFaker.Email() + Faker.NumberFaker.Number(),
6: Password = Faker.NumberFaker.Number(1000000000, 2000000000).ToString()
7: };
8: this.accountService.Register(dto);
9: }
1: [HttpGet]
2: [Authorize]
3: public ActionResult NewBlogPost()
4: {
5: var viewmodel = new BlogPostViewModel();
6: return this.View("NewBlogPost", viewmodel);
7: }
8:
9: [Authorize]
10: [HttpPost]
11: public ActionResult NewBlogPost(NewBlogPostDto dto)
12: {
13: this.postingService.NewBlogPost(dto);
14: return this.RedirectToAction("Index");
15: }
1: public void NewBlogPost(NewBlogPostDto dto)
2: {
3: var userId = this.accountService.GetCurrentLoggedUserId();
4: var model = new BlogPost(userId, dto.Text, DateTime.Now);
5: this.database.BlogPosts.Add(model);
6: this.database.SaveChanges();
7: }
1: public class NewBlogPostDto
2: {
3: public string Text { get; set; }
4: }
1: class WebSecurityAccountService : IAccountService
2: {
3: public void Logout()
4: {
5: WebSecurity.Logout();
6: }
7:
8: public bool Login(LoginDto dto)
9: {
10: var result = WebSecurity.Login(dto.Email, dto.Password);
11: return result;
12: }
13:
14: public void Register(RegisterDto dto)
15: {
16: WebSecurity.CreateUserAndAccount(
17: dto.Email,
18: dto.Password);
19: }
20:
21: public int GetCurrentLoggedUserId()
22: {
23: return WebSecurity.CurrentUserId;
24: }
25: }
1: [HttpGet]
2: public ActionResult NewPosts()
3: {
4: this.testService.CreateNewPosts();
5: return this.RedirectToAction("Index");
6: }
1: public void CreateNewPosts()
2: {
3: var users = this.database.Users.ToList();
4: var random = users.ElementAt(Faker.NumberFaker.Number(users.Count - 1));
5: var dto = new NewBlogPostDto
6: {
7: Text = Faker.TextFaker.Sentences(3)
8: };
9:
10: SpecialAccountService.CurrentLoggedUserId = random.Id;
11:
12: this.postingService.NewBlogPost(dto);
13: }
1: public class SpecialAccountService : IAccountService
2: {
3: private readonly WebSecurityAccountService realAccountService;
4:
5: public SpecialAccountService()
6: {
7: this.realAccountService = new WebSecurityAccountService();
8: }
9:
10: public void Logout()
11: {
12: this.realAccountService.Logout();
13: }
14:
15: public bool Login(LoginDto dto)
16: {
17: return this.realAccountService.Login(dto);
18: }
19:
20: public void Register(RegisterDto dto)
21: {
22: this.realAccountService.Register(dto);
23: }
24:
25: static public int CurrentLoggedUserId { get; set; }
26:
27: public int GetCurrentLoggedUserId()
28: {
29: return CurrentLoggedUserId;
30: }
31: }
1: public class HijackService : IHijackService
2: {
3: public void RestoreIOC()
4: {
5: var builder = new ContainerBuilder();
6: builder.RegisterType<WebSecurityAccountService>().As<IAccountService>();
7: builder.Update(ApplicationIocContainer.Container);
8: }
9:
10: public void HijackIOC()
11: {
12: var builder = new ContainerBuilder();
13: builder.RegisterType<SpecialAccountService>().As<IAccountService>().SingleInstance();
14: builder.Update(ApplicationIocContainer.Container);
15: }
16:
17: public IOCStatus GetStatus()
18: {
19: var isHijacked = ApplicationIocContainer.Container.Resolve<IAccountService>() is SpecialAccountService;
20: return isHijacked ? IOCStatus.Hijacked : IOCStatus.Normal;
21: }
22: }
Autofacu czytaj szablony z configa – please.
Czyżbym wrócił do formy z blogiem?
Niektórzy może pamiętają, a inni nie, ale walczę z takim tam sobie prywatnym projekcikiem. Otóż nastał tam etap refaktoringu i właśnie wtedy postanowiłem, że trzeba wreszcie przenieść rejestrację komponentów w IOC z kodu do configa. Używam Autofac, więc zerknełem w dokumentację i dociągnełem ‘Autofac.Configuration’ z NuGeta, jest to potrzebne aby móc grzebać z configu. Zaraz potem wziełem się do roboty,wycinek wygląda tak:
Trzeba dodać informację o nowej sekcji:
1: <section name="autofac" type="Autofac.Configuration.SectionHandler, Autofac.Configuration" />
A potem w sekcji autofac lecimy w taki sposób:
.csharpcode, .csharpcode pre
{
font-size: small;
color: black;
font-family: Consolas, “Courier New”, Courier, Monospace;
background-color: #ffffff;
/*white-space: pre;*/
}
.csharpcode pre { margin: 0em; }
.csharpcode .rem { color: #008000; }
.csharpcode .kwrd { color: #0000ff; }
.csharpcode .str { color: #a31515; }
.csharpcode .op { color: #0000c0; }
.csharpcode .preproc { color: #cc6633; }
.csharpcode .asp { background-color: #ffff00; }
.csharpcode .html { color: #800000; }
.csharpcode .attr { color: #ff0000; }
.csharpcode .alt
{
background-color: #f4f4f4;
width: 100%;
margin: 0em;
}
.csharpcode .lnum { color: #606060; }
1: <autofac>
2: <components>
3: <component type="ToBeImplemented.Services.AboutService, ToBeImplemented.Services"
4: service="ToBeImplemented.Contracts.IAboutService, ToBeImplemented" />
5: </components>
6: </autofac>
type: określa jaka klasę z pełnym namespace, oraz podaje nazwę assembly
service: określa interfejs z pełnym namespace, oraz podaje nazwę assembly.
To oznacza w kodzie:
1: /*** ToBeImplemented project ***/
2: namespace ToBeImplemented.Contracts
3: {
4: public interface IAboutService
5: {
6: /* definition goes here */
7: }
8: }
9:
10: /*** ToBeImplemented.Services project ***/
11: namespace ToBeImplemented.Services
12: {
13: public class AboutService : IAboutService
14: {
15: /* implemenetation goes here*/
16: }
17: }
Wygląda to prosto? Mam nadzieję że tak, bo to jest proste i prosta sytuacja. Życie oczywiście nie jest takie, dlatego natrafiłem na taki problem
1: namespace ToBeImplemented.Services
2: {
3: using System.Collections.Generic;
4:
5: using ToBeImplemented.ViewModels;
6:
7: public class TagViewModelEqualityComparer : IEqualityComparer<TagViewModel>
8: {
9: /* code goes here */
10: }
11: }
Szablony, jak ich nie kochać? Idea jest piękna, jeden kod, wiele zastosowań. A teraz wracając do do autofaca i w zasadzie podstaw w .net i programowaniu i idei tego wpisu na blogu.
Jakiego typu jest IEqualityComparer<TagViewModel>? Hę? Bo ja się pomyliłem, zapomniałem o tym, czym naprawdę są szablony. Może sprawdzimy na takim przykładzie
1: using System;
2: using System.Collections.Generic;
3:
4: namespace ConsoleApplication1
5: {
6: class Program
7: {
8: static void Main(string[] args)
9: {
10: Console.WriteLine(typeof(IEqualityComparer<int>));
11: Console.WriteLine(typeof(TagViewModelEqualityComparer));
12: }
13: }
14:
15: public class TagViewModel
16: {
17: /* code goes here */
18: }
19:
20: public class TagViewModelEqualityComparer : IEqualityComparer<TagViewModel>
21: {
22: /* implementation goes here */
23: }
24: }
Co będzie na ekranie?
Draniu! I teraz do mnie: jak mogłem o tym zapomnieć! Czyli, gdy w config kiedyś zechce zarejestrować klasę korzystającą z templejtów lub będącą templejtem to muszę pamiętać, że nie implementuje templejta, tylko jego konkretną implementacje, w moim przypadku wygląda to tak:
1: <component type="ToBeImplemented.Services.TagEqualityComparer, ToBeImplemented.Services"
2: service="System.Collections.Generic.IEqualityComparer`1[[ToBeImplemented.Model.Tag, ToBeImplemented.Model]], mscorlib" />
Od teraz będzie działać dobrze. I żeby nie było, chciałem tylko podzielić się wrażeniami z templejtów. Oczywiście autofac to dużo więcej możliwości i problemów, ale mnie ostatnio spotkał tylko ten jeden (kłamczuch ze mnie).
Po dokładne szczegóły dotyczące użytkownia autofaca zapraszam na ich strone projektu, na SO albo może ktoś mnie zapytać jeśli się wstydzi czytać i pisać po angielsku.
I to tyle. Mam nadzieję, że zaoszczędziłem dzięki temu komuś trochę czasu.
Dependency injection i automapper
Od dłuższego czasu korzystam z automappera, jest to genialne rozwiązanie gdy trzeba mapować jeden obiekt na drugi. Gdy w klasach, z i do której chcemy mapować wszystkie typy właściwości/pól i ich nazwy się zgadzają nie trzeba robić praktycznie nic, gdy pojawiają się inne nazwy, wystarczy tylko wskazać że pole właściwość W w klasie A to właściwość X w klasie B i tyle, raz i spokój do końca projektu. Gdy typy się nie zgadzają, można skorzystać z konwerterów, zarówno na poziomie właściwości jak i całych klas. Jeśli nie wiesz o czym mówię wróć 🙂 tutaj za kilka dnia, a ja obiecuje że popełnie wpis o automapperze. Opis – Automappera. Ale nie o tym chciałem napisać. Jeśli ktoś nie korzysta to polecam, jeśli potrzeba pomocy to proszę dać znać, a teraz do rzeczy.
Czasem konwertery aby dobrze wykonać swoją robotę potrzebują innej klasy, oczywiści można ją stworzyć w konstruktorze, ale to nie będzie ładne rozwiązanie, wszystko przez mocne wiązania, które ostatnio wyszły z mody. Na szczęście wymyślono wstrzykiwanie zależności, a automapper nie przeszkadza w skorzystaniu z tej idei. Co zrobić, aby to zadziałało?
Wystarczy taki kawałek kodu dodać do inicjalizacji automappera
.csharpcode, .csharpcode pre
{
font-size: small;
color: black;
font-family: Consolas, “Courier New”, Courier, Monospace;
background-color: #ffffff;
/*white-space: pre;*/
}
.csharpcode pre { margin: 0em; }
.csharpcode .rem { color: #008000; }
.csharpcode .kwrd { color: #0000ff; }
.csharpcode .str { color: #a31515; }
.csharpcode .op { color: #0000c0; }
.csharpcode .preproc { color: #cc6633; }
.csharpcode .asp { background-color: #ffff00; }
.csharpcode .html { color: #800000; }
.csharpcode .attr { color: #ff0000; }
.csharpcode .alt
{
background-color: #f4f4f4;
width: 100%;
margin: 0em;
}
.csharpcode .lnum { color: #606060; }
1: public static void Initialize(IDependencyResolver current)
2: {
3: Mapper.Initialize(cfg => cfg.ConstructServicesUsing(t => current.GetService(t)));
4: // here goes other initialization of automapper
5: }
Pewnie się zapytacie skąd i jak jest wołane? Ja bym się pytał. Więc tak to robię ja, w Application_Start w global.asax.cs
1: ModelMapper.Initialize(DependencyResolver.Current);
Potem gdy chcemy skorzystać z jakiegoś konwertera, na liście parametrów w kontruktorze można podać to czego potrzbujemy i czekać aż framework zrobic swoją magię:
1: public class Id2ConceptStatistics : TypeConverter<long, ConceptStatistics>
2: {
3: private readonly IUnitOfWork unitOfWork;
4:
5: public Id2ConceptStatistics(IUnitOfWork unitOfWork)
6: {
7: this.unitOfWork = unitOfWork;
8: }
9:
10: protected override ConceptStatistics ConvertCore(long id)
11: {
12: var conceptStatistics = this.unitOfWork.ConceptStatistics.GetByID(id);
13: var result = conceptStatistics ?? new ConceptStatistics();
14: return result;
15: }
16: }
Powyżej przykład na to jak z long (który jest id) można zmapować na pełno prawną klasę, która jest zagnieżdżona gdzieś w innej klasie.
To tyle.