Avatar billede jan_h Nybegynder
15. maj 2013 - 00:36 Der er 9 kommentarer og
1 løsning

Undgå duplikeret kode i en asp.net web form applikation

Hej

Jeg er ved at lave en CRUD web form applikation som har en index, create, update og delete .aspx side.

I min Page_Load metode på Read/Update/Delete har jeg gentagen kode som jeg ikke bryder mig om. Det er jo åndsvagt at skulle ændre kode 3 stedder hvis det er samme logik der skal bruges.

Det som koden bruges til er at tjekke om der findes data og hvilken slags data der allerede evt. måtte findes (indtil videre gemmes data i "stub" dvs i RAM, senere vil det være fil baseret).

Koden i Page_Load som pt. er copy pasted til både create,red og update er som flg:

protected void Page_Load(object sender, EventArgs e)
        {
            if(!hasAgents() && !hasScoundrels() )
            {
                ddlPersonType.Visible = false;
                ddlPersonList.Visible = false;
                lblInfo.Visible = true;
                lblInfo.Text = "no agents or scoundrels created";
                PanelAgent.Visible = false;
                PanelScoundrel.Visible = false;
            }
            else if(!hasScoundrels())
            {
                if(!Page.IsPostBack)
                {
                    ddlPersonType.DataSource = personTypes;
                    ddlPersonType.DataBind();
                    ddlPersonType.Visible = true;
                }
                Application["MyScoundrel"] = new ArrayList();
                ListItem li = ddlPersonType.Items.FindByValue("Scoundrel");
                if(li != null) ddlPersonType.Items.Remove(li);
            } 
            else if(!hasAgents())
            {
                if(!Page.IsPostBack)
                {   
                    ddlPersonType.DataSource = personTypes;
                    ddlPersonType.DataBind();
                    ddlPersonType.Visible = true;
                }
                Application["MyAgent"] = new ArrayList();
                ListItem li = ddlPersonType.Items.FindByValue("Agent");
                if (li != null) ddlPersonType.Items.Remove(li);
            }
            else if(agentArrayList.Count > 0 && scoundrelArrayList.Count > 0)
            {
                ddlPersonType.DataSource = personTypes;
                ddlPersonType.DataBind();
                ddlPersonType.Visible = true;
            }


    private bool hasAgents()
        {
            if (Application["MyAgent"] as ArrayList == null || (Application["MyAgent"] as ArrayList).Count == 0)
            {
                //no agents in the list, hide controls and show msg
                lblInfo.Text = "no agents created";
                lblInfo.Visible = true;
                PanelAgent.Visible = false;
                PanelScoundrel.Visible = false;
                return false;
            }
            return true;           
        }


        private bool hasScoundrels()
        {
            if (Application["MyScoundrel"] as ArrayList == null || (Application["MyScoundrel"] as ArrayList).Count == 0)
            {
                //no scoundrels in the list, hide controls and show msg
                lblInfo.Text = "no scoundrels created";
                lblInfo.Visible = true;
                PanelAgent.Visible = false;
                PanelScoundrel.Visible = false;
                return false;
            }
            return true;
        }


Bemærk venligst.. at min Page_Load kalder 2 metoder hasAgents() og hasScoundrels(). Og at disse 2 metoder ændrer properties på web controls (panels , label , dropdownlist). Denne kobling gør det vel meget sværere at flytte koden ud i en ny selvstændig klasse uden at programmet "mister sin funktionalitet". ?
Avatar billede platik Nybegynder
15. maj 2013 - 08:33 #1
3 muligheder er:

1. Usercontrol - Flyt det kode og de kontroller der går igen. Og lig usercontrollen på siderne.

2. Lav en fælles page de kan nedarve fra. Udvid så dine metoder så de tager de kontroller de skal bruge med ind som paremetre.

Noget ligende:

protected bool hasScoundrels(Label lblInfo, Panel PanelAgent, Panel PanelScoundrel)
{
  //Do stuff
}

3. Lav en klasse til at håndtere metoderne ud fra samme princip som ved nedarvningen. Hvor metoden tager controls som parametre.

Personligt ville jeg nok foretrække usercontrol hvis dette er muligt. Da dine metoder og controls har en naturlig tæt kobling. Omvent kan det også hurtigt blive bøvlet hvis de ikke altid skal ligge ens på siderne og så er en af de andre måder måske en bedre løsning.
Avatar billede jan_h Nybegynder
15. maj 2013 - 08:40 #2
Hej Plastik. Forstår ikke helt forskellen mellem mulighed 2 og 3. Kan du uddybe det lidt?
Avatar billede softspot Forsker
15. maj 2013 - 09:29 #3
Lidt af det samme som platik er inde på, men hvor du implementerer et interface som udstiller alle de relevante kontroller fra dine forms og så lægger din kode over i en klasse, der tager et interface (som dit Page-objekt implementerer). Det kunne tage sig nogenlunde således ud:

public interface IAgentsAndScoundrels
{
  DropDownList ddlPersonType { get; }
  DropDownList ddlPersonList { get; }

  Label lblInfo { get; }

  Panel PanelAgent { get; }
  Panel PanelScoundrel { get; }

  bool IsPostBack { get; }
}


Dine forms kunne så implementere dette interface:

partial class AgentsAndScoundrelsRead : Page, IAgentsAndScoundrels
{
  public DropDownList ddlPersonType { get { return ddlPersonType; } }
  public DropDownList ddlPersonList { get { return ddlPersonList; } }
  public Label lblInfo { get { return lblInfo; } }
  public Panel PanelAgent { get { return PanelAgent; } }
  public Panel PanelScoundrel { get { return PanelScoundrel; } }
  public bool IsPostBack { get { return Page.IsPostBack; } }

  protected void Page_Load(object sender, EventArgs e)
  {
    var viewModel = new AgentsAndScoundrelsViewModel(this);
    viewModel.HandleLoad();
  }
}


Din kode ville så ligge i ViewModel-klassen og se nogenlunde således ud:

public class AgentsAndScoundrelsViewModel
{
  private IAgentsAndScoundrels view;
  public AgentsAndScoundrelsViewModel(IAgentsAndScoundrels view)
  {
    this.view = view;
  }

  public void HandleLoad()
  {
    if(!hasAgents() && !hasScoundrels() )
    {
      view.ddlPersonType.Visible = false;
      view.ddlPersonList.Visible = false;
      view.lblInfo.Visible = true;
      view.lblInfo.Text = "no agents or scoundrels created";
      view.PanelAgent.Visible = false;
      view.PanelScoundrel.Visible = false;
    }
    else if(!hasScoundrels())
    {
      if(!view.IsPostBack)
      {
        view.ddlPersonType.DataSource = personTypes;
        view.ddlPersonType.DataBind();
        view.ddlPersonType.Visible = true;
      }
      Application["MyScoundrel"] = new ArrayList();
      ListItem li = view.ddlPersonType.Items.FindByValue("Scoundrel");
      if(li != null) view.ddlPersonType.Items.Remove(li);
    } 
    else if(!hasAgents())
    {
      if(!view.IsPostBack)
      {   
        view.ddlPersonType.DataSource = personTypes;
        view.ddlPersonType.DataBind();
        view.ddlPersonType.Visible = true;
      }
      Application["MyAgent"] = new ArrayList();
      ListItem li = view.ddlPersonType.Items.FindByValue("Agent");
      if (li != null) view.ddlPersonType.Items.Remove(li);
    }
    else if(agentArrayList.Count > 0 && scoundrelArrayList.Count > 0)
    {
      view.ddlPersonType.DataSource = personTypes;
      view.ddlPersonType.DataBind();
      view.ddlPersonType.Visible = true;
    }
  }

  private bool hasAgents()
  {
    if (Application["MyAgent"] as ArrayList == null || (Application["MyAgent"] as ArrayList).Count == 0)
    {
      //no agents in the list, hide controls and show msg
      view.lblInfo.Text = "no agents created";
      view.lblInfo.Visible = true;
      view.PanelAgent.Visible = false;
      view.PanelScoundrel.Visible = false;
      return false;
    }
    return true;           
  }

  private bool hasScoundrels()
  {
    if (Application["MyScoundrel"] as ArrayList == null || (Application["MyScoundrel"] as ArrayList).Count == 0)
    {
      //no scoundrels in the list, hide controls and show msg
      view.lblInfo.Text = "no scoundrels created";
      view.lblInfo.Visible = true;
      view.PanelAgent.Visible = false;
      view.PanelScoundrel.Visible = false;
      return false;
    }
    return true;
  }
}

Dermed har du isoleret din kode og du har gjort den lidt lettere at teste, da du kan bedre kan "mocke" dine forms, selvom der stadig er en ret stærk afhængighed af servercontrols i dit interface... men det kan der jo arbejdes videre med :-)
Avatar billede platik Nybegynder
15. maj 2013 - 09:27 #4
Jeg kan prøve :-). Lidt kode eksempler:

