Check alle échte Black Friday-deals Ook zo moe van nepaanbiedingen? Wij laten alleen échte deals zien

[C#/MVC4] Ingewikkeld model&view probleem

Pagina: 1
Acties:

  • F.West98
  • Registratie: Juni 2009
  • Laatst online: 23-11 13:04

F.West98

Alweer 16 jaar hier

Topicstarter
Hallo,

Het is lastig om dit probleem in woorden te omvatten, in 1 zin of enkele trefwoorden in ieder geval. Het kan zijn dat ik verkeerd denk, verbeter me dan :)
Laat ik bij het begin beginnen. Ik heb 2 classes Licenties die inheriten van base class Licentie:
Licenties_Versie1 : Licentie
Licenties_Versie2 : Licentie

In mijn code heb ik een variabele licentie waarvan ik niet weet welke Licentiesoort het is, enkel dat het een Licentie is (kan ook nog een Versie3, 4, 5, enz zijn). Omdat het stiekem toch wel een _Versie is, werkt dit gewoon:
C#:
1
2
3
4
if(Licentie licentie is Licenties_Versie2) {
    var licentie2 = licentie as Licenties.Versie2;
    return licentie2.versie2waarde;
}


Als ik gewoon alle waardes wil weergeven, zonder specifiek voor elke versie code te schrijven werkt dit:
C#:
1
2
3
4
var props = licentie.GetType().GetProperties();
foreach(var prop in props) {
    toreturn += prop.Name + prop.GetValue(licentie, null).ToString();
}

Geen probleem, ik krijg ook alle waardes van eventuele subversies (dus ik zou versie2waarde ook gewoon krijgen).

So far so good, nu komt het probleem.
Ik zit in een loop van alle eigenschappen (bij de toreturn += dus), en sommige props hebben bij DisplayAttribute de GroupName hidden, die worden ook mooi verborgen.
Sommige items hebben de GroupName editable, die moeten kunnen worden bewerkt en daarvoor moet ik een mooie input maken.

Hoe ga ik dit aanpakken, ik zit nu in een view met model van type Licentie?

Ik kan geen @Html.Editorfor(model => model.......) doen, want model is nog steeds van type Licentie, en die is read-only en dus niet aan te passen naar een andere soort. Aan de andere kant, als dit wel zou lukken, zou ik dit aan de verwerk-kant ook niet goed werkend kunnen krijgen.

Verder heb ik eigenlijk geen idee, en ook niet waarop ik moet zoeken. Jullie wel?

Ja het is laat :O

2x Dell UP2716D | R9 7950X | 128GB RAM | 980 Pro 2TB x2 | RTX2070 Super
.oisyn: Windows is net zo slecht in commandline als Linux in GUI


  • hell4you
  • Registratie: Mei 2006
  • Laatst online: 00:03
Waarom heb je uberhaupt de base class Licentie als je er geen gebruik van maakt? De member "waarde" lijkt me juist prima om in de base class te laten. Dan hoef je ook niet al die "vieze" reflection dingen te doen.

C#:
1
2
3
4
5
6
7
8
9
10
11
12
class Licentie
{
    public int Waarde { get; protected set; }
}

class Licentie1 : Licentie
{
    public Licentie1()
    {
        this.Waarde = 1;
    }
}


Ik heb de indruk dat je ontzettend moeilijk denkt, maar wellicht begrijp ik niet goed wat je wilt bereiken. Probeer eens een stapje terug te zetten en uit te leggen wat eigenlijk je doel is.

  • ThomasG
  • Registratie: Juni 2006
  • Laatst online: 18:54
Volgens mij snap jij niet wat je zelf nou eigelijk aan het doen ben. Het zier er naar uit dat jij het principe van inheritance niet helemaal snapt. Jij komt nu met een stukje relfectie aanzetten op je probleem op te lossen, terwijl dat zeer waarschijnlijk helemaal niet nodig is in deze situatie. Je kent je types namelijk al tijdens compile-time, en doet er geen fancy dingen mee. Maar ik mis een stukje context omschrijving. Hoe ziet je Licentie klasse eruit, en wat wil je nou precies bereiken?

  • Haan
  • Registratie: Februari 2004
  • Laatst online: 16:52

