Keistas ValueProvider veikimas ASP.NET MVC.

įvertino 0 Atsakyta (Patvirtinta) Ši žinutė turi 1 patvitintas atsakymas | 14 Atsakymai | 3 Šalininkai

Top 10 autorių
Vyras
21 Žinutės
Taškai 405
mipi3 parašyta 08-24-2009 0:20

Sveiki,

iš anksto atsiprašau už per ilgą postą, bet trumpiau nesigavo.

Jau kokį laiką bandau ASP.NET MVC ir neseniai susidūriau su problema, kai reikėjo atlikti paprastą UpdateModel paprastam modeliui tas modelis atsinaujino neteisingai. Pasirodo defoltinis ValueProviderDictionary ima duomenis bindinimui ne tik iš POST'o, bet ir iš RouteData ir QueryString'o.

Tai yra, jei aš turiu kokį modelį su properčiu, kuris (netyčia, nes sunku susekt) sutampa su kokia RouteData reikšme, iškviečiant UpdateModel modeliui tas propertis atsinaujina su Route reikšme(!), kuri su modeliu gali neturėti nieko bendra.

Nepagaunu minties, kam reikia POST metodui imti reikšmes iš RouteData?! Ir keista, kad tai nėra kokiu nors paprastu būdu konfigūruojama.

Tam, kad apeiti tą funkcionalumą, teko darytis savo ValueProvider. Jame palikau tik reikšmių paiešką POST formoje. Bet priskyręs savo ValueProvider kontroleriui ir kreipiantis į GET metodus, ASPas pradėjo keiktis, kad nežino iš kur ištraukti parametrus GET motodui. Tai teko žaisti čia. Teko kaitalioti kontroleryje einamus ValueProvaiderius atsižvelgiant į metodo HTTP kvietimo tipą ir pan.

Panaršius internete man susidarė įspūdis, kad tokia problema iškilo tik man vienam. Kažkiek apie ją užsiminė Hanselmanas, bet jos, sprendžiant iš turinio, neišsprendė, ir nelabai dėl jos nerimavo (tiesiog pakeitė modelio propertį Dinner.Id į Dinner.DinnerID !!?, keitė modelį(!) dėl per mandro ValueProvider) . Išspręst aišku galima šią problemą pataisius Route kelius, arba kaitaliojant ValueProviderius, kaip aš dariau, bet man tai atrodo kaip hakinimas...

Gal kas iš jūsų buvo susidūręs su ta problema, ar aš čia ne iš to kampo žiūriu į šį reikalą? :)

 

Atsakyta (Patvirtinta) Patvitintas atsakymas

Top 10 autorių
Vyras
21 Žinutės
Taškai 405
Atsakyta (Patvirtinta) mipi3 atsakyta 08-26-2009 0:01
Verified by Domantas

Pagaliau susikalbėjom :) Aš blogai paaiškinau, nepaminėjau, kad ta problema iškyla neįvedus visų modelio laukų į formą.

RouteData daugelyje mvc aplikaciju glaudziai risasi su redaguojamu modelio kontekstu ir tik vienetiniais atvejais (kaip pas tave) gali reiksti ir kita konteksta


Nesutinku, kad vienetinis atvejis. Užtenka naujame asp.net mvc projekte sukurti modelį su Int Id properčiu. Ir padaryti jam Create(). Viskas gausi Exceptioną, kad stringas yra bindinamas ant Int. Tai yra paprasčiausia bėda. Bet čia vienas atvejis, bet gali būti ir kitų, nes aš nežinau kaip vystysis mano modeliai ir keliai laike, visko gali būti. Tai yra vieta, kur reikės būti atsargiam. Nes ją nelabai ir ištestuosi. Pvz situacija. Turiu modelį, kuris sėkmingai buvo Updeitinamas, ilgą laiką, be problemų, visi laukai eina per Post. Per kelis metus aš kažką pataisau, gal kelius, gal dar ką. Viskas veikia toliau gerai. Tada aš nusprendžiu pervadinti kokį modelio propertį, pervadinu ir pamirštu pervadinti View formoje lauką. Ir tada tarkim sutapo mano naujai pervadintas propertis su kokia kelio reikšme. Gal praeis kelios dienos ar savaitės kol aš pastebėsiu klaidą. Žodžiu man nereikia per mandro ValueProviderio, man reikia paprasto ir saugaus ValueProvider'io. Ir man nesuprantama, kam toks reikalingas, tiksliau kodėl toks uždėtas pagal nutylėjimą?

