24
Snabbare/enklare utveckling med Webfoundation (RAD) • Förslag som med framgång redan har smygits in i en del kund projekt, testats och givit snabbare utveckling. – Konfigurerbar model(Mapper) med alternativt factory lager. – Mapper som alternativt Factory lager – Kontrollerbaserad MVC – Autoregistrering i Autofac – Använda standard MVC i största utsträckning eller open source alternativ, t.ex. form validation,js,css minifiering etc. • En fördel med dessa tekniker är att man kan smyga in dessa tekniker i nyutveckling på befintliga WF projekt.

Snabbare/enklare utveckling med Webfoundation (RAD)

  • Upload
    tamarr

  • View
    40

  • Download
    1

Embed Size (px)

DESCRIPTION

Snabbare/enklare utveckling med Webfoundation (RAD). Förslag som med framgång redan har smygits in i en del kund projekt, testats och givit snabbare utveckling. Konfigurerbar model ( Mapper ) med alternativt factory lager. Mapper som alternativt Factory lager Kontrollerbaserad MVC - PowerPoint PPT Presentation

Citation preview

Page 1: Snabbare/enklare utveckling med  Webfoundation  (RAD)

Snabbare/enklare utveckling med Webfoundation (RAD)

• Förslag som med framgång redan har smygits in i en del kund projekt, testats och givit snabbare utveckling.– Konfigurerbar model(Mapper) med alternativt factory

lager.– Mapper som alternativt Factory lager– Kontrollerbaserad MVC– Autoregistrering i Autofac– Använda standard MVC i största utsträckning eller open

source alternativ, t.ex. form validation,js,css minifiering etc.• En fördel med dessa tekniker är att man kan smyga in

dessa tekniker i nyutveckling på befintliga WF projekt.

Page 2: Snabbare/enklare utveckling med  Webfoundation  (RAD)

Konfigurerbar produkt modellVad

• Konfigurerbart kunna välja vilken data man vill ha ut i vyn för produkter.

• T.ex. kan utvecklare eller kund (med viss risk ) konfigurerbart välja vilken data som behövs i vyn för en produkt sida eller produktlistning eller relaterade produkter, carten etc.

• Borde utökas för att klara alla Enova objekt som adress mm.

Page 3: Snabbare/enklare utveckling med  Webfoundation  (RAD)

Konfigurerbar produkt modellFördelar

• Snabbare/enklare utveckling (slipper överlagra faktories, modeller och vill du t.ex. läsa av pris finns det troligen redan en klass för detta)

• Går att anpassa model strukturen mer då det är mer otypat (lugn i en vy kan man enkelt ändra).

