假设我有一个模型:
public class Thing
{
public int? identifier { get; set; }
public string Title { get; set; }
public string Description { get; set; }
... // arbitrary number of additional properties.
}
包含两个关键方法的服务层接口:
public interface IThingService
{
Thing GetThing(int id);
Thing AddThing(Thing thing);
}
现在,假设服务层将为identifier
上的每件事物自动生成AddThing
。还假设将此identifier
公开给表示层非常重要,因为它可能会传递给其他服务。
鉴于我必须将Thing
传递给AddThing
,这是建模Thing
的最佳方式吗?表示层的开发人员可能认为他们可以指定identifer
,但IThingService
的具体实现会忽略这一点。当然,identifier
必须公开IThingService
才能GetThing
为identifier
设置。
可以简单地忽略在AddThing
的调用中传递{{1}},还是有更好的方法对此进行建模以防止这种情况发生?
答案 0 :(得分:2)
您的服务应如下所示:
public interface IThingService
{
Thing GetThing(int id);
ThingCreationResponse AddThing(ThingCreationRequest request);
}
ThingCreationRequest
将拥有创建Thing
实体所需的所有数据。它没有标识符。标识符将由AddThing
返回。
AddThing
可以简单地返回一个Thing
id,但在这种情况下,您应该提供一个记录良好的协议来传达错误(在发生故障时返回负值或抛出异常)。
返回ThingCreationResponse
的实例会为您提供更好的灵活性,因为您可以返回新Thing
的ID或完整实例,也可以更好地传达失败原因。
答案 1 :(得分:1)
仅当服务在原始Thing
实际存在的同一内存上运行时,此操作才有效。如果此服务已断开连接,例如在另一台计算机上,则更新Thing
将不会影响原始实例,identifier
将永远不会返回到调用代码。
考虑到这一点,有两种可能的路径:您希望此服务始终是本地服务并在与调用应用程序相同的内存上运行,或者您打算使此服务实际上或最终断开连接。
如果已连接,此界面将正常工作,但您可能仍希望隐藏标识符的setter。如果这样做,您可以将其设置为内部并在单独的程序集中定义服务接口和Thing
对象,这将使表示层无法访问表示层。差不多,您仍然可以使用反射或其他hijinks调用它,但对于典型的东西,它将无法访问。
此界面刚刚破解,因为您无法以任何方式检索标识符,并且必须重新检索现在保存的项目。这里的典型解决方案将返回AddThing
方法中的标识符或整个对象(新内存),如果它足够小的话。
除此之外,不可变数据项很难推理,无论如何我都可能更愿意在调用Thing
之后创建一个新的AddThing
。
答案 2 :(得分:0)
我认为在这种情况下,AddThing的正确签名将是AddThing(字符串标题,字符串描述),并且该方法的任务是使用生成的标识符构建Thing对象。
考虑到你的上一个规范,我建议制作第二个类,ThingWithId有成员标识符和Thing。 AddThing的签名将更改为ThingWithId AddThing(Thing thing)