Entwickler-Ecke

Verteilte Systeme - WCF Datenvertrag mit eigener Klasse -> mehrfach Definition


MrKnogge - Mo 12.12.11 17:15
Titel: WCF Datenvertrag mit eigener Klasse -> mehrfach Definition
Hallo Zusammen,

ich habe ein verteiltes System bestehend aus mehreren Anwendungen (3 Service-Anbieter, ein Consumer). Alle drei Service-Anbieter haben in ihren Datenverträgen einen von mir erstellten Datentyp "TBeispiel". Diesen Datentyp beziehen sie aus der gleichen Klassenbibliothek "beispiel.dll".

Erstelle ich mir nun mit Hilfe der svcutil.exe Proxy-Klassen für meine Services, erhalte ich in meinem Consumer drei mal eine Definition für die Klasse TBeispiel.

Gibt es eine Möglichkeit, bei der Dekleration der Serialisierung der Klasse TBeispiel, oder des Datenvertrages für den Service anzugeben, dass die Klasse TBeispiel dem Consumer bereits bekannt ist?
(Auch der Consumer könnte Zugriff auf die DLL erhalten.)

Folgend die Deklaration meiner Beispielklasse sowie die Deklaration eines Service:


C#-Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
12:
13:
14:
15:
16:
17:
18:
19:
20:
21:
22:
23:
24:
25:
26:
27:
28:
29:
30:
31:
32:
33:
34:
35:
36:
37:
38:
39:
40:
41:
42:
43:
44:
45:
46:
47:
48:
49:
50:
51:
52:
53:
    [DataContract]
    [KnownType(typeof(Fahrzeug))]
    public abstract class Fahrzeug
    {
        private string _id = null;
        private string _info = null;
        [DataMember]
        public string ID
        {
            get { return _id; }
            set { _id= value; }
        }
        [DataMember]
        public string Info
        {
            get { return _info; }
            set { _info = value; }
        }
        public Fahrzeug(string ID, string Info)
        {
            this.Partnumber = Partnumber;
            this.Info = Info;
        }
    }
    [DataContract]
    [KnownType(typeof(Fahrzeug))]
    [KnownType(typeof(Auto))]
    public class Auto: Fahrzeug
    {
        public Fahrzeug(string ID, string Info)
            : base(ID, Info)
        {
        }
    }
    [DataContract]
    [KnownType(typeof(Fahrzeug))]
    [KnownType(typeof(Auto))]
    [KnownType(typeof(Transporter))]
    public class Transporter: Fahrzeug
    {
        private List<Auto> _autos;
        public Transporter(string ID, string Info)
            : base(ID, Info)
        {
            _autos = new List<Auto>();
        }
        [DataMember]
        public List<Auto> Autos
        {
            get { return _autos; }
            set { _autos= value; }
        }
    }



C#-Quelltext
1:
2:
3:
4:
5:
6:
    [ServiceContract(Namespace = "http://Microsoft.ServiceModel.Samples")]
    public interface IFuhrpark
    {
        [OperationContract]
        List<Transporter> GibMirAlleTransporter();
    }



Viele Grüße & besten Dank!
Christian


SakeSushi - Fr 30.12.11 03:18

(Hoffe habe das Problem richtig verstanden ^^; bin schon etwas müde)
Mir ist zwar eine Lösung eingefallen, finde sie jedoch nicht sehr elegant um erlich zu sein.

-> Im Grunde machst du es genau wie bisher und fügst einfach alle 3 Service Anbieter deinem Consumer Projekt als Refferenzen hinzu (unter verschiedenen Namespaces!).

-> Bevor du im Consumer-Projekt weitermachen kannst, stelle sicher das alle DataContract-Klassen partiel sind.

-> Im Consumer-Projekt erweiterst du jede der Data-Contract Klassen um eine Interface Implementierung, in einer partiellen Klasse, die dir jeglichen Zugriff auf die benötigten Properties gewährt.

Hier ein kleine Verdeutlichung:

Quelltext
1:
2:
3:
4:
5:
6:
[DataContract]
public partial class SomeEntity
{
    [DataMember]
    public object SomeProperty { get; set; }
}

-- Die Data-Contract Klasse in allen Service Projekten


Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
12:
13:
14:
15:
16:
17:
18:
19:
20:
21:
22:
23:
24:
namespace ServiceOne
{
    [ServiceContract]
    public class FirstService 
    {
        [OperationContract]
        public SomeEntity GetFirstData()
        {
            return new SomeEntity() { SomeProperty = "ahoi" };
        }
    }
}
namespace SecondService
{
    [ServiceContract]
    public class SecondService
    {
        [OperationContract]
        public SomeEntity GetSecondData()
        {
            return new SomeEntity() { SomeProperty = "moin" };
        }
    }
}

-- Zwei Services aus verschiedenen Projekten die mit gleicher Entität arbeiten


Nun zu dem Consumer-Projekt


Quelltext
1:
2:
3:
4:
public interface ISomeEntity
{
    object SomeProperty { get; set; }
}

-- Ein Interface, welches Zugriff auf die notwendigen Properties gewährt


Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
12:
namespace Consumer.ServiceReference1
{
    public partial class SomeEntity : ISomeEntity
    {
    }
}
namespace Consumer.ServiceReference2
{
    public partial class SomeEntity : ISomeEntity
    {
    }
}

-- Zwei partielle Klassen-Komponenten für jeden Service Namespace, der die Data-Contract Klasse um die Interface Angabe erweitert (Implementiert ist es ja sowieso)


Anwendung:


Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
12:
13:
14:
15:
16:
17:
18:
19:
20:
namespace Consumer
{
    public class Program
    {
        public static void Main()
        {
            ISomeEntity[] someEntities = new ISomeEntity[2];
            
            var client1 = new FirstServiceClient();
            someEntities[0] = client1.GetFirstData();

            var client2 = new SecondServiceClient();
            someEntities[1] = client2.GetSecondData();

            foreach(var entity in someEntities)
                Console.WriteLine("some service says: {0}", entity.SomeProperty);
            Console.ReadKey();
        }
    }
}



Es ist wirklich keine schöne Lösung, nur zu dieser späten Stunde fällt mir nichts besseres ein. Wenn du nach etwas kurzfristigen/schnellen suchst, versuch es so, wenn aber noch Zeit bleibt, warte lieber noch ein bisschen auf einen eleganteren Ansatz ;)

mfg SakeSushi


Pippl - Fr 30.12.11 10:57

Eine weitere Möglichkeit ist das du dem Client/Consumer die Beispiel.dll mitgibst.
Dann kann der Client direkt die TBeispiel Klasse verwenden um mit dem Services zu arbeiten

In VS2010 geht das so: Beim Client/Consumer Projekt entsprechenden Dienstverweis(e) suchen > Rechtsklick > Dienstverweis konfigurieren > Typen in Assemblys, auf die verwiesen wird, wiederverwenden (anhaken) und dann wählen was du am besten findest

Mfg