2. Nedarvning.

public partial AgentsScoundrelsPage : Page
{
    protected void Load(Label lblInfo, Panel PanelAgent, Panel PanelScoundrel, DropDownList ddlPersonType)
    {
      //indsæt kode for loaded fra din page.
    }

    //lav andre private metoder?

}

Dine pages

public partial MyPage : AgentsScoundrelsPage
{
    protected void Page_Load(object sender, EventArgs e)
    {
        base.Load(lblInfo, PanelAgent, PanelScoundrel, ddlPersonType);
    }

    //indsæt evt. unikke metoder for denne side
}

3. Klasse (Helper)

public class AgentsScoundrelsHelper
{
    public (static) void Load(Label lblInfo, Panel PanelAgent, Panel PanelScoundrel, DropDownList ddlPersonType)
    {
      //indsæt kode for loaded fra din page.
    }

    //lav andre private metoder?
}

Dine pages

public partial MyPage : Page
{
    protected void Page_Load(object sender, EventArgs e)
    {
        //Lav instans hvis du beslutter dig for metoden ikke skal være static.
        AgentsScoundrelsHelper.Load(lblInfo, PanelAgent, PanelScoundrel, ddlPersonType);
    }

    //indsæt evt. unikke metoder for denne side
}

Det er derfor et spørgsmål og du ser det som en arveart at siderne er ens eller blot en hjælp siderne kan kalde for at udføre noget logik.

