C# 在WCF中实现富域模型,其中客户端对象充当远程外观

C# 在WCF中实现富域模型,其中客户端对象充当远程外观,c#,.net,wcf,architecture,C#,.net,Wcf,Architecture,我正在从事一个WCF项目,我想实现一个富域模型,其中客户端对象充当服务器端功能的远程外观,但在客户端/服务器之间具有某些共享方面,例如验证。假设我有一个Order类,它有两个方法:Save()和Submit()。在服务器上,Save()方法将写入数据库,Submit()将订单发送到供应商的系统 我想在客户机上镜像富域模型,但不是在Save()方法中进行数据库写入,而是运行验证代码,然后在WCF服务接口上调用SaveOrder(this)。这将遵循Fowler的服务层+域模型模式。理想情况下,我希

我正在从事一个WCF项目,我想实现一个富域模型,其中客户端对象充当服务器端功能的远程外观,但在客户端/服务器之间具有某些共享方面,例如验证。假设我有一个Order类,它有两个方法:Save()和Submit()。在服务器上,Save()方法将写入数据库,Submit()将订单发送到供应商的系统

我想在客户机上镜像富域模型,但不是在Save()方法中进行数据库写入,而是运行验证代码,然后在WCF服务接口上调用SaveOrder(this)。这将遵循Fowler的服务层+域模型模式。理想情况下,我希望编写一个AbstractOrder基类,实现所有共享功能并指定抽象函数,然后实现ClientOrder、ServerOrder和WCF接口IOrderService(带有Save(AbstractOrder)和Submit(AbstractOrder)),后者充当服务服务器端。ClientOrder的Save()/Submit()将调用IOrderService上的Save/Submit方法,并在方法调用期间传递自身

是否有方法指示WCF实例化和反序列化内容到哪些对象中?我特别希望在整个应用程序中使用对象的抽象版本,并且仅在反序列化后才能确定是否需要对象的客户端/服务器端版本?我们已经定制了WCF通信通道:我们使用protobuf结合gzip压缩在客户机/服务器和Ninject之间进行数据传输,用于服务实例化。理想情况下,我希望将对象实例化卸载到Ninject

我特别不希望Order类成为WCF服务,因为我处理的是一个相当胖的客户机,需要大量的逻辑来保持系统在设定的限制内运行,我最好不要以一个贫乏的域模型结束,在这个模型中,大多数逻辑被塞进服务中

在代码中,它将如下所示:

[ServiceContract]
public interface IOrderService
{
    [OperationContract]
    AbstractOrder GetById(int id);
    [OperationContract]
    IEnumerable<AbstractOrder> GetBySupplier(int supplierId);
    [OperationContract]
    void Save(AbstractOrder order);
    [OperationContract]
    void Submit(AbstractOrder order);
}

public abstract class AbstractOrder()
{
    public int Id { get; set; }
    public string Description { get; set; }
    public List<AbstractOrderline> OrderLines { get; set; }

    public abstract void Save();
    public abstract void Submit();
}

public class ClientOrder : AbstractOrder
{
    public override void Save()
    {
        ValidateOrThrow();
        _service.Save(this);
    }

    public override void Submit()
    {
        ValidateOrThrow();
        _service.Submit(this);
    }
}

public class ServerOrder : AbstractOrder
{
    public override void Save()
    {
        ValidateOrThrow();
        _unitOfWork.Save(this);
    }

    public override void Submit()
    {
        Save();

        _supplierOrderService.Submit(this);
    }
}
// Interface
Order IOrderService.GetById(int);

// Service
Order OrderService.GetById(int id)
{
    return new Order(...);
}