Supratau tavo mintį su ModelBind, bet nelabai suprantu, kaip tai veiks jei sutaps ir Form key'us ir RouteData key'us? Pridėk šią eilutę Test.aspx

  Type : <input type="text" name="Type" />

ir kai apdeitinsi dabar Type reikšmė neišsisaugos, nes ji dubliuojasi ir RouteData ir Formoje. Kaip sakiau problema ir yra, kad neaišku iš kur paimta ta reikšmė, tai dar šiek tiek sudetingina užduotį.

Aš vis tik lieku prie savo sprendimo. Nors jis man ir netobulas (neveikia kai paupdeitintas Modelis gaunamas per parametrus :( , čia jau tikrai MVC problema nes nėra tos tikslios vietos, kur galima priskirt savo ValueProvideri kontroleriui). Beje patobulinau jį kiek galėjau, kad jis tiktų visiems. Dabar virš kontrolerio klasės ar metodo galima parašyti atributą pavadinau jį SmartValueProvider :D

  [SmartValueProvider(ValueSource.Form | ValueSource.QueryString | ValueSource.RouteData)] // mintis manau aiški :)

arba

  [SmartValueProvider(ValueSource.Form)]  // kaip mano atveju

Pavyzdys kaip tai atrodo. Taip pat išeities tekstas šio daikto, jei įdomu.

įdėjau naujesnį mūsų testinio projekto variantą.

Tai vistik aš taip ir nesupratau. Kodėl pagal nutylėjimą duomenys imami ir iš RouteData ir QueryString kai vyksta Post? Taip turi būti? Ar čia įžvelgia kas nors, apart manęs, hmmm... neintuityvų ir problematišką funkcionalumą :) ?

 

 

  • | Žinučių taškai: 70

Visi atsakymai

Top 10 autorių
Vyras
41 Žinutės
Taškai 760

Na ASP .NET MVC bindinimas vyksta tokia tvarka :

  • RouteData
  • Form
  • QueryString

Ir nieko cia nepadarysi :) Na o rasyti cia sitam ValueProvider manau kad neapsimoka. Geriau pakeisti RouteData mappinima ar naudoti kitus parametrus

Top 10 autorių
Vyras
21 Žinutės
Taškai 405

Nedaug suklydai dėl tvarkos, dabar jinai tokia (anksčiau buvo kitokia, pataisė):

  1. Form
  2. RouteData
  3. QueryString

Bet keisti RouteData nemanau, kad yra išeitis, čia tik laikinas sprendimas. Tarkim aš pakeisiu dabar RouteData, kad patenkinti mano kažkokio vieno modelio poreikius. Gerai jei jis yra vienas, o jei jų yra daug. Reiškia kokiam nors modeliui pridedant naują, ar keičiant seną propertį, aš turiu visada sutikrinti ar jo tas naujas propertis nesipjauna su RouteData reikšme, ir atvirkščiai įdėjus RouteData reikšmę aš turiu įsitikinti, kad jis nesipjaunu su kokiu esamu modeliu. Tai yra papildomas galvos skausmas. Ir šitoj vietoj atsiranda puiki spraga visokioms stebuklingom klaidom. Aš dar suprantu, kad ta galimybė (POST formos reikšmėms pridėti RouteData reikšmes) būtų kažkur naudojama, bet aš nelabai sugalvoju, kas tai naudoja ir kada. Tiesiog man šita vieta kaip rakštis, jei ją paliksiu kada nors suskaus, gal ir labai stipriai :)

Kol kas lieku su savo šios problemos 'workaround'u naudojant kelis ValueProvider'ius.  Beje jo kodą jūsų teismui įdėjau čia.

Top 10 autorių
Vyras
59 Žinutės
Taškai 1,255

Dar noriu įsiterpti į diskusiją, jeigu aš dar gerai atsimenu, tai tvarkoje trūksta dar vienos eilutės:

  1. Form
  2. RouteData
  3. QueryString
  4. Cookie
Top 10 autorių
Vyras
41 Žinutės
Taškai 760