Haan

dotnetter

Als ik je verhaal goed begrijp, ben je opzoek naar een interface. In die interface definieer je de eigenschappen die iedere specifieke licentie moet bezitten.
Opzich is een licentie base class zoals hell4you als voorbeeld geeft ongeveer hetzelfde, maar zolang er verder geen gedeelde functionaliteit in die base class zit, zou ik voor de interface gaan. Je mag namelijk zoveel interfaces implementeren als je wil, maar maar één class extenden.

[ Voor 47% gewijzigd door Haan op 09-06-2013 10:34 ]

Kater? Eerst water, de rest komt later


  • F.West98
  • Registratie: Juni 2009
  • Laatst online: 23-11 13:04

F.West98

Alweer 16 jaar hier

Topicstarter
De base class Licentie is als volgt, er zitten wel gedeelde eigenschappen in:
C#:
1
2
3
4
5
class Licentie {
    public int LicentieID { get; set; }
    public virtual Product product { get; set; }
    public virtual User user { get; set; }
}


Die zijn dus voor elke licentiesoort hetzelfde.


Waarschijnlijk denk ik idd verkeerd of te moeilijk, maar ik wil het volgende.
Wat al werkt
Ik wil verschillende soorten Licenties kunnen maken, die allemaal andere eigenschappen hebben. De ene soort heeft username + password, andere heeft enkel licentiecode, en zo kunnen er nog meer vormen komen.
Wel hebben ze allemaal gedeelde eigenschappen, namelijk ze horen bij een Persoon en een Product en hebben een id.

Daarom dacht ik dat ik een base class Licentie maak met de gedeelde eigenschappen, en die voor elke soort Licentie extend. Dit kan je ook gewoon opslaan in de DB via EF, want het volgende werkt:
C#:
1
2
3
4
5
6
Licentie licentie = new Licentie_Versie1 {
    product = product,
    eigenschap1 = "eigenschap"
};
db.Licenties.Add(licentie);
db.SaveChanges();

Als ik de licentie van type Licentie_Versie1 maak, geeft EF een error, want de tabel is van type Licentie.
In de tabel gaat het wel goed, want EF heeft keurig kolommen gemaakt voor alle eigenschappen van alle derived classes, en een kolom DIscriminator welke aangeeft welk type het eigenlijk is.

Als ik een entry terughaal, krijg ik echter gewoon een Licentie terug, maar als je in debugmode kijkt, staan de _Versie1-specifieke eigenschappen wel in de class, maar niet toegankelijk. Je kan dan wel dit doen:
C#:
1
var licentie2 = licentie1 as Licentie_Versie1;

Dan krijg je ook de oorspronkelijke waarden terug.

Aangezien je in de View nooit weet welk soort je 'eigenlijk' hebt, (Versie 1, 2, 3, enz), en niet steeds voor elke soort (if licentie is ...) andere code wilt maken, los ik het weergeven op met de bovenstaande loop.

Geavanceerder
Nu wil ik sommige eigenschappen, zoals LicentieID, niet weergeven, en anderen juist editable maken. Ik los dit (waarschijnlijk verkeerd) op door een Display attribuut mee te geven met GroupName:
C#:
1
2
3
4
5
6
7
8
9
class Licentie {
    [Display(GroupName = "hidden")]
    public int LicentieID { get; set; }
}
class Licentie_Versie1 : Licentie {
    public string username { get; set; }
    [Display(GroupName = "user_editable")]
    public string password { get; set; }
}


Het verbergen, niet tonen, van hidden items lukt al, door een if-statement te doen waarin wordt gekeken of de groupname "hidden" is, en dan een continue; te doen, zodat hij naar de volgende gaat.

Wat ik nu wil, is de user_editable velden ook daadwerkelijk editable maken. Daar heb je een Editor voor nodig, maar daar gaat het mis. Hoe krijg ik die werkend (met de juiste meldingen, zodat als er een RequiredAttr geset is, daar ook op wordt gecontroleerd), en hoe ga ik dat server-side afhandelen?
Daar zit ik een beetje mee te kloten.

