Avatar billede simsen Mester
21. november 2011 - 09:26 Der er 13 kommentarer og
1 løsning

Interface forståelses problem

Hej,

Jeg har altså problemer med at forstå interface og brugen af denne. Jeg forstår godt at man laver metoder og så tilgår dem.....Mit problem hænger sammen med variabler. Her et eksempel:

I min DAL metoder, har jeg følgende metoder (og dem ved jeg fungerer perfekt)

#region " Enums, Members, Properties "

        DBUtility DAL = new DBUtility();

        public string employeeId { get; set; }
        public string employeeName { get; set; }
        public string employeeGroupId { get; set; }
        public string password { get; set; }
        public bool isAdmin { get; set; }
        public bool isActive { get; set; }

        #endregion

        #region " Data Access Get "

        public DataTable GetEmployee(string employeeId)
        {
            DAL.AddParameter("employeeId", employeeId);

            DataTable getData = DAL.GetDataTable("GetEmployee", CommandType.StoredProcedure);

            employeeName = string.Empty;
            employeeGroupId = string.Empty;
            password = string.Empty;
            isAdmin = false;
            isActive = false;

            if (getData.Rows.Count > 0)
            {
                employeeId = Convert.ToString(getData.Rows[0]["employeeId"]);
                employeeName = Convert.ToString(getData.Rows[0]["employeeName"]);
                employeeGroupId = Convert.ToString(getData.Rows[0]["employeeGroupId"]);
                password = Convert.ToString(getData.Rows[0]["password"]);
                isAdmin = Convert.ToBoolean(getData.Rows[0]["isAdmin"]);
                isActive = Convert.ToBoolean(getData.Rows[0]["isActive"]);
            }

            return getData;
        }
        public bool GetEmployeeIsRegistered(string employeeId, string password, out bool isAdmin, out bool isActive)
        {
            bool isRegistered = false;
           
            DAL.AddParameter("employeeId", employeeId);
            DAL.AddParameter("password", password);

            DataTable getData = DAL.GetDataTable("GetEmployeeIsRegistered", CommandType.StoredProcedure);

            isAdmin = false;
            isActive = false;

            if (getData.Rows.Count > 0)
            {
                employeeId = Convert.ToString(getData.Rows[0]["employeeId"]);
                password = Convert.ToString(getData.Rows[0]["password"]);
                isAdmin = Convert.ToBoolean(getData.Rows[0]["isAdmin"]);
                isActive = Convert.ToBoolean(getData.Rows[0]["isActive"]);

                isRegistered = true;
            }

            return isRegistered;
        }

        #endregion

Som I kan se, så når jeg f.eks. har min metode GetEmployee(string employeeId)så smider jeg værdierne jeg henter ind i en datatable ud i public variabler.

Hvis jeg nu var på en "normal" program, ville jeg så hente værdierne ud ved at tilgå metoden (klassen) og så hente værdierne ud ved at skrive:

Employees employees = new Employees();
string test = employees.employeeName;

Det samme vil jeg frygtelig gerne opnå når jeg tilgår Interfacet, men aner ikke hvordan og hvorledes.

Mit interface ser ud på følgende måde lige nu;

public interface IEmployeeService
    {
        string employeeId { get; set; }
        string employeeName { get; set; }
        string employeeGroupId { get; set; }
        string password { get; set; }
        bool isAdmin { get; set; }
        bool isActive { get; set; }

        DataTable GetEmployee(string employeeId);

        bool GetEmployeeIsRegistered(string employeeId, string password, out bool isAdmin, out bool isActive);
    }

