【问题标题】:WCF Generic Dictionary and Understanding WCFWCF 通用字典和理解 WCF
【发布时间】:2012-12-27 23:07:48
【问题描述】:

好的,我在解决某个特定问题时遇到了很大的困难。通过服务转移对象。从概念上讲,这是有道理的……我认为?根据我的阅读,除非明确定义,否则无法序列化 Generic。

所以我想提供我的例子;我根本无法上班。意思是;我相信还有其他人也遇到了一些困难。当您提供代码时提供帮助;这将起作用并解释它。这样我就可以完全理解这个问题。这将有助于我了解 Windows Communication Foundation。

目标是一个只有五个字段的客户端应用程序;它在其中“发布”到服务器。

  • 名字
  • 姓氏
  • 电子邮件地址
  • 电话号码
  • 网站地址

这并不太复杂。

这就是我所做的,在我学习 WCF 的过程中,我已经尽可能地接近 OOP 主体,用于基于 SOA 的应用程序。这样,它提供了代码可重用性。

模型/数据合同:

#region Using Reference...

using System.Runtime.Serialization;
using System.Collections.Generic;
using System.Threading.Tasks;
using System.Text;
using System.Linq;
using System;

#endregion

namespace _2Do.Model.Customer
{

    [DataContract(IsReference = true)]
    public class Person
    {

        #region Declared Variable.

        string first;
        string last;
        string email;
        string phone;
        string site;

        #endregion

        #region Constructor:

        Person()
        {

            // Empty Constructor.

        }

        #endregion

        #region Data Member Properties:

        [DataMember()]
        public string First
        {

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

        }

        [DataMember()]
        public string Last
        {

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

        }

        [DataMember()]
        public string Email
        {

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

        }

        [DataMember()]
        public string Phone
        {

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

        }

        [DataMember()]
        public string Site
        {

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

        }

        #endregion

    }

}

这就是应该通过元数据为客户端公开的对象;所以对于服务接口我尝试了这个:

#region Using Reference...

using System.Collections.Generic;
using System.Threading.Tasks;
using System.ServiceModel;
using _2Do.Model.Customer;
using System.Text;
using System.Linq;
using System;

#endregion

namespace _2Do.Contract.Customer
{

    [ServiceContract (Namespace = "https://_2Do") ]
    public interface IPerson
    {

        [OperationContract()]
        Person SetCustomer(Dictionary<Guid, Person> info);

    }

}

所以以上是目标;转移我的 Person 对象;存储到字典中。另一件事要注意;我是否认为通过引用传递值的实现将有助于序列化。我想一旦数据存储在内存中;它将包含一个明确的方法来处理。这是我的错吗?

这就是我的DataContractServiceContract

此时的实现是这样的:

#region Using Reference...

using System.Collections.Generic;
using System.Threading.Tasks;
using _2Do.Contract.Customer;
using System.ServiceModel;
using _2Do.Model.Customer;
using System.Text;
using System.Linq;
using System;

#endregion

namespace _2Do.Service.Customer
{

    [ServiceBehavior(InstanceContextMode = InstanceContextMode.PerCall)]
    public class PersonService : IPerson
    {

        #region Constructor:

        PersonService()
        {

            // Empty Constructor.

        }

        #endregion

        #region Implement Interface:

        Person SetCustomer(Dictionary<Guid, Person> info)
        {

            // Receive an error that indicates; best overload method.
            // Contains invalid arguments.

        }

        #endregion

    }

}

然后我创建了一个单独的项目来托管应用程序;我创建了一个空文本文件并将其重命名为PersonService.svc

然后我输入:&lt;%@ ServiceHost Service = "_2Do.Service.Customer.PersonService" %&gt;

应该指向正确的命名空间;在PersonService 中,我有一个web.config 文件,其中包含要在Internet 信息系统中托管的最低配置。我认为这可以让我绕过定义我的地址、绑定和合同。因为 IIS 现在会为我做这一切。

然后我创建了一个ClientProxy 类;其中包含几乎是DataContract 的镜像。然后我创建了实际的客户端应用程序:

PersonProxy p = new PersonProxy();
p.First = txtFirst.Text;
p.Last = txtLast.Text;
p.Email = txtEmail.Text;
p.Phone = txtPhone.Text;
p.Site = txtSite.Text;
Dictionary<Guid, Person> i = new Dictionary<Guid, Person>();
i.Add(Guid.NewGuid(), p);

我有这些项目:

  • 模型 --> DataContract
  • 合同 --> 服务合同
  • 服务 --> 接口实现
  • Host --> Stores Service / Svc
  • ClientProxy --> 接口实现
  • Client --> 链接到 ClientProxy 要继承的实际值。

这就是我在客户端上使用它的方式。我不确定我在哪里搞砸了。我真的很想了解 WCF,因为我需要为工作项目学习它。但是我很沮丧,觉得自己很愚蠢,因为我似乎无法解决这个问题。

如果我按照教程进行操作,它会起作用。但是一旦我回到原来的实现,它就会失败。不知道我哪里出错了。一些手握将不胜感激;但如果你能一步一步地解释它,那将不胜感激。所以我可以从错误中吸取教训,从而真正改进。

我无法让它将变量存储在服务器上,传输,我现在不知道为什么。