2x Dell UP2716D | R9 7950X | 128GB RAM | 980 Pro 2TB x2 | RTX2070 Super
.oisyn: Windows is net zo slecht in commandline als Linux in GUI


  • alwinuzz
  • Registratie: April 2008
  • Laatst online: 22-11 19:13
Misschien wordt het probleem beter te behappen als je een aparte ViewModel class maakt, los van je database model. Je moet wat extra code schrijven om van viewmodel <--> database model te gaan maar dan krijg je wel duidelijke code. Je database model blijft dan simpel, en je view ook.

  • Zeebonk
  • Registratie: Augustus 2005
  • Laatst online: 30-07 20:50
Je hangt te veel View en Controller gerelateerde zaken aan de database Models. Hoe ik dit zou oplossen:
  1. In de database models alleen de entiteiten, de eigenschappen van de entiteiten, de constraints op de eigenschappen van de entiteitein en de relaties vastleggen. Dus geen Display() achtige informatie.
  2. Voor elk type Licentie een eigen edit Action, View en ViewModel. De ViewModel exposed de eigenschappen van de Licentie die jij wilt en bevat de meta data die jij wilt (Display() etc) en voorkomt Mass Assignment
  3. Je zult vast een Action hebben waarin je een lijst van alle Licenties met edit knop laat zien, ongeacht het type Licentie. Omdat je niet in je view de logica wilt hebben zitten welke op basis van het Licentie type een url maakt naar de bijbehorende Edit Action, defineer je één generieke edit action die voor alle Licentie types kan worden aangeroepen. Deze generieke action roept dan op basis van het Licentie type de juist specifieke Edit Action aan.

  • F.West98
  • Registratie: Juni 2009
  • Laatst online: 23-11 13:04

F.West98

Alweer 16 jaar hier

Topicstarter
alwinuzz schreef op zondag 09 juni 2013 @ 15:52:
Misschien wordt het probleem beter te behappen als je een aparte ViewModel class maakt, los van je database model. Je moet wat extra code schrijven om van viewmodel <--> database model te gaan maar dan krijg je wel duidelijke code. Je database model blijft dan simpel, en je view ook.
Moet ik dan ook voor elk niuew soort Licentiemodel een nieuw Model maken, en zo ja, hoe ga ik dat dus weer in mijn Views zetten?
Zeebonk schreef op zondag 09 juni 2013 @ 16:13:
Je hangt te veel View en Controller gerelateerde zaken aan de database Models. Hoe ik dit zou oplossen:

• In de database models alleen de entiteiten, de eigenschappen van de entiteiten, de constraints op de eigenschappen van de entiteitein en de relaties vastleggen. Dus geen Display() achtige informatie.
• Voor elk type Licentie een eigen edit Action, View en ViewModel. De ViewModel exposed de eigenschappen van de Licentie die jij wilt en bevat de meta data die jij wilt (Display() etc) en voorkomt Mass Assignment
Dat is dus heel veel code om een licentiesoort toe te voegen, terwijl ik dit systeem zo simpel mogelijk qua onderhoud probeer te maken. Het kan idd wel, maar het lijkt mij erg omslachtig.
• Je zult vast een Action hebben waarin je een lijst van alle Licenties met edit knop laat zien, ongeacht het type Licentie. Omdat je niet in je view de logica wilt hebben zitten welke op basis van het Licentie type een url maakt naar de bijbehorende Edit Action, defineer je één generieke edit action die voor alle Licentie types kan worden aangeroepen. Deze generieke action roept dan op basis van het Licentie type de juist specifieke Edit Action aan.
Die heb ik (nog) niet ;)

edit:
Als ik het volgende doe krijg ik een foutmelding, maar mij lijkt dit wel een oplossing op de huidige manier:
C#:
1
@Html.EditorFor(model => model.GetType().GetProperty(prop.Name))

code:
1
Templates can be used only with field access, property access, single-dimension array index, or single-parameter custom indexer expressions.

[ Voor 8% gewijzigd door F.West98 op 09-06-2013 16:39 ]

2x Dell UP2716D | R9 7950X | 128GB RAM | 980 Pro 2TB x2 | RTX2070 Super
.oisyn: Windows is net zo slecht in commandline als Linux in GUI


  • ThomasG
  • Registratie: Juni 2006
  • Laatst online: 18:54
