WCF i DataContract

0

Zapoznaję się z WCFem po trochu z tutoriali msdna. Utknęłam jednak w jednej rzeczy - kompletnie nie wiem, jak użyć DataContract. W jak najprostszej wersji. Po stronie serwera mam:

    [DataContract(Namespace = "http://Microsoft.ServiceModel.Samples")]
    class LastResults
    {        
        private double result;

        [DataMember]
        public double ResultValue
        {

            get { return result; }
            set { result = value; }
        }
    }

...i kompletnie nie wiem jak to zawołać po stronie aplikacji :/ A przykładu w necie znaleźć nie mogę...

0

np.
w kliencie podepnij referencje do dll z kontraktami

utworz ChannelFactory<T>
utworz Channel
uzywaj
zamknij channel i factory

0

Jest kilka sposobów, pierwszy z nich podał kolega wyzej, ale wg tego co mówi nam SOA to aplikacje współdzielą SCHEMA a nie KLASY.

Tak więc klasy generuje się z WSDL udostępnianego przez service. Wsdl zawiera wlasnie schematy klas i przykladowo informacje o operacjach ktore mozna wykonac.
Na podstawie wsdl visual studio jest w stanie stworzyć nam interfejs, klasy, a także klase gotowego klienta.

Oczywiscie wczesniej trzeba ustawić aby nasz service udostepnial wsdl (Dodaje się obiekt klasy ServiceMetadataBehavior do instancji klasy ServiceHost (Description).

Jak mamy to juz za sobą, wystarczy odpalic naszą aplikacje, a z drugiego visual studio uzyc opcji AddServiceReference i wpisac adres. Wszystko jest generowane z automatu.

0

wsdl oczywiscie mamy kiedy mowimy o web service, jesli hostujemy serwis wcf inaczej (np. tcp) to ogolniej nazywa sie to mex, ale idea ta sama
jesli mamy aplikacje po stronie klienta takze c# i mozemy dostarczyc biblioteke z kontraktami to wydaje mi sie to wlasciwym scenariuszem

0

Gdy ma być to SOA, to takie posunięcie jest błędem. Musimy wtedy mieć ciągłą kontrolę nad wszystkimi klientami korzystającymi z naszej usługi i przykładowo w razie jakichś zmian, dostarczać nowych dll z kontraktami i ręcznie zmieniać konfiguracje a nawet prze kompilować. A tak, nasz service jest zarejestrowany w UDDI, klienci mają aktualne konfiguracje i klasy.

No ale zależy jaki to projekt, jeżeli WCF używa się tylko jako technologii do przesyłu informacji to owszem taki scenariusz, jak powiedziałeś, może zaistnieć. Jednak wydaje mi się zbyt wiele 'swoich' założeń przytoczyłeś aby stwierdzić czy faktycznie scenariusz z dll jest jako ten bardziej właściwy.

0

ok, jesli SOA, to faktycznie w pelni sie zgadzam

0

Ok, ale chyba wciąż coś jest nie tak z moim kontraktem. Wkleję cały kod:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.ServiceModel;
using System.ServiceModel.Description;
using System.Runtime.Serialization;

namespace MyTypes
{
    [DataContract(Namespace = "http://Microsoft.ServiceModel.Samples")]
    class LastResults
    {
        private double result;

        [DataMember]
        public double ResultValue
        {

            get { return result; }
            set { result = value; }
        }
    }
}

namespace Microsoft.ServiceModel.Samples
{
    [ServiceContract(Namespace = "http://Microsoft.ServiceModel.Samples")]
    public interface ICalculator
    {
        [OperationContract]
        double Add(double n1, double n2);
        [OperationContract]
        double Subtract(double n1, double n2);
        [OperationContract]
        double Multiply(double n1, double n2);
        [OperationContract]
        double Divide(double n1, double n2);
    }

    

    class CalculatorService : ICalculator
    {
       MyTypes.LastResults lastResult;

        public double Add(double n1, double n2)
        {
            double result = n1 + n2;
            Console.WriteLine("Received Add({0},{1})", n1, n2);
            // Code added to write output to the console window.
            Console.WriteLine("Return: {0}", result);
            lastResult.ResultValue = result;
            return result;
        }

        public double Subtract(double n1, double n2)
        {
            double result = n1 - n2;
            Console.WriteLine("Received Subtract({0},{1})", n1, n2);
            Console.WriteLine("Return: {0}", result);
            lastResult.ResultValue = result;
            return result;
        }

        public double Multiply(double n1, double n2)
        {
            double result = n1 * n2;
            Console.WriteLine("Received Multiply({0},{1})", n1, n2);
            Console.WriteLine("Return: {0}", result);
            lastResult.ResultValue = result;
            return result;
        }

        public double Divide(double n1, double n2)
        {
            double result = n1 / n2;
            Console.WriteLine("Received Divide({0},{1})", n1, n2);
            Console.WriteLine("Return: {0}", result);
            lastResult.ResultValue = result;
            return result;
        }
    }

    class Program
    {
        static void Main(string[] args)
        {
            Uri baseAddress = new Uri("http://localhost:8000/ServiceModelSamples/Service");
            ServiceHost selfHost = new ServiceHost(typeof(CalculatorService), baseAddress);

            try
            {
                selfHost.AddServiceEndpoint(typeof(ICalculator), new WSHttpBinding(), "CalculatorService");
                ServiceMetadataBehavior smb = new ServiceMetadataBehavior();
                smb.HttpGetEnabled = true;
                selfHost.Description.Behaviors.Add(smb);

                selfHost.Open();
                Console.WriteLine("The service is ready.");
                Console.WriteLine("Press <ENTER> to terminate service.");
                Console.WriteLine();
                Console.ReadLine();

                // Close the ServiceHostBase to shutdown the service.
                selfHost.Close();
            }
            catch (CommunicationException ce)
            {
                Console.WriteLine("An exception occurred: {0}", ce.Message);
                selfHost.Abort();
            }
        }
    }
}

Widzę wszystkie Operation Contracty, ale Data Contract za nic nie mogę zawołać :/ Zapewne ma to związek z tym, że jedno ma dodane atrybuty kontraktu w interfejsie a drugie nie...? Powinnam to do interfejsu dorzucić? Ale czy nowego, czy tego samego? Czy może być przy jednym interfejsie zarówno atrybut Service Contract jak i Data Contract...?

edit:
To wydzielenie do oddzielnego namespace'a to już wynik moich kombinacji, z początku było w tym samym, ale to raczej nie ma znaczenia - nie działa tak czy siak :P

0

zrob klase kontraktu publiczna, moze byc w osobnym namespace

0

Nic to nie zmieniło :/
A czy powodem moich problemów nie jest przypadkiem to, że nie wystawiam oddzielnego endpointu dla tego kontraktu? Chodzi mi o linijkę:

selfHost.AddServiceEndpoint(typeof(ICalculator), new WSHttpBinding(), "CalculatorService");

Tutaj dodaję interfejs ICalculator, i faktycznie widzę w Service References wszystkie operacje z tego interfejsu. Nie widzę absoltnie nic na temat klasy LastResult :/ Może za bardzo trzymam się przykładu z msdna? (tam DataContract jest przy klasie, nie przy interfejsie). Może muszę zrobić interfejs?

0

Nie widzisz, pewnie dlatego że w Interfejsie nie używasz nigdzie tej klasy więc nie ma powodu jej udostępniania na zewnątrz.

0

To jak sprawić, by był powód? Mnie nie chodzi o funkcjonalnośc. Ja chcę zobaczyć kilka różnych rodzai kontraktów. Chcę mieć możliwość użycia klasy LastResult po stronie klienta, nawet jeśli nie ma ona żadnego sensu ani żadnego związku z interfejsem CalculatorService. Jak dla mnie to może nawet nie przechowywać ostatniego wyniku, być po prostu klasą z jednym doublem dowolnym.

Ponowię pytanie - czy żeby wystawić kontrakt, musi on być nałożony na interfejs? Bo w przykładzie DataContractu na msdnie jest na klasie. Stąd w ogóle wziął się mój kod.

0

Nie, DataContract jest na klasy.

przykładowy kod z użyciem DataContract

Interfejs

    [ServiceContract]
    public interface IPersonService
    {
        [OperationContract]
        Person GetPerson(int id);
    }

Implementacja

    public class PersonService : IPersonService
    {
        #region IPersonService Members

        public Person GetPerson(int id)
        {
            return SomeDatabase.Instance.GetPersonObject(id);
        }

        #endregion
    }
DataContract, zaleca się dziedziczenie po IEDO, przydaje sie przy Versioningu
    [DataContract]
    public class Person:IExtensibleDataObject
    {
        [DataMember] 
        public string Name { get; set; }
        
        [DataMember]
        public string Surname { get; set; }
        
        [DataMember] 
        public string Town { get; set; }

        [DataMember] 
        public int Age { get; set; }

        #region IExtensibleDataObject Members

        public ExtensionDataObject ExtensionData { get; set; }

        #endregion
    }

DataContract jest tutaj dlatego, ponieważ obiekty są serializowane przy pomocy DataContractSerializera do postaci XMLowej, stąd też oznaczanie poszczególnych elementów atrybutami DataMember. Nazwy elementów można zmieniać nadając Właściwość Name dla atrybutu DataMember

0

Ok, zaczyna powoli mi to działać, cieszy mnie to bardzo. Teraz kolejne pytanie:
Po stronie klienta mogę sobie zawołać:

client.getLastResult();

gdzie getLastResult zwraca właśnie typ LastResults (otagowany jako DataContract). Jak powinnam odebrać takie dane? Do nie obiektu bez określania typu? Czy też mogę jakoś pobrać dane potrzebne do używania klasy LastResults?

0

Jeśli włączyłaś sobie udostępnianie WSDL to możesz użyć opcji AddServiceReference do wygenerowania wszystkiego co potrzebne.
Jesli robisz to wszystko, że tak powiem, "z palca" to musisz wrzucić klasy z DataContract do jakiejs współdzielonej dll, albo po prostu skopiowac klase LastResult do klienta (majac na uwadze aby zachowac ten sam namespace w atrybucie DataContract).

Oczywiscie jesli masz juz wygenerowanego klienta (AddServiceReference albo ręcznie svcutil) to aby pobrać getLastResult() i przypisac to do obiektu typu LastResult nie musisz nic wiecej czarować. Zwyczajne obiektowe programowanie

LastResult lr_instance = client.GetLastResult();

Jesli w kliencie nie bedziesz miała klasy docelowej to WCF nie bedzie wiedział to czego zdeserializować obiekt. To że napiszemy
object o = client.getLastResult()
to nie znaczy że kompilator nie zna typu ktory jest pod "o".

Trzymając się konwencji nazwy metod w C# pisze sie z dużej litery... byc może przyzwyczajenie z Javy ;P

0

Ha! Pięknie jest :)
Wszystko się wyjaśniło, wystarczyło wpisać:

Microsoft.ServiceModel.Samples.LastResults resultClient;

1 użytkowników online, w tym zalogowanych: 0, gości: 1