Na taip, matau kad nuo MVC RTM tvarka pasikeite - zinosiu dabar ;) Tik va nesuprantu vieno, kaip gali RouteData reiksme uzsisetinti, jei tavo idetame kode yra metodas AddToDictionaryIfNotPresent, kuris uztikrina, kad su tokiu paciu key nebutu idetas rezultatas. Taigi darosi cia idomu. Jei formos key ir RouteData key sutampa, tai tik formos reiksme bus ideta i ValueProviderDictionary. Cia viskas atrodo ivyksta UpdateModel metode. Jis ne kas kita kaip sukuria ModelBindingContext ir kartu su Controllerio contextu paduoda i binderi. Siuo atveju lieka du variantai. Arba binderis yra tavo uzsetintas arba naudojamas DefaultModelBinder. Pasiziurek gal turi koki binderi susikures. Pasidebuggink jei toks yra, nes gal jis BindModel metode, ne ka kitka gauna kaip RouteData reiksme. Na cia tik speliojimai, nes nematau tavo kodo. Siaip idomi beda ;)

Top 10 autorių
Vyras
21 Žinutės
Taškai 405

Sergejus:

Dar noriu įsiterpti į diskusiją, jeigu aš dar gerai atsimenu, tai tvarkoje trūksta dar vienos eilutės:

  1. Form
  2. RouteData
  3. QueryString
  4. Cookie

 Ne Cookie ten nėra. Čia ValueProviderDictionary išeities tekstas ir ten gali pasižiūrėt PopulateDictionary metodą. Ten tik trys šaltiniai.

Top 10 autorių
Vyras
21 Žinutės
Taškai 405

Domantas:

Tik va nesuprantu vieno, kaip gali RouteData reiksme uzsisetinti, jei tavo idetame kode yra metodas AddToDictionaryIfNotPresent, kuris uztikrina, kad su tokiu paciu key nebutu idetas rezultatas. Taigi darosi cia idomu. Jei formos key ir RouteData key sutampa, tai tik formos reiksme bus ideta i ValueProviderDictionary.

Mano kode, kurį įdėjau, į RouteData apskritai nežiūrima, nes konstruktoriuje iškviečiu this.Clear(), tai anuliuoju DefaultValueDictionary padarytą darbą. Paveldžiu iš jo tik tam, kad nereikėtu implementuoti paprastų IDictionary metodų ir pan.

Domantas:

Cia viskas atrodo įvyksta UpdateModel metode. Jis ne kas kita kaip sukuria ModelBindingContext ir kartu su Controllerio contextu paduoda i binderi. Siuo atveju lieka du variantai. Arba binderis yra tavo uzsetintas arba naudojamas DefaultModelBinder. Pasiziurek gal turi koki binderi susikures. Pasidebuggink jei toks yra, nes gal jis BindModel metode, ne ka kitka gauna kaip RouteData reiksme.

Bandžiau ir savo ir DefaultModelBinder, supratau tik, kad jie ten nieko dėti. DefaultModelBinder ima kontrolerio ValueProvider ir jį naudoja bindinant reikšmes, ir tiek žinių. Tik kad tos reikšmės imamos iš visur, tas man labiausiai ir užkliūva. Bent galėtų tas ValueProvider pažymėti kur nors iš kur jis tą reikšmę ima, bet jis to nedaro. Žodžiu nežinau, arba aš kažką praleidau (labai gali būti :) ) arba čia man panašu į iki galo neišgalvotą vietą tame SystemWebMvc.

Top 10 autorių
Vyras
41 Žinutės
Taškai 760

Na siaip as naudoju tipizuotus view'sus tai susidurti su situo nepavyko. Bandziau pasidaryti mini demo http://dotnetgroup.lt:8080/members/Domantas/files/UpdateModelMVC.zip.aspx, bet net neisivaizduoju kaip pas taves viskas veikia, kaip gaunama reiksme is Route. Jei dar vis idomu, tai gal ant to pacio pavyzduko parodyk savo pavyzdi :)

Top 10 autorių
Vyras
21 Žinutės
Taškai 405

Domantas:

Na siaip as naudoju tipizuotus view'sus tai susidurti su situo nepavyko. Bandziau pasidaryti mini demo http://dotnetgroup.lt:8080/members/Domantas/files/UpdateModelMVC.zip.aspx, bet net neisivaizduoju kaip pas taves viskas veikia, kaip gaunama reiksme is Route. Jei dar vis idomu, tai gal ant to pacio pavyzduko parodyk savo pavyzdi :)

