域驱动设计中的WCF序列化和值对象模式

时间:2010-08-23 06:15:36

标签: wcf serialization domain-driven-design

Eric Evans的Domain Driven Design一书描述了称为值对象的模式。值对象的一个​​重要特征是它是不可变的。

作为一个例子,我有一个值对象“Clinic”,必须有一个名字和一个id。为了使它成为一个值对象,我没有在name和id上提供setter。另外为了确保没有无效的实例,我在构造函数中使用name和id,并且不提供参数较少的构造函数。

公共类诊所 {

public Clinic(string name, string id)
{
    Name = name;
    Id = id;  
}

public string Name{get; private set;}
public string Id{get; private set;}

}

问题是,当我尝试从WCF服务返回此对象时,我得到一个异常,即该对象没有参数较少的构造函数,并且属性没有公共setter。我想避免添加参数较少的构造函数和公共setter因为那时我的域模型模式需要折腾。我怎样才能解决这个问题呢?

此致 Unmesh

2 个答案:

答案 0 :(得分:3)

我之前在序列化不可变类型时遇到了类似的问题,最后我决定实现ISerializable接口并使用SerializationInfo来存储&检索序列化/反序列化过程两端的私有变量:

http://theburningmonk.com/2010/04/net-tips-making-a-serializable-immutable-struct/

我刚刚使用相同的技术构建并运行测试应用程序,它似乎对我有用。因此,对于Clinic类的更改,您可以将其更改为:

[Serializable]
public class Clinic : ISerializable
{
public Clinic(string name, string id)
{
    Name = name;
    Id = id;  
}

public Clinic(SerializationInfo info, StreamingContext context)
{
    Name= info.GetString("Name");
    Id= info.GetString("Id");
}

public string Name{get; private set;}
public string Id{get; private set;}

[SecurityPermission(SecurityAction.Demand, SerializationFormatter = true)]
public void GetObjectData(SerializationInfo info, StreamingContext context)
{
    info.AddValue("Name", Name);
    info.AddValue("Id", Id);
}
}

这将解决您从WCF传回数据的问题。但从设计的角度来看,我同意Ladislav所说的内容,通常你会想要将你的域对象与纯粹用于消息传递的对象(DataTransferObjects)分开,在这种情况下,这是一个你可能如何接近它的例子:

// the domain object (NOT EXPOSED through the WCF service)
public class Clinic
{
public Clinic(string name, string id)
{
    Name = name;
    Id = id;  
}

public string Name{ get; private set;}
public string Id{ get; private set;}

// other methods encapsulating some business logic, etc.
...
}
// the corresponding DTO object for the domain object Clinic
// this is the type exposed through the WCF layer, that the client knows about
[DataContract]
public class ClinicDTO
{
    [DataMember]
    public string Name { get; set; }
    [DataMember]
    public string Id { get; set; }
}
// WCF service contract, NOTE it returns ClinicDTO instead of Clinic
[ServiceContract]
public interface IClinicService
{
   [OperationContract]
   ClinicDTO GetClinicById(string id);
}

为了减轻从Clinic转换为ClinicDTO的痛苦,您可以在Clinic上添加方法来执行此操作或实现隐式/显式转换器。我在这里有一个如何做到这一点的例子: http://theburningmonk.com/2010/02/controlling-type-conversion-in-c/

希望这有帮助!

答案 1 :(得分:2)

问题是您的值对象不可序列化。您打算如何使用该服务?您是否计划与客户共享域对象/值对象?如果是,IMO违反了您的域驱动设计 - 只有业务层应该能够使用域对象并调用他们的方法。如果您不想共享对象,您可能会通过添加服务引用来创建代理,这将为客户端生成数据contrats。这些合同将具有公共无参数构造函数和所有可设置的属性(并且没有域方法)。

如果您想拥有真正的域驱动设计,则不应在WCF中公开您的域对象。相反,您应该创建一组DTO并公开这些DTO。服务层将负责将这些DTO转换为Domain对象/值对象,反之亦然。