Hvordan får jeg hentet værdierne (jeg henter i GetEmployee ud i de public variabler i Interfacet, så jeg kan tilgå dem fra koden af?
Avatar billede Syska Mester
21. november 2011 - 09:36 #1
public class Employees : IEmployeeService
{
// Her skal alt implementeres
}

IEmployeeService employees = new Employees();
string test = employees.employeeName;

Nu bruger du din klasse via dit interface. Et interface er bare en kontrakt som en klasse skal overholde. Du kan ikke oprette en instance af et interface.
Avatar billede simsen Mester
21. november 2011 - 09:53 #2
Hmmmmmm nu gør du mig rundphorvirret *griner*

Sådan som jeg tilgår (og har fået at vide af Arne) er på følgende måde:

IEmployeeService employees = (IEmployeeService)Activator.GetObject(typeof(IEmployeeService), "tcp://127.0.0.1:50000" + "/Employees");

employees.GetEmployee(txtEmployeeId.Text);

Hvor er det jeg i stedet for at tilgå IEmployeeService kan tilgå Employees?
Avatar billede simsen Mester
21. november 2011 - 09:55 #3
Altså det jeg tænker på er at på klienten kender jeg jo kun til mit Interface og ikke Employee (og sådan jeg ønsker det, så jeg ikke skal opdatere to steder, hvis der er en fejl i koden).
Avatar billede Syska Mester
21. november 2011 - 10:08 #4
Pas ... nu kom der en masse andet information med som jeg ikke har arebjdet med.

Hvordan ser din service ud? Den må vel returnere et object når du kalder

employees.GetEmployee(txtEmployeeId.Text);

Smider den ikke en DataTable tilbage til dit klient program?
Avatar billede Syska Mester
21. november 2011 - 10:13 #5
Jeg tror ikke du kan gøre det sådan, da Activator.GetObject kun laver en proxy ... altså sender kald videre til din Url du har angivet.

Hvis du ikke kender den konkrete implementation på din klient og du f.eks har en metode ... hvad kode skal den så kalde?

Logisk set kan jeg ikke se hvordan det skal kunne lade sig gøre uden at kalde endnu en proxy  hvor din implementation findes.

Men hvad er problemet i at din klient kender implementeringen? Jeg kan godt se problemet hvis du har en metode som er implementeret forkert, så ville det være nemmere at fikse server side og alle klienter ville automatisk komme til at virke.

Men så kan du også have lavet Proxy'er til dit IEmployee interface på din server.

Håber det giver lidt stof til eftertanke i dit design.
Avatar billede simsen Mester
21. november 2011 - 10:45 #6
Jo, jeg får table med over og vil tænke over implementation videre (synes ikke jeg bestiller andet *griner)...

Jeg bruger så tabellen istedet for - havde bare håbet jeg kunne undgå at skrive tabel navne direkte ind i klienten.

Smid et svar og du får pointsene :-)
Avatar billede Syska Mester
21. november 2011 - 10:57 #7
Svar.

Well ... du kan undgå mange ting, men for at hjælpe der skal vi nok have en bedre oversigt over din projekt.

Jeg ville nok lave nogen DTO(Data Tranfers Object) klasser som kun indeholder data(altså properties) og så sende dem tilbage så du har det type stært i stedet for at sende en DataTable tilbage.

mvh
Avatar billede simsen Mester
21. november 2011 - 12:51 #8
Den dag, hvor jeg føler, jeg selv har styr på oversigten, skal du nok få den *griner* Tror Arne, er den der har mest styr på den lige nu :-)

Jeg har faktisk fået det til at virke....

Interface ser sådan her ud:

public interface IEmployeeService
    {
        string employeeId { get; set; }
        string employeeName { get; set; }
        string employeeGroupId { get; set; }
        string password { get; set; }
        bool isAdmin { get; set; }
        bool isActive { get; set; }

        DataTable GetEmployee(string employeeId);

        bool GetEmployeeIsRegistered(string employeeId, string password, out bool isAdmin, out bool isActive);
    }

og mit implementation ser det så sådan her ud:

public class Employees : MarshalByRefObject, IEmployeeService
    {
        public string employeeId { get; set; }
        public string employeeName { get; set; }
        public string employeeGroupId { get; set; }
        public string password { get; set; }
        public bool isAdmin { get; set; }
        public bool isActive { get; set; }

        public DataTable GetEmployee(string EmployeeId)
        {
            try
            {
                DALEmployees employees = new DALEmployees();

                DataTable getData = employees.GetEmployee(EmployeeId);

                employeeId = EmployeeId;
                employeeName = employees.employeeName;
                employeeGroupId = employees.employeeGroupId;
                password = employees.password;
                isAdmin = employees.isAdmin;
                isActive = employees.isActive;

                return getData;
            }
            catch
            {
                return null;
            }
        }

        public bool GetEmployeeIsRegistered(string employeeId, string password, out bool isAdmin, out bool isActive)
        {
            isAdmin = false;
            isActive = false;

            try
            {
                DALEmployees employees = new DALEmployees();

                return employees.GetEmployeeIsRegistered(employeeId, password, out isAdmin, out isActive);
            }
            catch
            {
                return false;
            }

        }
    }

og så har jeg selvfølgelig min DAL som du har set:

#region " Enums, Members, Properties "

        DBUtility DAL = new DBUtility();

        public string employeeId { get; set; }
        public string employeeName { get; set; }
        public string employeeGroupId { get; set; }
        public string password { get; set; }
        public bool isAdmin { get; set; }
        public bool isActive { get; set; }

        #endregion

        #region " Data Access Get "

        public DataTable GetEmployee(string employeeId)
        {
            DAL.AddParameter("employeeId", employeeId);

            DataTable getData = DAL.GetDataTable("GetEmployee", CommandType.StoredProcedure);

            employeeName = string.Empty;
            employeeGroupId = string.Empty;
            password = string.Empty;
            isAdmin = false;
            isActive = false;

            if (getData.Rows.Count > 0)
            {
                employeeId = Convert.ToString(getData.Rows[0]["employeeId"]);
                employeeName = Convert.ToString(getData.Rows[0]["employeeName"]);
                employeeGroupId = Convert.ToString(getData.Rows[0]["employeeGroupId"]);
                password = Convert.ToString(getData.Rows[0]["password"]);
                isAdmin = Convert.ToBoolean(getData.Rows[0]["isAdmin"]);
                isActive = Convert.ToBoolean(getData.Rows[0]["isActive"]);
            }

            return getData;
        }
        public bool GetEmployeeIsRegistered(string employeeId, string password, out bool isAdmin, out bool isActive)
        {
            bool isRegistered = false;
           
            DAL.AddParameter("employeeId", employeeId);
            DAL.AddParameter("password", password);

            DataTable getData = DAL.GetDataTable("GetEmployeeIsRegistered", CommandType.StoredProcedure);

            isAdmin = false;
            isActive = false;

            if (getData.Rows.Count > 0)
            {
                employeeId = Convert.ToString(getData.Rows[0]["employeeId"]);
                password = Convert.ToString(getData.Rows[0]["password"]);
                isAdmin = Convert.ToBoolean(getData.Rows[0]["isAdmin"]);
                isActive = Convert.ToBoolean(getData.Rows[0]["isActive"]);

                isRegistered = true;
            }

            return isRegistered;
        }

        #endregion

Når jeg så tilgår gør jeg det på følgende måde:

IEmployeeService employees = (IEmployeeService)Activator.GetObject(typeof(IEmployeeService), ServerSettings + "/Employees");

employees.GetEmployee(txtEmployeeId.Text);
employeeName = employees.employeeName;

og så får jeg fat i det rigtige :-)
Avatar billede Syska Mester
21. november 2011 - 13:00 #9
Det virker helt mærkeligt.

Du kalder "GetEmployee" som sætter en værdi på server objectet på serveren. den DataTable du får tilbage smider du væk. Så return "null" hvis du ikke skal bruge den.

Derefter kadler du en property på dit object på serveren som blev sat af overstående kald. Hvilket jeg ikke helt kan forstå. Hvorfor returere du ikke hvad du sprøger efter i det første kald ?

mvh
Avatar billede arne_v Ekspert
22. november 2011 - 04:40 #10
Til inspiration:

Common.cs


using System;
using System.Collections.Generic;

namespace Demo.Common
{
    [Serializable]
    public class Employee
    {
        public int Id { get; set; }
        public String Name { get; set; }
        public String Job { get; set; }
        public override string ToString()
        {
            return "(" + Id + "," + Name + "," + Job + ")";
        }
    }
    public interface IEmployeeService
    {
        List<Employee> GetAllEmployees();
        Employee GetEmployeeById(int id);
    }
}


Server.cs


using System;
using System.Collections.Generic;
using System.Runtime.Remoting;

using Demo.Common;

namespace Demo.Server
{
    public class EmployeeService : MarshalByRefObject, IEmployeeService
    {
        public List<Employee> GetAllEmployees()
        {
            List<Employee> res = new List<Employee>();
            res.Add(new Employee { Id=1, Name="Dilbert", Job="Engineer" });
            res.Add(new Employee { Id=2, Name="Wally", Job="Socalled engineer" });
            res.Add(new Employee { Id=3, Name="PHB", Job="Boss" });
            return res;
        }
        public Employee GetEmployeeById(int id)
        {
            return new Employee { Id=id, Name="Fusk", Job="Aner det ikke" };
        }
    }
    public class ServerMain
    {
        public static void Main(string[] args)
        {
            RemotingConfiguration.Configure("Server.exe.config", true);
            Console.Write("Press enter to exit");
            Console.ReadKey();
        }
    }
}


Client.cs


using System;
using System.Collections.Generic;
using System.Runtime.Remoting;
using System.Runtime.Remoting.Channels;
using System.Runtime.Remoting.Channels.Tcp;

using Demo.Common;