No problem :) Pataisiau tavo pavyzdį, kad išryškėtų problema. Pataisiau 'Test' route'ą ir įdėjau į TestModel laukų, kurių nėra Test.aspx formoje. Id kaip defoltą uždėjau 99, jei palikčiau tuščią eilutę (kaip turėtų būti pagal nutylėjimą) UpdateModel net neįvyktų, išmestų InvalidOperationException (gali pabandyti). Postinant modelį Id laukas neturi būti liečiamas, bet jis yra liečiamas, kadangi jis yra kelyje. Toliau įdėjau dar tokią 'type' reikšmę. Tarkim model Type reikšmė gali reikšti vieną o kelyje tai gali reikšti visai kitą. Tačiau ši reikšmė bus priskirta modeliui, net neatsiklausius.

  • | Žinučių taškai: 20
Top 10 autorių
Vyras
41 Žinutės
Taškai 760

Cha ! pagaliau pilnai supratau tavo beda.  Jeigu modelyje yra papildomu properciu, kurie sutampa su RouteData, tai jie by default yra uzsetinami RouteData reiksmemis, nes tokiu lauku formoje nebuvo. Chmz :). Cia yra beda ir nera bedos. RouteData daugelyje mvc aplikaciju glaudziai risasi su redaguojamu modelio kontekstu ir tik vienetiniais atvejais (kaip pas tave) gali reiksti ir kita konteksta. Ir cia nera manau nera MVC bugas, tiesiog gal funkcionalumo trukumas. Reikia parasyti Phill'ui :D Tavo sprendimas man siek tiek tada nepatinka. Nepatinka del to, kad jis galbut veiks tik ant tavo aplikacijos. Klausimas ar visada postinant nori atsisakyti RouteData ir kitu MVC teikiamu malonumu ;) ? Ar visada postinant tau nereikes pasiimti reiksmes is RouteData arba get parametro. As siulyciau gal kitoki sprendima. As perrasiau DefaultModelBinder klases OnModelUpdating metoda. jame paprasciausiai pasiziurima ar norima isvengti RouteData reiksmiu ir jas pasalinam is bindingContext'o. Stai kaip atrodo si klase : http://codepaste.net/bjqq25 ir stai paprasciausias idetas propertis i custom Controller klase : http://codepaste.net/axz1et. na ir galiausiai panaudojimas - pries iskvieciant UpdateModel reikia uzsetinti HiderouteData = false; Visas sprendimas http://dotnetgroup.lt:8080/members/Domantas/files/UpdateModelMVC_5F00_2.zip.aspx 

P.S. Keletas komentaru : Sitokiu budu galima ijungti ir isjungti RouteData reiksmes. + cia galima zaisti kiek tik laikas leidzia. Galima pasidaryt custom atributa vietoj propercio, galima butu pasidaryti, kad butu galima nurodyti net kokius propercius ignoruoti, kokius palikti is RouteData. Tikiuosi tiks :) 

Top 10 autorių
Vyras
21 Žinutės
Taškai 405
Atsakyta (Patvirtinta) mipi3 atsakyta 08-26-2009 0:01
Verified by Domantas

Pagaliau susikalbėjom :) Aš blogai paaiškinau, nepaminėjau, kad ta problema iškyla neįvedus visų modelio laukų į formą.

RouteData daugelyje mvc aplikaciju glaudziai risasi su redaguojamu modelio kontekstu ir tik vienetiniais atvejais (kaip pas tave) gali reiksti ir kita konteksta


Nesutinku, kad vienetinis atvejis. Užtenka naujame asp.net mvc projekte sukurti modelį su Int Id properčiu. Ir padaryti jam Create(). Viskas gausi Exceptioną, kad stringas yra bindinamas ant Int. Tai yra paprasčiausia bėda. Bet čia vienas atvejis, bet gali būti ir kitų, nes aš nežinau kaip vystysis mano modeliai ir keliai laike, visko gali būti. Tai yra vieta, kur reikės būti atsargiam. Nes ją nelabai ir ištestuosi. Pvz situacija. Turiu modelį, kuris sėkmingai buvo Updeitinamas, ilgą laiką, be problemų, visi laukai eina per Post. Per kelis metus aš kažką pataisau, gal kelius, gal dar ką. Viskas veikia toliau gerai. Tada aš nusprendžiu pervadinti kokį modelio propertį, pervadinu ir pamirštu pervadinti View formoje lauką. Ir tada tarkim sutapo mano naujai pervadintas propertis su kokia kelio reikšme. Gal praeis kelios dienos ar savaitės kol aš pastebėsiu klaidą. Žodžiu man nereikia per mandro ValueProviderio, man reikia paprasto ir saugaus ValueProvider'io. Ir man nesuprantama, kam toks reikalingas, tiksliau kodėl toks uždėtas pagal nutylėjimą?

