.net WCF:公开不带集合的只读DataMember属性?

.net WCF:公开不带集合的只读DataMember属性?,.net,wcf,readonly,datacontract,datamember,.net,Wcf,Readonly,Datacontract,Datamember,我有一个服务器端类,可以通过[DataContract]在客户端使用它。这个类有一个只读字段,我想通过属性使其可用。但是,我无法这样做,因为似乎不允许我在没有get和set的情况下添加[DataMember]属性 那么-有没有一种不使用setter就拥有[DataMember]属性的方法 [DataContract] class SomeClass { private readonly int _id; public SomeClass() { .. } [Data

我有一个服务器端类,可以通过[DataContract]在客户端使用它。这个类有一个只读字段,我想通过属性使其可用。但是,我无法这样做,因为似乎不允许我在没有get和set的情况下添加[DataMember]属性

那么-有没有一种不使用setter就拥有[DataMember]属性的方法

[DataContract]
class SomeClass
{
    private readonly int _id; 

    public SomeClass() { .. }

    [DataMember]
    public int Id { get { return _id; } }        

    [DataMember]
    public string SomeString { get; set; }
}
或者解决方案是使用[DataMember]作为字段,如图所示?也尝试过这样做,但似乎不关心字段是否为只读

编辑:这是通过这样的黑客攻击来创建只读属性的唯一方法吗?不-我不想这么做

[DataMember]
public int Id
{
    get { return _id; }
    private set { /* NOOP */ }
}

在使用类实现契约之前定义服务契约接口。

将DataMember属性放在字段而不是属性上

记住,WCF不知道封装。封装是一个OOP术语,而不是SOA术语


也就是说,请记住,该字段对于使用您的类的人是只读的-任何使用该服务的人都可以完全访问他们这一方的字段。

您的服务器端类实际上不会提供给客户端

发生的情况是:基于数据契约,客户机将从服务的XML模式中创建一个新的独立类。它不能使用服务器端类本身

它将根据XML模式定义重新创建一个新类,但该模式不包含任何特定于.NET的内容,如可见性或访问修饰符—毕竟,它只是一个XML模式。客户端类将以这样一种方式创建,即它在线路上具有相同的封装外形-例如,它基本上序列化为相同的XML格式

您不能通过标准的基于SOAP的服务来传输有关该类的.NET专有技术-毕竟,您传递的只是序列化消息-没有类

检查由Microsoft的Don Box定义的SOA的四个原则:

边界是明确的 服务是自主的 服务共享架构和契约,而不是类 兼容性基于策略
请参阅第3点-服务共享模式和契约,而不是类-您只共享数据契约的接口和XML模式-仅此而已-没有.NET类。

我想传递给Silverlight的服务层中的一个类中有一些属性。我不想创建一个全新的类

不是真正的“推荐”,但这似乎是将整个属性传递给silverlight仅用于可视化数据绑定的两个缺点中较小的一个

public class PricingSummary
{
    public int TotalItemCount { get; set; } // doesnt ideally belong here but used by top bar when out of store area

    public decimal SubTotal { get; set; }
    public decimal? Taxes { get; set; }
    public decimal Discount { get; set; }
    public decimal? ShippingTotal { get; set; }
    public decimal Total
    {
        get
        {
            return + SubTotal
                   + (ShippingTotal ?? 0)
                   + (Taxes ?? 0)
                   - Discount;
        }
        set
        {
            throw new ApplicationException("Cannot be set");
        }
    }
}

有一种方法可以实现这一点。但请注意,它直接违反了本节中引用的以下原则:

三,。服务共享架构和契约,而不是类

如果这一违规行为与您无关,您可以这样做:

将服务和数据契约移动到单独的可移植类库中。让我们将此程序集称为SomeService.Contracts。以下是定义不可变[DataContract]类的方法:

请注意,[DataMember]应用于支持字段,而不是相应的只读属性

从您的服务应用程序项目中引用契约程序集,我将我的称为SomeService.Web,从您的客户机项目中,我的称为SomeService.client。这可能会导致解决方案中存在以下项目依赖关系:

接下来,当您向客户端项目添加服务引用时,请确保启用了“重用类型”选项,并确保您的合同程序集为SomeService。合同将包含在此项目中:

瞧!VisualStudio不会从服务的WSDL模式生成新的Foo类型,而是将重用来自契约程序集中的不可变Foo类型


最后一个警告:您已经偏离了中引用的服务原则。但是不要再迷路了。您可能会开始向数据契约类添加业务逻辑;不要。它们应该尽可能地靠近哑数据传输对象DTO。

啊,是的-如前所述,我尝试将字段设置为DataMember,但它在客户端上没有显示为只读。但是没有办法让它在客户端成为只读的吗?不,只读是一个C术语,而不是SOA。您不能使XML成为ReadOnlyTanks的一部分。这似乎是除了创建特定于数据的类之外的最佳选择。。。对于小型应用程序来说,这就是太多的额外代码。你是什么意思?DataMember位于通过DataContract可用的DTO类中。我也有一份服务合同,但这在这里并没有真正的关系-是吗?这是一份数据合同,而不是一份解释得很好的服务合同。谢谢你的澄清!这些信息很好,但我不认为它直接回答了问题。所有这些都说明了目前的情况是多么荒谬。既然服务器端类只用于生成契约,为什么需要setter?服务器上的实例可能是串行的
如果不是因为DataContractSerializer是prissy的话,没有它们的话,ize对客户机来说很好。只有相反的情况才会导致问题。同意@atoumey。Marc,你能详细说明一下OP问题的可能解决方案吗?我在这个解决方案中看到的问题是,因为不能同时使用ISerializable和DataContract,DataContract还定义了序列化。如果您尝试序列化此对象,它将在反序列化期间引发异常。因此,与抛出异常相比,黑客明智地选择no op似乎是唯一的选择。在这种情况下,将Total实现为一个扩展方法效果很好,我知道在你写这篇文章时,扩展方法可能不存在。@PaulSuart我需要它作为数据绑定的属性,所以它需要是属性,并且没有扩展属性:-但是的,我确实倾向于忘记扩展方法,除非延伸libraries@Simon_Weaver我有一个FullName属性,它的get-like:[DataMember]公共字符串FullName{get{return FirstName++names;}}},但它抛出异常。ay解决方案?如果名字和姓氏是简单的字符串属性,则不应引发异常-可能异常来自其他地方?此外,我还建议您使用FirstName++lasname.Trim,如果两个名称都没有提供,那么它将为您提供一个不带空格的全名。你到底得到了什么样的异常?如果在客户端使用WCF,你对NOOP setter的想法会导致该属性无法正确传输:在DataContract反序列化中,该类在不调用任何构造函数的情况下被实例化,然后,任何DataMember属性的setter都会传递该属性的getter在序列化时返回的内容。因此,NOOP setter将丢弃该属性,在反序列化时将其值保留为默认值。相反,请编写一个实际的、正在工作的私有setter,或者将支持变量标记为DataMember,而不是标记属性。
namespace SomeService.Contracts
{
    [DataContract]
    public sealed class Foo
    {
        public Foo(int x)
        {
            this.x = x;
        }

        public int X
        {
            get
            {
                return x;
            }
        }

        [DataMember]  // NB: applied to the backing field, not to the property!
        private readonly int x;
    }
}