namespace Demo.Client
{
    public class ClientMain
    {
        public static void Main(string[] args)
        {
            ChannelServices.RegisterChannel(new TcpClientChannel(), true);
            IEmployeeService srv = (IEmployeeService)Activator.GetObject(typeof(IEmployeeService), "tcp://localhost:40000/EmployeeService");
            Employee one = srv.GetEmployeeById(123);
            Console.WriteLine(one);
            List<Employee> all = srv.GetAllEmployees();
            foreach(Employee e in all)
            {
                Console.WriteLine(e);
            }
        }
    }
}


Server.exe.config


<configuration>
  <system.runtime.remoting>
      <application name="EmployeeService">
        <service>
            <wellknown mode="Singleton" type="Demo.Server.EmployeeService, Server" objectUri="EmployeeService"/>
        </service>
        <channels>
            <channel port="40000" ref="tcp"/>
        </channels>
      </application>
  </system.runtime.remoting>
</configuration>


build:


csc /t:library Common.cs
csc /r:Common.dll Server.cs
csc /r:Common.dll Client.cs
Avatar billede arne_v Ekspert
22. november 2011 - 04:40 #11
C:\Work\remoting>client
(123,Fusk,Aner det ikke)
(1,Dilbert,Engineer)
(2,Wally,Socalled engineer)
(3,PHB,Boss)
Avatar billede arne_v Ekspert
22. november 2011 - 04:43 #12
Og dt er faktisk Buzzzz's DTO koncept.

Du skal husk at de klasser skal vaere serializable.

Jeg har saa valgt at putte DTO klassen i common.

Det er efter min mening helt fint. Hvis man vil aendre i DTO klassen skal der som oftest ogsaa aendres i client.

Hvis man gerne vil undgaa det burde man kunne lave et IEmployee og kun brug dette i common og client.
Avatar billede arne_v Ekspert
22. november 2011 - 04:51 #13
Jeg proevede lige:


using System;
using System.Collections.Generic;

namespace Demo.Common
{
    public interface IEmployee
    {
        int Id { get; set; }
        String Name { get; set; }
        String Job { get; set; }
    }
    public interface IEmployeeService
    {
        List<IEmployee> GetAllEmployees();
        IEmployee GetEmployeeById(int id);
    }
}



using System;
using System.Collections.Generic;
using System.Runtime.Remoting;

using Demo.Common;

namespace Demo.Server
{
    [Serializable]
    public class Employee : IEmployee
    {
        public int Id { get; set; }
        public String Name { get; set; }
        public String Job { get; set; }
    }
    public class EmployeeService : MarshalByRefObject, IEmployeeService
    {
        public List<IEmployee> GetAllEmployees()
        {
            List<IEmployee> res = new List<IEmployee>();
            res.Add(new Employee { Id=1, Name="Dilbert", Job="Engineer" });
            res.Add(new Employee { Id=2, Name="Wally", Job="Socalled engineer" });
            res.Add(new Employee { Id=3, Name="PHB", Job="Boss" });
            return res;
        }
        public IEmployee GetEmployeeById(int id)
        {
            return new Employee { Id=id, Name="Fusk", Job="Aner det ikke" };
        }
    }
    public class ServerMain
    {
        public static void Main(string[] args)
        {
            RemotingConfiguration.Configure("Server.exe.config", true);
            Console.Write("Press enter to exit");
            Console.ReadKey();
        }
    }
}



using System;
using System.Collections.Generic;
using System.Runtime.Remoting;
using System.Runtime.Remoting.Channels;
using System.Runtime.Remoting.Channels.Tcp;

using Demo.Common;

namespace Demo.Client
{
    public class ClientMain
    {
        public static void Main(string[] args)
        {
            ChannelServices.RegisterChannel(new TcpClientChannel(), true);
            IEmployeeService srv = (IEmployeeService)Activator.GetObject(typeof(IEmployeeService), "tcp://localhost:40000/EmployeeService");
            IEmployee one = srv.GetEmployeeById(123);
            Console.WriteLine(one.Id + " " + one.Name + " " + one.Job);
            List<IEmployee> all = srv.GetAllEmployees();
            foreach(IEmployee e in all)
            {
                Console.WriteLine(e.Id + " " + e.Name + " " + e.Job);
            }
        }
    }
}
Avatar billede arne_v Ekspert
22. november 2011 - 04:51 #14
Men som sagt tror jeg ikke at jeg gad bruge interfaces for DTO.
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
IT-kurser om Microsoft 365, sikkerhed, personlig vækst, udvikling, digital markedsføring, grafisk design, SAP og forretningsanalyse.

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