【问题讨论】:

  • 我目前的猜测是您没有正确设置 Data Contract 类。我认为您需要另一个派生自Dictionary 并具有CollectionDataContract 属性集的类。我正在复制您的代码以进行检查。
  • 如果你能让它工作,我将成为有史以来最快乐的人;老实说,我根本无法让这种愚蠢的服务发挥作用。感谢您的回复和帮助。
  • 奇怪,我的代码似乎可以传递参数和结果。您使用的是什么版本的框架?
  • 另一件事(非 WCF):确保 PersonService 中的 SetCustomer 方法标记为 public
  • Framework 4.5 不知道是不是因为我分离了Assemblies。或者如果我搞砸了主机/客户端代理。

标签: c# wcf web-services generics iis


【解决方案1】:

我仍然不确定您的代码为什么不起作用。也许我不了解 IIS 中的托管以及我应该了解的情况。但是,下面是我能想到的最简单的 Web 服务 + 客户端的完整工作示例。希望它能帮助您找到并解决问题。

首先,project structure:合同在一个类库项目中,服务和主机在控制台应用程序中,客户端在不同的控制台应用程序中。

数据合约:

[DataContract]
public class Person
{
    [DataMember]
    public string Name { get; set; }
    [DataMember]
    public string Email { get; set; }
}

服务合同:

[ServiceContract]
public interface IPerson
{
    [OperationContract]
    Person SetCustomer(Dictionary<Guid, Person> info);
}

服务:

public class PersonService : IPerson
{
    public Person SetCustomer(Dictionary<Guid, Person> info)
    {
        foreach (var person in info.Values)
        {
            Console.WriteLine("Name: {0} | Email: {1}", person.Name, person.Email);
        }

        var p = new Person { Name = "John Doe", Email = "John@Doe.com" };
        return p;
    }
}

服务宿主(在宿主项目的 Program.cs 中):

static void Main(string[] args)
{
    using (ServiceHost host = new ServiceHost(typeof(PersonService), new Uri("http://localhost:8080")))
    {
        host.Open();

        Console.WriteLine("Ready!");
        Console.ReadKey(true);

        host.Close();
    }
}

最后,客户端(在它自己项目的 Program.cs 中):

static void Main(string[] args)
{
    var binding = new BasicHttpBinding();
    var endpoint = new EndpointAddress("http://localhost:8080/");
    using (var factory = new ChannelFactory<IPerson>(binding, endpoint))
    {
        var request = new Dictionary<Guid, Person>();
        request[Guid.NewGuid()] = new Person { Name = "Bob", Email = "Bob@abc.com" };

        var client = factory.CreateChannel();
        var result = client.SetCustomer(request);

        Console.WriteLine("Name: {0} | Email: {1}", result.Name, result.Email);
        factory.Close();
    }
    Console.ReadKey(true);
}

希望这会有所帮助!

【讨论】:

  • 在您的服务示例中;您添加一个新项目。我假设这是来自服务器。如果我想让它反转怎么办?客户端实际为服务器提供变量的位置。业务层可以写入数据库或写入日志的位置。或者只使用这些变量。
  • 这是一篇不错的帖子,我会比较和审查,看看我是否能弄清楚并告诉你。
  • @Greg 我的客户创建了一个Dictionary&lt;Guid,Person&gt; 并在将其发送到服务之前添加了一个元素。我这样做是为了测试发送是否可以正常工作。此外,该服务会返回一个不同的 Person 对象,这在我的示例中也有效。
  • 太棒了,我会试一试,让你知道。您的帖子可能会解决我的问题。
  • @Greg 还可以查看 msdn,了解如何在 IIS 中托管 WCF 服务。 (msdn.microsoft.com/en-us/library/ms733766.aspx) 如果您仍然需要帮助,我会尝试自己在 IIS 中运行。
【解决方案2】:

您真的需要将字典作为服务合同的参数吗?保持简单 - 使用 List 代替。我的建议是:

1) 将 Guid 属性添加到 Person 数据协定

[DataContract(IsReference = true)]
public class Person
{
    #region Constructor:

    public Person()
    {
        // Empty Constructor.
    }

    #endregion

    #region Data Member Properties:

    [DataMember]
    public Guid Guid { get; set; }

    [DataMember]
    public string First { get; set; }

    [DataMember]
    public string Last { get; set; }

    [DataMember]
    public string Email { get; set; }

    [DataMember]
    public string Phone { get; set; }

    [DataMember]
    public string Site { get; set; }

    #endregion
}

2) 更改服务合同以使用列表:

[ServiceContract (Namespace = "https://_2Do") ]
public interface IPerson
{
    [OperationContract()]
    Person SetCustomer(List<Person> info);
}

3) 客户端代码如下:

PersonProxy p = new PersonProxy();
p.Guid = Guid.NewGuid();
p.First = txtFirst.Text;
p.Last = txtLast.Text;
p.Email = txtEmail.Text;
p.Phone = txtPhone.Text;
p.Site = txtSite.Text;

var list = new List<Person>();
list.Add(p);

最后,如果您的服务代码中确实需要字典,您可以使用 ToDictionary 方法轻松转换列表

【讨论】:

  • 感谢您的帖子;我会试试看。看看我的问题是在通用还是我搞砸了客户端/程序集分离。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多