F.West98 schreef op zondag 09 juni 2013 @ 16:31:
Dat is dus heel veel code om een licentiesoort toe te voegen, terwijl ik dit systeem zo simpel mogelijk qua onderhoud probeer te maken. Het kan idd wel, maar het lijkt mij erg omslachtig.
Je kunt met Partial Views gaan werken, waarbij je standaard elementen gebruikt. Je hebt bijvoorbeeld een licentie type welke een gebruikersnaam en wachtwoord nodig heeft. En je hebt er een die een URL nodig heeft. En nog een die een gebruikersnaam en wachtwoord, en URL nodig heeft. En misschien eentje die alleen een licentiecode heeft. En daar weer combinaties op. Dat zijn dan verschillende licentie type. Het lijkt mij dat je zoiets gebruikt. Ik zou ze dan zelf geen namen geven als Licentie_Versie1, maar het beestje bij zijn naam noemen. Maar goed, dat is een persoonlijke keuze.

Om gemakkelijk te kunnen zien welke extra eigenschappen de desbetreffende licentie heeft, kun je gebruik maken van interfaces. Zo heb je een interface voor gebruikersnaam en wachtwoord. Een interface voor URL, etc. Zo kun je heel gemakkelijk aan de hand van Partial Views deze elementen toevoegen aan je view. Zo krijg je een generieke view, die zich aanpast aan de 'wensen' van de licentie. Mocht je ooit een nieuw element krijgen, die je nog niet had, en niet in een andere kunt stoppen. Kun je dat er redelijk eenvoudig bij maken.

  • F.West98
  • Registratie: Juni 2009
  • Laatst online: 23-11 13:04

F.West98

Alweer 16 jaar hier

Topicstarter
ThomasG schreef op zondag 09 juni 2013 @ 16:44:
[...]
Je kunt met Partial Views gaan werken, waarbij je standaard elementen gebruikt. Je hebt bijvoorbeeld een licentie type welke een gebruikersnaam en wachtwoord nodig heeft. En je hebt er een die een URL nodig heeft. En nog een die een gebruikersnaam en wachtwoord, en URL nodig heeft. En misschien eentje die alleen een licentiecode heeft. En daar weer combinaties op. Dat zijn dan verschillende licentie type. Het lijkt mij dat je zoiets gebruikt. Ik zou ze dan zelf geen namen geven als Licentie_Versie1, maar het beestje bij zijn naam noemen. Maar goed, dat is een persoonlijke keuze.

Om gemakkelijk te kunnen zien welke extra eigenschappen de desbetreffende licentie heeft, kun je gebruik maken van interfaces. Zo heb je een interface voor gebruikersnaam en wachtwoord. Een interface voor URL, etc. Zo kun je heel gemakkelijk aan de hand van Partial Views deze elementen toevoegen aan je view. Zo krijg je een generieke view, die zich aanpast aan de 'wensen' van de licentie. Mocht je ooit een nieuw element krijgen, die je nog niet had, en niet in een andere kunt stoppen. Kun je dat er redelijk eenvoudig bij maken.
Erg slim bedacht! Zelf niet op gekomen.
Ik snap nog niet helemaal het doel met die interfaces. Klopt het als ik zeg dat het een soort partial views zijn maar dan van classes, die je in een licentiesoort kan zetten?
Dus zoiets:
C#:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
interface userpass {
    string username { get; set; }
    string password { get; set; }
}
interface licentiekey {
    string licentiekey { get; set; }
}

class Licentie {
    public int LicentieID { get; set; }
    public virtual User user { get; set; }
    /****/
}
class Licentie_Versie1 : Licentie, userpass, licentiekey { }


De _Versie1 is fictief ;)
Maar hoe zorg je dan dat hij adhv de interfaces de juiste Partial Views gebruikt?

edit:
Ik zie dat je in de derived class alsnog de eigenschappen moet setten. Wat is dan het voordeel van de interfaces?

edit2:
Ah, een interface bevat de eisen van een class. Dat verduidelijkt het. Maarwat is het voordeel van het gebruik ervan dan? (in dit geval) Is het dat je dan dus kan zeggen over een class dat die wel username+password MOET hebben omdat die interface erin zit, en een licentiekey omdat....., enz?