Der er fordel ulemper ved dem begge. Sig til hvis jeg skal uddybe yderligere :-)
Avatar billede jan_h Nybegynder
17. maj 2013 - 21:55 #5
Hej igen. Sorry for den sene tilbagemelding.
@softspot: Jeg prøvede at følge dit eksempel, men den kender ikke Application deklarationen ("The name 'Application' does not exist in the current context").

Kan ikke se hvilket library jeg mangler at importere via using.
Måske jeg skal arve Page via class deklarationen?
Avatar billede jan_h Nybegynder
17. maj 2013 - 21:57 #6
Ja nevermind, det skulle jeg nok have afprøvet før jeg spurgte jer ad. Compileren accepterede Application deklarationen så snart jeg extendede class via : System.Web.UI.Page

@softspot: Jeg skal lige se om jeg kan få dit koncept til at virke så melder jeg tilbage.
Avatar billede softspot Forsker
18. maj 2013 - 01:47 #7
Jeg vil tro du skal inkludere System.Web for at få adgang til Application...
Avatar billede softspot Forsker
21. maj 2013 - 13:52 #8
Du kan nok også undlade nedarvning fra Page og blot referere Application igennem HttpContext.Current.Application. Du kan evt. erklære et felt på klassen og blot initiere dette i constructor. Noget i stil med:

  private HttpAppliationState Application;
  private IAgentsAndScoundrels view;
  public AgentsAndScoundrelsViewModel(IAgentsAndScoundrels view)
  {
    this.view = view;
    this.Application = HttpContext.Current.Application;
  }

Herefter burde du kunne referere Application som i det oprindelige eksempel jeg viste...
Avatar billede jan_h Nybegynder
02. december 2013 - 13:52 #9
skriv et svar hvis i vil have point nogle af jer. Kom aldrig rigtig videre med det her projekt, beklager den sene tilbagemelding.
Avatar billede softspot Forsker
02. december 2013 - 15:10 #10
:-)
Avatar billede Ny bruger Nybegynder

Din løsning...

Tilladte BB-code-tags: [b]fed[/b] [i]kursiv[/i] [u]understreget[/u] Web- og emailadresser omdannes automatisk til links. Der sættes "nofollow" på alle links.

Loading billede Opret Preview
Kategori
Kurser inden for grundlæggende programmering

Log ind eller opret profil

Hov!

For at kunne deltage på Computerworld Eksperten skal du være logget ind.

Det er heldigvis nemt at oprette en bruger: Det tager to minutter og du kan vælge at bruge enten e-mail, Facebook eller Google som login.

Du kan også logge ind via nedenstående tjenester