• Bättre prestanda då allt automatiskt cachas och cachningen går att konfigurerbart kund anpassa. (T.ex. kan man välja att inte cacha pris men produkt namn beroende på språk. Vidare så riskerar man inte att läsa av properties som inte behövs för specifik kund.

• Enklare att återanvända då kund specifika properties separeras ut i en egen klass.

• Möjliggör att en specifik sida som t.ex. en produktlistning kan innehålla olika properties beroende på produkt. (Förutom att konfigurera önskade properties kan man programmatiskt välja properties)

• Oberoende av CMS.

Page 4: Snabbare/enklare utveckling med  Webfoundation  (RAD)

ImageMapper

ProductlistControllerRenderAction

Hjälp jag behöver modellen för

produktlistningen

CmsProductlistController

Vy och model för CMS’en

ProductlistService

Aktuella Enova produkter

ProductlistMapperModel för produktlistningen

(Factory)

ProductConfigMapperHjälp ge mig modellen för

varje produkt i produktlistningen med de

properties som är konfigurerade

Otypad (Dictionary) Model (ProductConfigViewModel) för varje produkt

ProductConfigurationService

UrlMapper

PriceMapper

EnovaPropertyMapper

Dessa properties skall läsas av och de skall läsas av t.ex. på bara produkten och/eller varianter etc.

CMS Produktlistningssida

http://www..

Produkt x1, pris,lagerProdukt x2 , pris,lagerProdukt x3, pris,lagerProdukt x4, pris,lagerProdukt x5 , pris,lagerProdukt x6, pris,lagerProdukt x7 , pris,lager

Produkt kategori x

LookupVaryByProductService

Dessa produkter skall du läsa av properties på

Property värdet

För tillfället konfigureras allt i en xml fil (runtime settings). Men alternativa servicar som

läser från Backoffice kommer/kan enkelt skapas.

Man kan enkelt lägga till egna kund anpassade property mappers. EnovaPropertyMapper kan läsa vilken property som hellst som finns på enova objektet medan t.ex. PriceMapper

bara kan läsa av price.

Konfigurerbar produkt modell flödet för t.ex. en produktlistning.

PropertyValuesCacheService

Cacha property värdet per språk eller behörighet

Page 5: Snabbare/enklare utveckling med  Webfoundation  (RAD)

InStockMapper

Konfigurerbar produkt modell flödet för t.ex. en produktlistning.

(Dynamic or typed) ViewData

Name Price InStock Url MyData

ImageMapper

UrlMapper

PriceMapper

EnovaPropertyMapper

MyDataMapper

Description

Page 6: Snabbare/enklare utveckling med  Webfoundation  (RAD)

Konfigurerbar produkt modellProperty mapper

• INamedPropertyMapper används för mappers som klarar av en specifik property som t.ex. pris.

• IPropertyMapper klarar av properties beroende på produkt eller konfigurering. T.ex. Enova properties, bild urler till olika storlekar som konfigureras med ett namn.

• Allting anropas med: _productConfigMapper.Map(WebFoundationProduct, pageId)

Page 7: Snabbare/enklare utveckling med  Webfoundation  (RAD)

Konfigurerbar produkt modellProperty mapper

• Ett exempel på hur man gör en property mapper som returnerar true eller false om produkten finns i lager. (Notera att kravet att det skall vara en lista av strängar troligen kommer att ändras.)

• public class IsInStockMapper : INamedPropertyMapper• {• private readonly IWarehouseService _warehouseService;• private readonly IsInStockMapperSettings _settings;

• public IsInStockMapper(IWarehouseService warehouseService,IsInStockMapperSettings settings )• {• _warehouseService = warehouseService;• _settings = settings;• }

• public string PropertyName• {• get { return "Stock"; }• }

• public List<string> Map(WebFoundationProduct product,string pageId)• {• //TODO borde in i WarehouseServicen• double stockQuantity = 0;• if (product.IsVariantOwner)• {• stockQuantity = product.GetVariantMembers<WebFoundationProduct>().Sum(p=>_warehouseService.GetStockQuantity(p));• }• else• {• stockQuantity = _warehouseService.GetStockQuantity(product);• }

• return new List<string> { (stockQuantity > _settings.InStockTreshold).ToString(CultureInfo.InvariantCulture) };• }

• public string DefaultVaryBy• {• get { return null; }• }• }

Page 8: Snabbare/enklare utveckling med  Webfoundation  (RAD)

Konfigurerbar produkt modellxml konfiguration

• För tillfället stöds bara xml konfiguration men backoffice stöd är önskvärt också.• I runtimme_settings.xml ställer man in följande exempel:

<setting key=“Location.Properties.PageIdentifier" value="Name,ID,listImage,Url,IsNewProduct,MoreVariants,Category" type="string" />

• Location anger man i koden t.ex. produktsida, produktlistning, relaterade produkter dvs plats i siten. PageIdentifier är valfrit, är man på en sida som inte är konfigurerad går den på inställningar för t.ex. Location.Properties

• Man kan ställa in att i produktlistningen skall t.e.x. Name läsas av även på dess varianter med: <setting key=“productList.varyBy.Name" value=“Variants" type="string" />

• För tillfället finns följande alternativ som man kan utöka genom arv eller interface (LookupVaryByProductService ):

public HashSet<WebFoundationProduct> GetVaryByProductList(WebFoundationProduct product, string varyBy) { switch (varyBy) { case "Product": return new HashSet<WebFoundationProduct>() { product }; case "ProductOrVariants": if (product.IsVariantOwner) return new HashSet<WebFoundationProduct>(m_productService.GetVariants(product)); return new HashSet<WebFoundationProduct>() { product }; case "VariantDefault": return new HashSet<WebFoundationProduct>() { m_variantService.GetDefaultVariant(product) ?? product }; case "Variants": return new HashSet<WebFoundationProduct>( m_productService.GetVariants(product) ); case "ProductAndVariants": var productList= new HashSet<WebFoundationProduct>(m_productService.GetVariants(product)); productList.Add(product); return productList; default: return null; } }

Page 9: Snabbare/enklare utveckling med  Webfoundation  (RAD)

Konfigurerbar produkt modellLathund

• Fråga: Jag vill läsa av en property som det inte finns stöd för idag.Svar: Skapa en property mapper genom att implementera IPropertyMapper eller INamedPropertyMapper

• Fråga: jag vill läsa av samma properties på alla varianter eller produkter som ingår i ett paket.Svar: Ärv/implementera (I)LookupVaryByProductService Eller skapa en ny property mapper som slår upp de andra produkterna och läser propertyn på dem också.

• Fråga: Jag vill special anpassa cachningen själv.Svar: Implementera IPropertyValuesCacheService eller ärv/implementera (I)ProductConfigMapper.

• Fråga: Jag vill inte bara returnera en dictionary med strängar utan en mer komplicerad objekt hiarki som produktens attribut kategoriserad på attributtyp.Svar: Skapa en specifik property mapper för det som inte returnerar en sträng.

• Fråga: Jag har redan en typad modell och vill inte behöva skriva om allt.Svar: Det går att kombinera med befintliga kundlösningar genom att du på din befintliga model lägger till en property med typen ProductConfigViewModel och anropar ProductConfigMapper.Map.

• Fråga: jag vill läsa av vissa properties på produkten när jag är i produktlistningen och vissa på produkt sidan.Svar: Man kan namnge olika sammanhang kallat location och t.om konfigurera per sida.

• Fråga: När är det inte lämpligt att använda detta?Svar: För tillfället stöds inte formulär data med detta då man måste göra en ny modelbinder som förstår den otypade datan. Eller när affärslogik måste läsa av modellen.

• Fråga: Kan inte prestandan bli dålig om jag måste slå upp samma Enova objekt flera gånger.Svar: Mellanlagra i httpcontext som t.e.x:

public class AttributeValuesMapper : IPropertyMapper {

protected EnovaAttributeType GetAttributeType(Context context, string property) { var key = "AttributeValuesAsStringMapper." + property; if (HttpContextFactory.Current.Items.Contains(key)) return HttpContextFactory.Current.Items[key] as EnovaAttributeType; var type = context.FindObject<EnovaAttributeType>(property); HttpContextFactory.Current.Items[key] = type; return type; }

Page 10: Snabbare/enklare utveckling med  Webfoundation  (RAD)

Mapper (Factory)• Mapper är ett försök till att förenkla Factory lagret i Webfoundation.• Motsvarar ungefär Factory.Create. • Factory namnet kan lätt förväxlas med design patternet medans Mapper begreppet är inarbetat som

t.e.x. AutoMapper. Namnet förklarar mer vad det gäller. Skapa och mappa data från Enova till en vy model.

• Modell klassen innehåller ingen kod bara properties. Pg.a. detta behöver modell klassen inget interface (Förutom om properties med validations attribut behöver variera med kundlösningar).

• Model klassens properties bör alltid vara för formaterade strängar som url, förformaterade priser etc. Detta för att få bort logik i vyn och effektivisera cachning.

• Mapper är inte singelton vilket gör att man enklare kan mellan lagra data som är jobbig att hämta i klassen. T.ex.. CartMapper vill kanske spara undan aktuell valuta etc istället för att slå upp det för varje cart rad.

• Mappers har bara en Metod: viewModel Mapper.Map(EnovaObject). • Man skapar en mapper per model klass för singel responsible. Så CartMapper kan anropa

CartItemMapper.• Stödjer Autofac på ett bättre sätt.• Mappers för anropa services mm. Man bör sträva efter att ha affärslogik i service klasserna och bara

presentations logik i mappern.• Man bör försöka hålla sig till max två nivåer av mappers som anropar varrandra. Ett sätt är att platta ut

model vyn och inte spegla enova objekt hiarkin rakt av. Eller anropa renderaction om modellen under inte är kopplad till huvud modellen.

Page 11: Snabbare/enklare utveckling med  Webfoundation  (RAD)

MapperAnrop

• Mappers anropar man i sin controller genom att först anropa en service för att hämta grunden till sin enova data och sedan anropar man mapperns Map metod.

public class CartController : Controller { private readonly ICartService _cartService; private readonly ICartMapper _mapper;

public CartSurfaceController(ICartService cartService, ICartMapper mapper) { _cartService = cartService; _mapper = mapper; }

public PartialViewResult MiniCart() { var cart = _cartService.GetCartOrCreateNew(); var model = _mapper.Map(cart); return PartialView(model); }}

Page 12: Snabbare/enklare utveckling med  Webfoundation  (RAD)

MapperCartMapper model

public class CartViewModel { public List<ICartItemViewModel> CartItems { get; set; } public string TotalPriceWithCurrency { get; set; }}

public class CartItemViewModel : ICartItemViewModel { public int ProductID { get; set; }

public string ProductArtNr { get; set; }

public string Name { get; set; }

public string Description { get; set; }

public string PriceWithCurrency { get; set; } }

Page 13: Snabbare/enklare utveckling med  Webfoundation  (RAD)

MapperCartMapper

public interface ICartMapper { CartViewModel Map(EnovaCart cart); }

public class CartMapper : ICartMapper { private readonly Func<CartViewModel> _createCartModel; private readonly ICartItemMapper _cartItemMapper;

public CartMapper(Func<CartViewModel> createCartModel, ICartItemMapper cartItemMapper) { _createCartModel = createCartModel; _cartItemMapper = cartItemMapper; }

public virtual ICartViewModel Map(EnovaCart cart) { var model = _createCartModel(); var context = cart.GetContext(); model.CartItems = cart.GetCartItems<CartItem>().Select(item => _cartItemMapper.Map(item)).ToList();

model.TotalPriceWithCurrency = context.AmountToString(totalPrice, context.CurrentCurrency, context.CurrentCurrency.Decimals, true, true); return model; } }

Page 14: Snabbare/enklare utveckling med  Webfoundation  (RAD)

MapperCartItemMapper

public interface ICartItemMapper { CartItemViewModel Map(CartItem cart); }

public class CartItemMapper : ICartItemMapper { private readonly Func<ICartItemViewModel> _createCartItemModel; private readonly ImageService _imageService;

protected bool _showPriceIncludingTax;

public CartItemMapper(Func<ICartItemViewModel> createCartItemModel, ImageService imageService) { _createCartItemModel = createCartItemModel; _imageService = imageService; _showPriceIncludingTax = true; }

public virtual ICartItemViewModel Map(CartItem cartItem) { var context = cartItem.GetContext(); // General var model = _createCartItemModel(); model.Name = cartItem.Name;

// Product if (cartItem is EnovaProductCartItem) { var productCartItem = cartItem as EnovaProductCartItem;

model.ProductArtNr = productCartItem.ProductIdentifier; model.Quantity = productCartItem.Quantity; model.Image = _imageService.GetImage(productCartItem.Product, ImageService.ImageSize.Small); model.PriceWithCurrency = context.AmountToString(productCartItem.Product.Price, context.CurrentCurrency, context.CurrentCurrency.Decimals, true, true);

} return model; }

}

Page 15: Snabbare/enklare utveckling med  Webfoundation  (RAD)

Mapper (Factory)

• Möjligen kanske alla mappers bör vara konfigurerbara mappers.

• I exemplet innan med cart och cartitems kan man tänka sig en property mapper som klarar av cartitems och som för varje item anropar ConfigMapper.

Page 16: Snabbare/enklare utveckling med  Webfoundation  (RAD)

MVC kontrollbaserat• I Webfoundation kör man strikt MVC, dvs en model som typat är komplett för en hel sida, vilket har en del nackdelar.

Page 17: Snabbare/enklare utveckling med  Webfoundation  (RAD)

MVC kontrollbaserat

• Kör man kontroll baserat dvs där man i vyn anropar RenderAction får man en mycket enklare lösning och mer lös kopplat.

• Färre nivåer i modellen och factories som anropar varrandra.• Lättare att lägga till och ta bort saker på sidan. Som t.ex.

lägga till en modell för twitter på sidan eller ta bort model för varianters färger.

• Alla vyerna och koden för en kontroll kan man enklare samla ihop i ett projekt eller en folder. Vilket enklare gör att man kan återanvända eller anpassa koden.

• Enklare att output cacha delar av sidan.

Page 18: Snabbare/enklare utveckling med  Webfoundation  (RAD)

MVC kontrollbaserat• Det betyder inte att man struntar i att separera ui från logik som i web.forms.• I exemplet med produktlistning, kan man t.e.x. lägga följande klasser i

samma folder:– ProductListController med en subaction– Modell klasserna.– Eventuella servicar specifikt för denna.– Vyerna måste tyvärr läggas separat under views men kan samlas under foldern

ProductList.• På så sätt ser man enklare var gemensamma funktioner ligger och man bör

sträva efter att försöka kategorisera ner de under respektive kontroll folder såvida det inte är en generell funktion. Detta kan leda till att det blir enklare att stycka upp stora servicar.

• Man bör dock kanske inte gå till absurdum som i web.forms kontroller som pratar med varandra och undvika renderaction i stora for loopar.

Page 19: Snabbare/enklare utveckling med  Webfoundation  (RAD)

MVC kontrollbaseratExempel sida

• Ett exempel på en sida och vilka kontroller den skulle kunna delas in i (hela sidan anropas av en huvud controller med sidans cms data):

Nyheter

Produktlistning

Topmeny

Sök Varukorg

Reklambanner

Produkt tips

Login

(Betyg)

Page 20: Snabbare/enklare utveckling med  Webfoundation  (RAD)

MVC kontrollbaseratExempel kod

• Exempel på hur det kan se ut i vyn:– <% Html.RenderAction("List", "ProductListWithFilter"); %>– <% Html.RenderAction("AccessoriesProduct", new { productId = Model.ID });%>

• I Controllern (notera hur jag kan få aktuell sida som inparameter då all routing data från sidan automatiskt skickas vidare.) [ChildActionOnly]

public PartialViewResult List(int cmsid, string layout) { var pageId = cmsid.ToString();

Page 21: Snabbare/enklare utveckling med  Webfoundation  (RAD)

Autoregistrering med Autofac• Målet är att utvecklaren inte skall behöva bry sig så mycket om Autofac

och dess komplexitet.Utvecklaren skapar bara en klass vars namn slutar med t.ex. Service så kan han injekta servicen sen med dess interface. Vill man byta ut en klass i Webfoundation skapar utvecklaren bara en klass som implementerar samma interface elelr ärver av webfoundation klasssen (Allt utan att behöva skriva någon Autofac registering).

• Förenklare modularisering av sin kod då man inte har registrering i en jätte modul eller applikations start.

• Auto registrerar alla typer i ens projekt genom namn konventioner eller klass attribut.

• Ta bort servicelocator klasser från WF och istället köra med strikt konstruktor injection. (Inget med autoregistrering men borde göras)

Page 22: Snabbare/enklare utveckling med  Webfoundation  (RAD)

Autoregistrering med AutofacExempel kod

• Registrera din Autofac modul på standard sätt, fast istället för att fylla den med massa registreringar anropa RegisterAllKnownServicesInAssembly , te.x. public class MyModule : Module

{ protected override void Load(ContainerBuilder builder) { //will autoregister all types in this assembly with naming convention builder.RegisterAllKnownServicesInAssembly(this.GetType().Assembly);

• Som valfria argument kan man skicka in vilka typer som inte skall registreras och vilka namn konventioner som skall gälla. Detta kan man även ställa in i web.config

• Typerna vill registreras som alla deras implementerade interface och bas klasser och som sin egna typ (loggas på tracelevel) om man inte anger annat.

Page 23: Snabbare/enklare utveckling med  Webfoundation  (RAD)

Autoregistrering med Autofacweb.config

• Följande web.config inställningar finns:• WebFoundation.AutoRegister.ExceptTypes

Gör att ingen autoregistrering kommer att ske på önskade typer (komma separerat fullständigt namn) som standard blank.

• WebFoundation.AutoRegister.singletonNameConventionEndsWithAutogeristrera alla klasser som singelton vars klass namn slutar med som standard: "Service", "Repository", "Provider", "Listener","Factory"

• WebFoundation.AutoRegister.newInstanceEachTimeNameConventionEndsWithAutogeristrera alla klasser att skapas upp alltid vars klass namn slutar med som standard: "Model", "Mapper", "Filter", "ViewData", "Settings"

• WebFoundation.AutoRegister.httpRequestNameConventionEndsWithAutogeristrera alla klasser att skapas upp per http request vars klass namn slutar med som standard: För tillfället inga då det är lite besvärligt att http request måste vara tillgängligt och att den inte nergraderar till ny instans varje gång annars.

• WebFoundation.AutoRegister.containerNameConventionEndsWith Autogeristrera alla klasser att skapas upp per scope vars klass namn slutar med som standard: inga.

• Standard värdena kan ändras. Fördelen med namn konventioner är att man mer kommer att följa dem och att man får rätt livstid på objekten utan att behöva tänka på det.

Page 24: Snabbare/enklare utveckling med  Webfoundation  (RAD)

Autoregistrering med AutofacAttribut

• Om man inte vill använda sig av namn konvention kan man använda sig av följande klass attribut.

• ExcludeFromAutoregisterKlassen följer en namn konvention men man vill inte registrera den automatiskt.

• Alla följande attribut har ett name argument om man vill registrera klassen med ett namn som t.ex. en MVC controller som kan se ut så här: [AutoRegister(Named = "controller.newspage")]

public class NewsPageController : FoundationController {

• AutoRegisterAsSingelton• AutoRegisterAsNewInstanceEachTime• AutoRegisterAttribute

Samma som AutoRegisterAsNewInstanceEachTime• AutoRegisterAsContainer• AutoRegisterAsHttpRequest