En kan ik dan ook gewoon dit doen:
C#:
1
2
3
4
var interfaces = licentie.GetType().GetInterfaces();
foreach(var interface in interface)
  RenderPartial(interface.Name);
}

[ Voor 13% gewijzigd door F.West98 op 09-06-2013 17:32 ]

2x Dell UP2716D | R9 7950X | 128GB RAM | 980 Pro 2TB x2 | RTX2070 Super
.oisyn: Windows is net zo slecht in commandline als Linux in GUI


  • Megamind
  • Registratie: Augustus 2002
  • Laatst online: 10-09 22:45
Ik denk dat een interface hier niet geschikt voor is, je defineert dan alleen de signature en niet de body. Als je dat wilt moet je een abstracte klasse maken? Dan moeten alle classes een aantal methodes implementeren.

  • kaesve
  • Registratie: Maart 2009
  • Laatst online: 16-05 03:04
Misschien wil je eens kijken naar het decorator pattern. Hiermee kan je functionaliteit modulair uitbreiden, wat volgens mij is wat je probeert?

  • F.West98
  • Registratie: Juni 2009
  • Laatst online: 23-11 13:04

F.West98

Alweer 16 jaar hier

Topicstarter
kaesve schreef op zondag 09 juni 2013 @ 17:36:
Misschien wil je eens kijken naar het decorator pattern. Hiermee kan je functionaliteit modulair uitbreiden, wat volgens mij is wat je probeert?
Dat is idd ongeveer wat ik wil, leuk leesvoer :)
Megamind schreef op zondag 09 juni 2013 @ 17:31:
Ik denk dat een interface hier niet geschikt voor is, je defineert dan alleen de signature en niet de body. Als je dat wilt moet je een abstracte klasse maken? Dan moeten alle classes een aantal methodes implementeren.
Volgens mij is een abstract class hier ook beter. Je kan direct [Display] instellen en je hoeft niet alles opnieuw te implementeren, terwijl je dat wel bij specifieke dingen aan kan geven...

edit: Ik ga het proberen
edit2: Ik kan niet inheriten uit een base class EN een abstract class. Hoe ga ik dat oplossen in dit geval?

[ Voor 6% gewijzigd door F.West98 op 09-06-2013 17:47 ]

2x Dell UP2716D | R9 7950X | 128GB RAM | 980 Pro 2TB x2 | RTX2070 Super
.oisyn: Windows is net zo slecht in commandline als Linux in GUI


  • Megamind
  • Registratie: Augustus 2002
  • Laatst online: 10-09 22:45
Geen abstracte klasse gebruiken maar een normale :P

  • F.West98
  • Registratie: Juni 2009
  • Laatst online: 23-11 13:04

F.West98

Alweer 16 jaar hier

Topicstarter
Een interface lijkt toch wel een oplossing, minder handig dan abstract classes, maar goed genoeg :*)

En het werkt helemaal goed, heel erg bedankt allemaal!

[ Voor 22% gewijzigd door F.West98 op 09-06-2013 18:09 ]

2x Dell UP2716D | R9 7950X | 128GB RAM | 980 Pro 2TB x2 | RTX2070 Super
.oisyn: Windows is net zo slecht in commandline als Linux in GUI


  • RobIII
  • Registratie: December 2001
  • Niet online

RobIII

Admin Devschuur®

^ Romeinse Ⅲ ja!

(overleden)
F.West98 schreef op zondag 09 juni 2013 @ 18:05:
minder handig dan abstract classes, maar goed genoeg :*)
Dat is hetzelfde als zeggen dat een vork minder handig is dan een lepel. Dat is helemaal correct als je 't hebt over 't eten van soep, maar als je probeert een stuk vlees vast te houden tijdens 't snijden is een lepel toch ook verdomd lastig.

M.a.w: ieder heeft z'n doel; leer 't onderscheid en vergelijk dan geen vorken met lepels meer ;)

There are only two hard problems in distributed systems: 2. Exactly-once delivery 1. Guaranteed order of messages 2. Exactly-once delivery.

Je eigen tweaker.me redirect

Over mij

Pagina: 1