// Client
Order order = IOrderService.GetById(42);
order.Repository = new ClientRepository(...);
order.Submit();
[服务合同]
公共接口服务
{
[经营合同]
抽象订单GetById(int-id);
[经营合同]
IEnumerable GetBySupplier(int supplierId);
[经营合同]
作废保存(抽象订单);
[经营合同]
无效提交(抽象订单);
}
公共抽象类AbstractOrder()
{
公共int Id{get;set;}
公共字符串说明{get;set;}
公共列表顺序行{get;set;}
公共摘要无效保存();
公开摘要无效提交();
}
公共类ClientOrder:AbstractOrder
{
公共覆盖无效保存()
{
ValidateOrThrow();
_服务。保存(此);
}
公共覆盖无效提交()
{
ValidateOrThrow();
_服务。提交(本);
}
}
公共类ServerOrder:AbstractOrder
{
公共覆盖无效保存()
{
ValidateOrThrow();
_单位工作。保存(此);
}
公共覆盖无效提交()
{
Save();
_supplierOrderService.Submit(此);
}
}

默认情况下,您无法执行此操作:

// Interface
AbstractOrder IOrderService.GetById(int);

// Service
AbstractOrder OrderService.GetById(int id)
{
    return new ServiceOrder(...);
}

// Client
ClientOrder = (ClientOrder)IOrderService.GetById(42);
因为服务返回的订单不是
ClientOrder
。通过一些反思和思考,你应该能够走很长的路

或者,您可以依赖组合,而不是继承。在共享代码中引入一个
IRepository
(或给它一个名称),并在您的模型上为此创建一个属性:

public interface IRepository<T>
{
    void Save(T model);
    void Submit(T model);
}

public class Order()
{
    public int Id { get; set; }
    public string Description { get; set; }
    public List<AbstractOrderline> OrderLines { get; set; }

    [XmlIgnore]
    public IRepository<Order> Repository { get; set; }

    public void Save()
    {
        if (Repository == null) { throw new NotSupportedException(); }
        Repository.Save(this);
    }

    public void Submit()
    {
        if (Repository == null) { throw new NotSupportedException(); }
        Repository.Submit(this);
    }
}
// Client-specific implementation
public class ClientOrderRepository : IRepository<Order>
{
    private readonly IClientOrderService _service;
    public ClientOrderRepository(IClientOrderService clientOrderService)
    {
        _service = clientOrderService;
    }

    public void Save(Order order)
    {
        _service.Save(order);
    }

    public void Submit(Order order)
    {
        _service.Submit(order);
    }
}

您有五个以I开头的段落。无论如何,如果您想在WCF服务和客户端之间共享DTO类型,那么它们在整个调用堆栈中都是完全相同的类型。您可以始终在该单订单类上引入IOrderService,并通过属性设置IOrderService。在服务器上实现的类将写入数据库或调用其他服务,在客户端上设置的服务将调用WCF服务。问题是,我不想将我的域模型转换为DTO,然后再转换回客户端上的域模型。Fowler特别指出,在本地上下文中使用DTO可能有害,并使API更难使用()。其思想是将ServerOrder传输到客户端,并将其反序列化为ClientOrder实例。我们使用protobuf进行(反序列化),所以我目前正在研究是否可能影响反序列化期间创建的对象,可能是通过将它重定向到Ninject。我知道WCF边界意味着“远程”,但是因为我们的客户机/服务器应用程序共享大量代码,我们认为它们之间的通信是本地的。我的观点是,在服务器端使用的类型在客户端是完全相同的类型。(我称之为你们的DTO,我并没有提议引入另一个)。因此,您不能通过继承添加行为;请改用组合。感谢您对使用IClientMessageFormatter的提示。在将对象实例化传递给反序列化程序之前,我将看看是否可以使用它将对象实例化卸载到DI容器。如果不起作用,我将使用组合路径。使用自定义IClientFormatter/IDispatchForm试图实现我的初衷实际上是很有可能的。我必须放弃Protobuf,转而使用Newtonsoft的Json,因为Protobuf似乎不支持DI容器,但如果我能找出WCF层生成的异常,这是很有可能的。@Toolmaker很酷,如果您有任何疑问,请随意发布您自己的答案ot it working.我绝对会的,我正计划在一切正常工作后再这样做。目前正在Dispatcher.ProxyOperationRuntime.BeforeRequest()中的服务器上处理一些WCF异常方法。关于进一步的问题,Stackoverflow的礼仪是什么?我是否编辑我的原始帖子并用进一步的问题扩展我的问题?@Toolmaker否,这会改变你问题的范围。每个问题问一个问题