当使用RIA服务与Silverlight时,我如何扩展实体?

7
在我的Silverlight解决方案的服务器端,我有两个项目:
  1. 网站用于提供Silverlight页面。
  2. 一个Entity Framework数据访问层。
我有一个实体,其中包括FirstName和LastName属性。我想要添加一个FullName属性,以便从Silverlight客户端中使用。
我已经添加了该属性:
namespace Server.DAL.Model
{
    public partial class Contact
    {
        public string FullName
        {
            get
            {
                return string.Format("{0} {1}", this.FirstName, this.LastName);
            }
        }
    }
}

在服务器端测试时,该新属性存在并且正常工作。但是在Silverlight客户端上不存在该属性。我尝试添加一个带有Include属性的元数据类,但由于字符串是原始类型,因此在编译时会出现以下错误:

实体类型“Contact”中的属性“FullName”不能标记为IncludeAttribute,因为“String”不是有效的实体类型。实体类型不能是原始类型或像字符串或Guid这样的简单类型。

如何使此属性可用于Silverlight客户端?

在服务器端的xxDataService.cs文件中,如果您在处理联系人的某个方法中引用FullName属性,您的代码是否能够编译通过?在客户端,如果您查看Generated_Code文件夹中的*.Web.g.cs文件,您能否找到Contact类并查看FullName属性是否存在? - hatchet - done with SOverflow
@hatchet - 不,FullName属性不存在于处理联系人实体的方法中。只有一个名为EntityKeyPropertyName的属性和三个方法。*.Web.g.cs文件为空。 - DaveB
1
你在 FullName 属性中添加了 [DataMember] 吗?以下是添加方法/属性到复杂类型的说明。它们可能也适用于实体。也许可以使用 buddy 类,但我没有尝试过对实体使用它。http://thegrayzone.co.uk/blog/tag/wcf-ria-services/ - Derek Beattie
@Derek Beattie - 我加了[DataMember]属性,现在可以正常工作了。您应该把这个评论转化为一个答案。 - DaveB
4个回答

6

在您的FullName属性中添加[DataMember]。 这里提供了有关向ComplexTypes添加方法/属性的说明。它们可能也适用于实体。也许可以使用buddy类,但我没有尝试过这个方法来处理实体。

namespace Server.DAL.Model
{
    public partial class Contact
    {
        [DataMember]
        public string FullName
        {
            get
            {
                return string.Format("{0} {1}", this.FirstName, this.LastName);
            }
        }
    }
}

1

你应该把你分享的代码放到一个名为Contact.shared.cs的文件中。WCF RIA工具会精确地获取这段代码,并在Silverlight项目中创建一个包含该代码的文件。客户端代码随后可以访问此成员并复制编译在服务器项目中的代码。

MSDN文档中,您可以了解更多关于共享代码的信息。


此解决方案无需手动复制任何代码,这是一个巨大的优势。 - Scott Munro

0

我认为将属性添加到数据模型中并不是最好的方法,因为我喜欢保持模型的清晰(而不是实现特定的,这样我的实体可以在各种项目中工作,而不会受到其他项目的干扰)。我解决这个问题的方式是使用扩展方法。以下是使用扩展方法而不是将其添加到数据成员中的完全相同的场景(来自我的代码)。

namespace <MyAppName>.Services.Entities
{
  public static class UserExtension
  {

    public static String FullName(this User user)
    {
        return String.Format("{0} {1}", user.First, user.Last);

    }
  }
}

请注意方法的参数及其定义(即这不是一个“普通”的方法定义)。我有一个名为“Common.dll”的基础程序集,它始终被引用...因此我将扩展方法放在该DLL中。我还确保扩展方法的命名空间与实体的命名空间完全匹配。如果您这样做,该方法将显示在实体上,就像它是实体的一部分一样。
唯一的缺点是您必须将其实现为方法,而不能将其定义为属性...这意味着没有直接数据绑定。但是您的ViewModel或IValueConverter可以轻松处理这个问题。

0

我猜你正在使用RIA Services?如果是这样,问题在于RIA Services只会将服务器端类的结构复制到客户端,而不会复制你在属性或方法中编写的任何自定义代码。

好消息是,解决方案很简单,你已经接近成功了。RIA Services生成的客户端类是部分类,就像Entity Framework在服务器上所做的那样。这意味着你可以在Silverlight项目中使用部分类扩展这些类,就像在服务器项目上所做的那样。

只需将你的类从服务器项目移动到Silverlight项目中,确保你的命名空间与RIA Services为你生成的类的命名空间匹配,你就可以开始工作了。

祝你好运!


在这种情况下,RIA服务不需要将FullName的逻辑传递给客户端。如果将其公开为公共属性并在服务器上执行逻辑,则计算出的值应传输到客户端,并在那里可用,只要客户端模型也公开该值即可。话虽如此,我通常会像你建议的那样使用客户端部分类来处理。 - hatchet - done with SOverflow
有趣。我从未尝试过在服务器上扩展类以将计算值传播到客户端。如果您正在为多个客户端开发单个服务,则可能会有优势,因为您不必为每个客户端重新定义FullName。但是我也喜欢客户端方法,因为a)它对我总是有效的,b)我认为Silverlight项目是表示层,而计算FullName之类的内容则是表示问题! - Michael Ames
@Michael Ames - 我可以接受客户端解决方案。我正在使用MVVM模式。你能发一些示例代码吗? - DaveB
我可以这么做,但也许我的解释不够清楚,因为实际上我能给你展示的东西就是你已经写好的代码。你的思路非常正确,使用部分类来扩展Contact类。唯一需要做的不同之处就是扩展RIA Services为你生成的Contact版本,该版本存储在Silverlight项目中,而不是扩展Entity Framework为你生成的版本,该版本存储在服务器项目中。 - Michael Ames
你可以通过将你已经编写的部分类定义从Server项目直接拖放到Silverlight项目中来完成此操作。简单易行!唯一需要注意的是,RIA可能会在不同的命名空间中生成客户端版本的Contact类,而不是你定义的那个。如果是这种情况,你需要更改你的命名空间定义以匹配它。 - Michael Ames
你的部分类不需要任何特殊的[Attributes]或其他修饰符。Contact.FullName将作为Contact类的一等公民进行编译,就像Contact.LastName和Contact.FirstName一样。它将出现在Intellisense中,您可以将其绑定到TextBlock或DataGrid列等等。这就是全部内容了。 - Michael Ames

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接