Exodus

Na początek zapowiedzniane w poprzednim wpisie kilka linijek o rozwiązaniu problemu z automatyczną walidacją danych – a właściwie jej brakiem. Rozwiązaniem okazała się być zmiana nagłówka metody kontrolera z takiego:

(generowanego automatycznie, tutaj dla scenariusza tworzenia nowego produktu)

[HttpPost]
public ActionResult Create(FormCollection collection)

na taki:

[HttpPost]
public ActionResult Create(ProductsViewModel viewModel)

gdzie ProductsViewModel jest klasą, na której bazuje silnie typowany widok dodawania produktu:

public class ProductsViewModel
{
	public ProductModel Product { get; set; } // patrz typ RegisteredProductModel przedstawiony w poprzednim wpisie
	public List<string> VatRates { get; set; } // stawki VAT możliwe do przypisania produktowi
}

Tak jak się spodziewałem – banał. Coż, tak bywa z rzeczami na tyle prostymi, że nie są jasno omówione w tutorialach. Przejdźmy jednak do tematu przewodniego dzisiejszego wpisu:

Pierwsza uruchamialna wersja programu
Po pięciu tygodniach pracy mogę wreszcie sprawdzić działanie kodu w praktyce – nie tylko, jak dotychczas, poprzez testy jednostkowe. Przez kilka dni nauki maglowałem tabelę RegisteredProducts bazy danych. Stworzyłem jej model, kontoler i widoki, realizujące przeglądanie, dodawanie, edytowanie i usuwanie produktów. Następnie wziąłem się za mechanizm rejestracji i logowania użytkowników. Jest on implementowany automatycznie, więc pozostało mi tylko zapoznać się z jego działaniem.
Dopiero teraz mogłem przemyśleć sposób przypisywania encji bazy danych konkretnym użytkownikom. Jako że nazwa użytkownika jest unikalna, zdecydowałem się na dodanie do tabeli RegisteredSellers kolumny UserName (co widać na załączonym w poprzednim akapicie schemacie) i używania jej jako łącznika między użytkownikiem, a encją. Po zarejestrowaniu nowego użytkownika, do bazy danych dodawany jest nowy obiekt RegisteredSeller – z jego nazwą umieszczoną w polu UserName.
Zarejestrowany użytkownik jest przenoszony na stronę swojego profilu, gdzie może podać dane takie jak imię, nazwisko, czy nazwa i adres firmy. Ma też do dyspozycji wspomnianą wcześniej stronę produktów, z możliwością filtrowania po nazwie i cenie. Niezalogowani użytkownicy mogą zobaczyć jedynie stronę O programie. Na razie to wszystko, aplikację w obecnej formie można pobrać tutaj.
Należałoby jeszcze wspomnieć o sposobie przechowywania (podczas sesji) informacji o zalogowanym użytkowniku. W dowolnym (oprócz konstruktorów) miejscu kontrolerów mam dostęp do jego nazwy:

string userName = User.Identity.Name;

Bardziej przydałby mi się jednak identyfikator encji RegisteredSeller powiązanej z użytkownikiem. Do repozytorium sprzedawców (RegisteredSellerRepository) dodałem więc taką metodę:

public int GetIdByUserName(string userName)
{
	using (MainDataContext dataContext = new MainDataContext(_connectionString))
	{
		return dataContext.RegisteredSellers.SingleOrDefault(seller => seller.UserName == userName).Id;
	}
}

Kontroler produktów uzupełniłem natomiast o metodę wywoływaną przed wykonaniem każdej akcji:

protected override void OnActionExecuting(ActionExecutingContext filterContext)
{
	if (Session["SellerId"] == null)
		Session["SellerId"] = new RegisteredSellerRepository().GetIdByUserName(User.Identity.Name);

	_sellerId = (int)Session["SellerId"]; // _sellerId - prywatne pole
	_repository = new RegisteredProductRepository(_sellerId); // _repository - prywatne pole

	base.OnActionExecuting(filterContext);
}

Zaznaczony fragment będzie powtarzał się w każdym kontrolerze, a wykorzystany zostanie podczas pojedynczej sesji tylko raz, przy wykonaniu pierwszej w programie akcji, dlatego takie rozwiązanie niezbyt mi się podoba. Na razie nie znalazłem jednak lepszego – może Czytelnicy takie znają?