Supratau tavo mintį su ModelBind, bet nelabai suprantu, kaip tai veiks jei sutaps ir Form key'us ir RouteData key'us? Pridėk šią eilutę Test.aspx

  Type : <input type="text" name="Type" />

ir kai apdeitinsi dabar Type reikšmė neišsisaugos, nes ji dubliuojasi ir RouteData ir Formoje. Kaip sakiau problema ir yra, kad neaišku iš kur paimta ta reikšmė, tai dar šiek tiek sudetingina užduotį.

Aš vis tik lieku prie savo sprendimo. Nors jis man ir netobulas (neveikia kai paupdeitintas Modelis gaunamas per parametrus :( , čia jau tikrai MVC problema nes nėra tos tikslios vietos, kur galima priskirt savo ValueProvideri kontroleriui). Beje patobulinau jį kiek galėjau, kad jis tiktų visiems. Dabar virš kontrolerio klasės ar metodo galima parašyti atributą pavadinau jį SmartValueProvider :D

  [SmartValueProvider(ValueSource.Form | ValueSource.QueryString | ValueSource.RouteData)] // mintis manau aiški :)

arba

  [SmartValueProvider(ValueSource.Form)]  // kaip mano atveju

Pavyzdys kaip tai atrodo. Taip pat išeities tekstas šio daikto, jei įdomu.

įdėjau naujesnį mūsų testinio projekto variantą.

Tai vistik aš taip ir nesupratau. Kodėl pagal nutylėjimą duomenys imami ir iš RouteData ir QueryString kai vyksta Post? Taip turi būti? Ar čia įžvelgia kas nors, apart manęs, hmmm... neintuityvų ir problematišką funkcionalumą :) ?

 

 

  • | Žinučių taškai: 70
Top 10 autorių
Vyras
41 Žinutės
Taškai 760

Jo problema aiski ir atrodo kad turim ir sprendima. Tavo pvz puikiai veikia ir man patinka, kad viskas sprendziasi per atributus. Kaip matai MVC labai lankstus. Taciau sita vieta dar neisdirbta iki galo.

mipi3:
Kodėl pagal nutylėjimą duomenys imami ir iš RouteData ir QueryString kai vyksta Post? Taip turi būti? Ar čia įžvelgia kas nors, apart manęs, hmmm... neintuityvų ir problematišką funkcionalumą :) ?

Na MVC kurejai tikriausiai yra sumaste, kad RouteData yra update formos dalis. Tarkim editinam irasa, tai i forma nereikia deti net id hidden fieldo ar panasiai. Bet kaip rasiai ir kaip issiaiskinom, kad routeData kontekstas gali nesutapti su formos kontekstu. Cia turetu buti galimybe to isvengti. Ta galimybe jau pasidarei :) (Aciu MVC fw lankstumui). Nzn galima pabandyti sita klausima iskelti ir i aukstesnius vandenis : http://stackoverflow.com/, arba Phil'ui Haack'ui. Cia gausi konkretu ir aisku atsakyma : padarysim, nedarysim, pasidarei pats ko dar nori :) .... Siulau panaudoti musu pavyzduka ( istrynus mano koda, nes jis nepasitvirtino ).  

Top 10 autorių
Vyras
21 Žinutės
Taškai 405

Buvau išklėlęs šį klausimą stackoverflow.com, jis štai čia. Bet nesulaukiau didelio dėmesio tenai.

Jau minėjau, kad tai nėra 100% sprendimas. Nes tas ValueProvider užsisetina po to kai užmapinami parametrai Action metodui. Tai yra OnActionExecuting nėra ideali vieta priskirti ValueProvider, o kitos geresnės vietos nerandu.

Gal tikrai susisiekt su Haack'u :)

  • | Žinučių taškai: 20
Top 10 autorių
Vyras
21 Žinutės
Taškai 405

Beje, ačiū už pagalba ;)

  • | Žinučių taškai: 5
Top 10 autorių
Vyras
41 Žinutės
Taškai 760

mipi3:
Gal tikrai susisiekt su Haack'u :)

Tai aisq bandyk :) parasyk kad cia lietuvos .NET groupso beda, tai gal teiksis atsakyti nors trumpai.

Puslapis 1 iš 1 (15) | RSS
DotNetGroup.LT, 2009
Sprendimas