演练:将身份验证服务用于 Silverlight 业务应用程序

“Silverlight 业务应用程序”模板创建一个解决方案,该解决方案自动启用身份验证(其身份验证模式为 Forms 身份验证)、角色和配置文件。该解决方案包括用于登录现有用户和注册新用户的数据窗体。您不必编写任何附加代码即可使用这些功能。您可以通过定义角色和配置文件属性来自定义该解决方案。

在本演练中,您将了解如何在 Silverlight 业务应用程序中使用身份验证、角色和配置文件。您将基于用户的凭据来限制对某些域操作的访问权限并且基于用户首选项自定义用户界面。您将使用 ASP.NET 网站管理工具来管理网站中的角色和用户。

必备条件

除 WCF RIA Services 和 WCF RIA Services 工具包之外,本演练和 RIA Services 文档中提供的其他演练还要求正确安装和配置 Visual Studio 2010 和 Silverlight Developer 运行时及 SDK 等必备程序。它们还要求安装和配置具有高级服务的 SQL Server 2008 R2 Express,并安装 AdventureWorks OLTP 和 LT 数据库。

WCF RIA Services 的必备条件节点中的主题提供有关如何满足这些前提条件的详细说明。在继续本演练之前,请按照此处提供的说明执行操作,以确保您在执行本 RIA Services 演练时尽可能少地遇到问题。

创建用户和角色

您可以使用在 Silverlight 业务应用程序中提供的功能来快速实现身份验证。在下一节中,您将使用 ASP.NET 配置工具来创建用户和角色,然后以该用户的身份登录。您将通过在 Silverlight 业务应用程序中提供的注册窗体来注册新用户。

创建网站、角色和用户

  1. 在 Visual Studio 2010 中,依次选择**“文件”“新建”“项目”**。

    此时将出现**“新建项目”**对话框。

  2. 选择**“Silverlight”**项目类型。

  3. 选择**“Silverlight 业务应用程序”**模板并将应用程序命名为 ExampleBusinessApplication

    RIA_ServicesCreateBizApp

  4. 单击**“确定”**。

    请注意所创建的项目结构。Silverlight 客户端项目在 Views 文件夹中包括 Silverlight 页。这些页可用于登录用户和注册新用户。

  5. 若要打开 ASP.NET 网站管理工具,请首先在解决方案资源管理器中选择服务器项目 (ExampleBusinessApplication.Web),然后打开**“ASP.NET 配置”**工具。

  6. 在**“项目”菜单上,选择“ASP.NET 配置”**。

    如果“项目”菜单中未显示“ASP.NET 配置”选项,则可能是因为您选择了客户端项目。

    RIA_OpenAdminTool

  7. 在 ASP.NET 网站管理工具中选择**“安全性”**选项卡。

    RIA_WebAdminSecurity

  8. 在**“角色”部分中,单击“创建或管理角色”**链接。

  9. 添加名为 Managers 的新角色,然后选择**“添加角色”**按钮。

    WebAdmin_CreateRole

  10. 在右下角,单击**“上一步”**按钮。

  11. 在**“用户”部分中,单击“创建用户”**链接。

  12. 使用以下值创建新用户并选中“Managers”角色复选框。

    用户名:CustomerManager

    密码:P@ssword

    电子邮件:someone@example.com

    安全提示问题:最喜欢什么颜色?

    安全答案:蓝色

    Managers 角色:已选定

    WebAdmin_CreateUser

  13. 单击**“创建用户”**按钮。

  14. 关闭 ASP.NET 网站管理工具。

  15. 运行该解决方案。

    该应用程序的主页将出现在 Web 浏览器中。

  16. 在该页的右上角,单击**“登录”**链接。

    将显示“登录”对话框。

  17. 为用户名输入 CustomerManager,为密码输入 P@ssword,然后单击**“确定”**按钮。

    RIA_LoginManager

    您现在将以该用户的身份登录。请注意右上角的文本“欢迎您,CustomerManager”。

  18. 单击**“注销”**链接。

    您不再以 CustomerManager 的身份登录。在下面的步骤中,您将通过注册窗体创建一个新用户。

  19. 再次单击**“登录”**链接。

  20. 在“登录”对话框中,单击**“立即注册”**链接。

    注册窗体现在将显示。

  21. 填写该注册窗体以便创建新的用户帐户。将以下值用于这个新用户。

    用户名:SalesUser

    友好名称:SalesUser

    电子邮件:someone@example.com

    密码:P@ssword

    安全问题:您的第一辆车什么颜色?

    安全答案:绿色

    RIA_RegisterUser

  22. 单击**“确定”**创建新用户。

    请注意,您现在将以 SalesUser 的身份登录。

  23. 关闭浏览器。

  24. 再次打开 ASP.NET 网站管理工具,然后单击**“安全性”**选项卡。

    请注意,现在有 2 个用户存在于该网站中并且存在 2 个角色,即使您只创建了一个角色。

  25. 单击**“创建或管理角色”**并且注意 Managers 角色和“已注册的用户”。

    “已注册的用户”角色将由业务应用程序模板自动创建。

    RIA_ManageRoles

  26. 对于“已注册的用户”,单击**“管理”**链接。

    请注意,您通过该应用程序添加的名为 SalesUser 的用户现在处于“已注册的用户”角色中。

  27. 关闭 ASP.NET 网站管理工具。

定义访问和配置文件属性

您可以通过将 RequiresAuthenticationAttribute 特性或 RequiresRoleAttribute 特性应用于域操作来限制对域操作的访问。没有特性的域操作对所有用户都可用。将特性应用于域操作不会阻止用户调用域操作;但没有所需凭据的用户将收到异常。

按角色限制显示的数据

  1. 在解决方案资源管理器中,右击服务器项目中的 App_Data 文件夹,选择**“添加”,然后选择“现有项”**。

  2. 在“添加现有项”对话框中,添加 AdventureWorksLT 示例数据库。

  3. 在服务器项目中,添加一个新项并且从**“数据”模板中选择“ADO.NET 实体数据模型”**模板。

  4. 将该模型命名为 AdventureWorksModel.edmx,然后单击**“添加”**。

    此时将出现“实体数据模型向导”。

  5. 选择**“从数据库生成”选项,然后单击“下一步”**。

  6. 选择 AdventureWorksLT 数据库,然后单击**“下一步”**。

  7. 从数据库对象列表中,选择 Customer、Product 和 SalesOrderHeader 表,然后单击**“完成”**。

    实体数据模型将出现在设计器中。

  8. 生成解决方案。

  9. 在服务器项目中,添加一个新项并且从**“Web”模板中选择“域服务类”**模板。

  10. 将域服务命名为 AdventureWorksDomainService,然后单击**“添加”**。

  11. 在“添加新的域服务类”对话框中,选择 Customer、Product 和 SalesOrderHeader 实体。

    RIA_CreateDSForAuth

  12. 单击**“确定”**以完成域服务的创建。

  13. 在 AdventureWorksDomainService 类文件中,将 RequiresAuthenticationAttribute 特性添加到 GetSalesOrderHeader 方法。

    <RequiresAuthentication()> _
    Public Function GetSalesOrderHeaders() As IQueryable(Of SalesOrderHeader)
        Return Me.ObjectContext.SalesOrderHeaders
    End Function
    
    [RequiresAuthentication()]
    public IQueryable<SalesOrderHeader> GetSalesOrderHeaders()
    {
        return this.ObjectContext.SalesOrderHeaders;
    }
    
  14. RequiresRoleAttribute 特性添加到 GetCustomers 方法中,并将所需角色的名称设置为“Managers”。

    <RequiresRole("Managers")> _
    Public Function GetCustomers() As IQueryable(Of Customer)
        Return Me.ObjectContext.Customers
    End Function
    
    [RequiresRole("Managers")]
    public IQueryable<Customer> GetCustomers()
    {
        return this.ObjectContext.Customers;
    }
    

    GetProducts 域操作可用于任何用户,GetSalesOrderHeaders 可用于经身份验证的用户,而 GetCustomers 仅可用于 Managers 角色中的用户。

    下面演示了完整域服务。

    <EnableClientAccess()>  _
    Public Class AdventureWorksDomainService
        Inherits LinqToEntitiesDomainService(Of AdventureWorksLT_DataEntities)
    
        <RequiresRole("Managers")> _
        Public Function GetCustomers() As IQueryable(Of Customer)
            Return Me.ObjectContext.Customers
        End Function
    
        Public Function GetProducts() As IQueryable(Of Product)
            Return Me.ObjectContext.Products
        End Function
    
        <RequiresAuthentication()> _
        Public Function GetSalesOrderHeaders() As IQueryable(Of SalesOrderHeader)
            Return Me.ObjectContext.SalesOrderHeaders
        End Function
    End Class
    
    [EnableClientAccess()]
    public class AdventureWorksDomainService : LinqToEntitiesDomainService<AdventureWorksLT_DataEntities>
    {
        [RequiresRole("Managers")]
        public IQueryable<Customer> GetCustomers()
        {
            return this.ObjectContext.Customers;
        }
    
        public IQueryable<Product> GetProducts()
        {
            return this.ObjectContext.Products;
        }
    
        [RequiresAuthentication()]
        public IQueryable<SalesOrderHeader> GetSalesOrderHeaders()
        {
            return this.ObjectContext.SalesOrderHeaders;
        }
    }
    

您在 Web.config 文件中定义一个配置文件属性。在您向服务器上的 User 类添加该属性时,将为客户端项目生成相应的属性。

添加配置文件属性

  1. 在服务器项目中,打开 Web.config 文件。

  2. <profile> 元素中,添加名为 DefaultRows 的配置文件属性。该属性将包含针对要显示的行数的用户首选项。

    下面的代码显示 Web.config 文件的 profile 节。

    <profile>
      <properties>
        <add name="FriendlyName" />
        <add type="System.Int32" defaultValue="10" name="DefaultRows"/>
      </properties>
    </profile>
    
  3. 保存 Web.config 文件。

  4. 在服务器项目中,展开 Models 文件夹。

  5. 打开 User.cs 或 User.vb 文件,然后添加一个名为 DefaultRows 的属性。

    Imports System.ServiceModel.DomainServices.Server.ApplicationServices
    Imports System.Runtime.Serialization
    Namespace Web
        Partial Public Class User
            Inherits UserBase
    
            Public Property FriendlyName As String
    
            Public Property DefaultRows As Integer
    
        End Class
    End Namespace
    
    namespace ExampleBusinessApplication.Web
    {
        using System.Runtime.Serialization;
        using System.ServiceModel.DomainServices.Server.ApplicationServices;
    
        public partial class User : UserBase
        {
            public string FriendlyName { get; set; }
    
            public int DefaultRows { get; set; }
        }
    }
    

从客户端使用身份验证服务

在使用受限权限调用域操作之前,您应检查用户是否具有所需凭据;否则,将会引发异常。在下一节中,您将检查用户的凭据并且基于用户的凭据填充一到三个 DataGrid 控件。您还将基于该用户配置文件中的属性检索记录的数目。默认值 10 将用于未经身份验证的用户。此部分不包括供用户设置 DefaultRows 配置文件属性的方法,但您将在后面的部分中添加它。

添加 Silverlight 页以显示数据

  1. 在客户端项目中,向 Views 文件夹中添加一个新项。

  2. 选择**“Silverlight 页”**模板并将新页命名为 Reports.xaml。

  3. 打开 MainPage.xaml 文件,然后通过在链接到“关于”页的名为 Link2HyperlinkButton 后添加以下 XAML 来为“报告”页添加链接。

    <Rectangle x:Name="Divider2" Style="{StaticResource DividerStyle}"/>
    
    <HyperlinkButton x:Name="Link3" Style="{StaticResource LinkStyle}" 
           NavigateUri="/Reports" TargetName="ContentFrame" Content="{Binding Path=ApplicationStrings.ReportsPageTitle, Source={StaticResource ResourceWrapper}}"/>
    
  4. 在 Assets\Resources 文件夹中,打开 ApplicationStrings.resx 文件。

  5. 添加一个名为 ReportsPageTitle 且值为 Reports 的新字符串资源。

    RIA_AddReportResource

  6. 保存并关闭 ApplicationStrings.resx 文件。

  7. 打开 Reports.xaml 文件,将以下 XAML 添加到 Grid 元素以便匹配网站中其他页的格式。

    <ScrollViewer x:Name="PageScrollViewer" Style="{StaticResource PageScrollViewerStyle}">
        <StackPanel x:Name="ContentStackPanel" Style="{StaticResource ContentStackPanelStyle}">
            <TextBlock x:Name="HeaderText" Style="{StaticResource HeaderTextStyle}"
                       Text="{Binding Path=ApplicationStrings.ReportsPageTitle, Source={StaticResource ResourceWrapper}}"/>
            <TextBlock x:Name="ContentText" Style="{StaticResource ContentTextStyle}"
                       Text="Display reports based on user permissions"/>
    
        </StackPanel>
    </ScrollViewer>
    
  8. 将三个 DataGrid 控件从**“工具箱”**拖出,然后刚好放置于名为 ContentStackPanel 的堆叠面板的结束标记之前。

    在您从**“工具箱”**拖动 DataGrid 控件时,会将对 System.Windows.Controls.Data 程序集的引用添加到项目中,并会将 System.Windows.Controls 命名空间的前缀添加到该页。

  9. 将这三个 DataGrid 控件分别命名为 ProductsGridSalesOrdersGridCustomersGrid

  10. 对于每个 DataGrid 控件,将 Margin 属性设置为 5

    下面的示例演示了完整的 Reports.xaml 文件。

    <navigation:Page x:Class="ExampleBusinessApplication.Views.Reports"
               xmlns="https://schemas.microsoft.com/winfx/2006/xaml/presentation"
               xmlns:x="https://schemas.microsoft.com/winfx/2006/xaml"
               xmlns:d="https://schemas.microsoft.com/expression/blend/2008"
               xmlns:mc="https://schemas.openxmlformats.org/markup-compatibility/2006"
               xmlns:data="clr-namespace:System.Windows.Controls;assembly=System.Windows.Controls.Data"
               mc:Ignorable="d"
               xmlns:navigation="clr-namespace:System.Windows.Controls;assembly=System.Windows.Controls.Navigation"
               d:DesignWidth="640" d:DesignHeight="480"
               Title="Reports Page" >
        <Grid x:Name="LayoutRoot">
            <ScrollViewer x:Name="PageScrollViewer" Style="{StaticResource PageScrollViewerStyle}">
                <StackPanel x:Name="ContentStackPanel" Style="{StaticResource ContentStackPanelStyle}">
                    <TextBlock x:Name="HeaderText" Style="{StaticResource HeaderTextStyle}"
                               Text="{Binding Path=ApplicationStrings.ReportsPageTitle, Source={StaticResource ResourceWrapper}}"/>
                    <TextBlock x:Name="ContentText" Style="{StaticResource ContentTextStyle}"
                               Text="Display reports based on user permissions"/>
                    <data:DataGrid Name="ProductsGrid" Margin="5" />
                    <data:DataGrid Name="SalesOrdersGrid" Margin="5" />
                    <data:DataGrid Name="CustomersGrid" Margin="5" />
                </StackPanel>
            </ScrollViewer>
        </Grid>
    </navigation:Page>
    
  11. 打开 Reports.xaml.cs 或 Reports.xaml.vb 文件。

  12. 对于 C#,为 System.ServiceModel.DomainServices.ClientSystem.ServiceModel.DomainServices.Client.ApplicationServicesExampleBusinessApplication.Web 命名空间添加 using 语句。对于 Visual Basic,为 System.ServiceModel.DomainServices.ClientSystem.ServiceModel.DomainServices.Client.ApplicationServicesSystem.Windows.ControlsExampleBusinessApplication.Web 命名空间添加 Imports 语句。

  13. 创建名为 AdventureWorksDomainContext 的上下文实例,并创建一个名为 numberOfRows 的变量,该变量包含要检索的行数。

    Private context As New AdventureWorksDomainContext
    Private numberOfRows As Integer = 10
    
    private AdventureWorksDomainContext context = new AdventureWorksDomainContext();
    int numberOfRows = 10;
    
  14. 添加一个名为 LoadRestrictedReports 的方法,该方法调用 GetSalesOrderHeaderQuery 方法和 GetCustomersQuery 方法;如果用户属于 Managers 角色,则用结果填充相应的数据网格。

    如果您在用户不具有所需凭据的情况下调用某个域操作,则该域操作将返回一个异常。您可以通过在调用域操作之前检查凭据来避免发生此情况。

    Private Sub LoadRestrictedReports()
        Dim loadSales = context.Load(context.GetSalesOrderHeadersQuery().Take(numberOfRows))
        SalesOrdersGrid.ItemsSource = loadSales.Entities
        SalesOrdersGrid.Visibility = System.Windows.Visibility.Visible
    
        If (WebContext.Current.User.IsInRole("Managers")) Then
            Dim loadCustomers = context.Load(context.GetCustomersQuery().Take(numberOfRows))
            CustomersGrid.ItemsSource = loadCustomers.Entities
            CustomersGrid.Visibility = System.Windows.Visibility.Visible
        Else
            CustomersGrid.Visibility = System.Windows.Visibility.Collapsed
        End If
    End Sub
    
    private void LoadRestrictedReports()
    {
        LoadOperation<SalesOrderHeader> loadSales = context.Load(context.GetSalesOrderHeadersQuery().Take(numberOfRows));
        SalesOrdersGrid.ItemsSource = loadSales.Entities;
        SalesOrdersGrid.Visibility = System.Windows.Visibility.Visible;
    
        if (WebContext.Current.User.IsInRole("Managers"))
        {
            LoadOperation<Customer> loadCustomers = context.Load(context.GetCustomersQuery().Take(numberOfRows));
            CustomersGrid.ItemsSource = loadCustomers.Entities;
            CustomersGrid.Visibility = System.Windows.Visibility.Visible;
        }
        else
        {
            CustomersGrid.Visibility = System.Windows.Visibility.Collapsed;
        }
    }
    
  15. 添加一个名为 LoadReports 的方法,该方法检查是否对用户进行了身份验证;如果对用户进行了身份验证,则调用 LoadRestrictedReports 方法。它还检索名为 DefaultRows 的配置文件属性,并为 User 对象上的 PropertyChanged 事件添加事件处理程序。最后,它为所有用户调用 GetProductsQuery 方法。

    Private Sub LoadReports()
        If (WebContext.Current.User.IsAuthenticated) Then
            numberOfRows = WebContext.Current.User.DefaultRows
            AddHandler WebContext.Current.User.PropertyChanged, AddressOf User_PropertyChanged
            LoadRestrictedReports()
        Else
            CustomersGrid.Visibility = System.Windows.Visibility.Collapsed
            SalesOrdersGrid.Visibility = System.Windows.Visibility.Collapsed
        End If
    
        Dim loadProducts = context.Load(context.GetProductsQuery().Take(numberOfRows))
        ProductsGrid.ItemsSource = loadProducts.Entities
    End Sub
    
    private void LoadReports()
    {
        if (WebContext.Current.User.IsAuthenticated)
        {
            numberOfRows = WebContext.Current.User.DefaultRows;
            WebContext.Current.User.PropertyChanged += new System.ComponentModel.PropertyChangedEventHandler(User_PropertyChanged);
            LoadRestrictedReports();
        }
        else
        {
            CustomersGrid.Visibility = System.Windows.Visibility.Collapsed;
            SalesOrdersGrid.Visibility = System.Windows.Visibility.Collapsed;
        }
    
        LoadOperation<Product> loadProducts = context.Load(context.GetProductsQuery().Take(numberOfRows));
        ProductsGrid.ItemsSource = loadProducts.Entities;
    }
    
  16. 如果属性 DefaultRows 已发生更改,则为调用 LoadReportsPropertyChanged 事件添加事件处理程序。

    Private Sub User_PropertyChanged(ByVal sender As Object, ByVal e As System.ComponentModel.PropertyChangedEventArgs)
        If (e.PropertyName = "DefaultRows") Then
            LoadReports()
        End If
    End Sub
    
    void User_PropertyChanged(object sender, System.ComponentModel.PropertyChangedEventArgs e)
    {
        if (e.PropertyName == "DefaultRows")
        {
            LoadReports();
        }
    }
    
  17. 为基于用户身份验证凭据中的更改而加载或隐藏数据的 LoggedInLoggedOut 事件添加事件处理程序。

    Private Sub Authentication_LoggedIn(ByVal sender As Object, ByVal e As ApplicationServices.AuthenticationEventArgs)
        LoadReports()
    End Sub
    
    Private Sub Authentication_LoggedOut(ByVal sender As Object, ByVal e As ApplicationServices.AuthenticationEventArgs)
        CustomersGrid.Visibility = System.Windows.Visibility.Collapsed
        SalesOrdersGrid.Visibility = System.Windows.Visibility.Collapsed
    End Sub
    
    void Authentication_LoggedIn(object sender, AuthenticationEventArgs e)
    {
        LoadReports();
    }
    
    void Authentication_LoggedOut(object sender, AuthenticationEventArgs e)
    {
        CustomersGrid.Visibility = System.Windows.Visibility.Collapsed;
        SalesOrdersGrid.Visibility = System.Windows.Visibility.Collapsed;
    }
    
  18. 将下面的代码添加到构造函数中。此代码加载处理程序并调用 LoadReports

    Public Sub New()
        InitializeComponent()
    
        Me.Title = ApplicationStrings.ReportsPageTitle
    
        AddHandler WebContext.Current.Authentication.LoggedIn, AddressOf Authentication_LoggedIn
        AddHandler WebContext.Current.Authentication.LoggedOut, AddressOf Authentication_LoggedOut
    
        LoadReports()
    End Sub
    
    public Reports()
    {
        InitializeComponent();
    
        this.Title = ApplicationStrings.ReportsPageTitle;
    
        WebContext.Current.Authentication.LoggedIn += new System.EventHandler<AuthenticationEventArgs>(Authentication_LoggedIn);
        WebContext.Current.Authentication.LoggedOut += new System.EventHandler<AuthenticationEventArgs>(Authentication_LoggedOut);
    
        LoadReports();
    }
    

    下面演示了完整的代码文件。

    Imports System.Windows.Navigation
    Imports System.Windows.Controls
    Imports System.ServiceModel.DomainServices.Client
    Imports System.ServiceModel.DomainServices.Client.ApplicationServices
    Imports ExampleBusinessApplication.Web
    
    Partial Public Class Reports
        Inherits Page
    
        Private context As New AdventureWorksDomainContext
        Private numberOfRows As Integer = 10
    
        Public Sub New()
            InitializeComponent()
    
            Me.Title = ApplicationStrings.ReportsPageTitle
    
            AddHandler WebContext.Current.Authentication.LoggedIn, AddressOf Authentication_LoggedIn
            AddHandler WebContext.Current.Authentication.LoggedOut, AddressOf Authentication_LoggedOut
    
            LoadReports()
        End Sub
    
        Private Sub LoadReports()
            If (WebContext.Current.User.IsAuthenticated) Then
                numberOfRows = WebContext.Current.User.DefaultRows
                AddHandler WebContext.Current.User.PropertyChanged, AddressOf User_PropertyChanged
                LoadRestrictedReports()
            Else
                CustomersGrid.Visibility = System.Windows.Visibility.Collapsed
                SalesOrdersGrid.Visibility = System.Windows.Visibility.Collapsed
            End If
    
            Dim loadProducts = context.Load(context.GetProductsQuery().Take(numberOfRows))
            ProductsGrid.ItemsSource = loadProducts.Entities
        End Sub
    
        Private Sub LoadRestrictedReports()
            Dim loadSales = context.Load(context.GetSalesOrderHeadersQuery().Take(numberOfRows))
            SalesOrdersGrid.ItemsSource = loadSales.Entities
            SalesOrdersGrid.Visibility = System.Windows.Visibility.Visible
    
            If (WebContext.Current.User.IsInRole("Managers")) Then
                Dim loadCustomers = context.Load(context.GetCustomersQuery().Take(numberOfRows))
                CustomersGrid.ItemsSource = loadCustomers.Entities
                CustomersGrid.Visibility = System.Windows.Visibility.Visible
            Else
                CustomersGrid.Visibility = System.Windows.Visibility.Collapsed
            End If
        End Sub
    
        Private Sub User_PropertyChanged(ByVal sender As Object, ByVal e As System.ComponentModel.PropertyChangedEventArgs)
            If (e.PropertyName = "DefaultRows") Then
                LoadReports()
            End If
        End Sub
    
        Private Sub Authentication_LoggedIn(ByVal sender As Object, ByVal e As ApplicationServices.AuthenticationEventArgs)
            LoadReports()
        End Sub
    
        Private Sub Authentication_LoggedOut(ByVal sender As Object, ByVal e As ApplicationServices.AuthenticationEventArgs)
            CustomersGrid.Visibility = System.Windows.Visibility.Collapsed
            SalesOrdersGrid.Visibility = System.Windows.Visibility.Collapsed
        End Sub
    End Class
    
    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Net;
    using System.Windows;
    using System.Windows.Controls;
    using System.Windows.Documents;
    using System.Windows.Input;
    using System.Windows.Media;
    using System.Windows.Media.Animation;
    using System.Windows.Shapes;
    using System.Windows.Navigation;
    using System.ServiceModel.DomainServices.Client;
    using System.ServiceModel.DomainServices.Client.ApplicationServices;
    using ExampleBusinessApplication.Web;
    
    namespace ExampleBusinessApplication.Views
    {
        public partial class Reports : Page
        {
            private AdventureWorksDomainContext context = new AdventureWorksDomainContext();
            int numberOfRows = 10;
    
            public Reports()
            {
                InitializeComponent();
    
                this.Title = ApplicationStrings.ReportsPageTitle;
    
                WebContext.Current.Authentication.LoggedIn += new System.EventHandler<AuthenticationEventArgs>(Authentication_LoggedIn);
                WebContext.Current.Authentication.LoggedOut += new System.EventHandler<AuthenticationEventArgs>(Authentication_LoggedOut);
    
                LoadReports();
            }
    
            private void LoadReports()
            {
                if (WebContext.Current.User.IsAuthenticated)
                {
                    numberOfRows = WebContext.Current.User.DefaultRows;
                    WebContext.Current.User.PropertyChanged += new System.ComponentModel.PropertyChangedEventHandler(User_PropertyChanged);
                    LoadRestrictedReports();
                }
                else
                {
                    CustomersGrid.Visibility = System.Windows.Visibility.Collapsed;
                    SalesOrdersGrid.Visibility = System.Windows.Visibility.Collapsed;
                }
    
                LoadOperation<Product> loadProducts = context.Load(context.GetProductsQuery().Take(numberOfRows));
                ProductsGrid.ItemsSource = loadProducts.Entities;
            }
    
            private void LoadRestrictedReports()
            {
                LoadOperation<SalesOrderHeader> loadSales = context.Load(context.GetSalesOrderHeadersQuery().Take(numberOfRows));
                SalesOrdersGrid.ItemsSource = loadSales.Entities;
                SalesOrdersGrid.Visibility = System.Windows.Visibility.Visible;
    
                if (WebContext.Current.User.IsInRole("Managers"))
                {
                    LoadOperation<Customer> loadCustomers = context.Load(context.GetCustomersQuery().Take(numberOfRows));
                    CustomersGrid.ItemsSource = loadCustomers.Entities;
                    CustomersGrid.Visibility = System.Windows.Visibility.Visible;
                }
                else
                {
                    CustomersGrid.Visibility = System.Windows.Visibility.Collapsed;
                }
            }
    
            void Authentication_LoggedIn(object sender, AuthenticationEventArgs e)
            {
                LoadReports();
            }
    
            void Authentication_LoggedOut(object sender, AuthenticationEventArgs e)
            {
                CustomersGrid.Visibility = System.Windows.Visibility.Collapsed;
                SalesOrdersGrid.Visibility = System.Windows.Visibility.Collapsed;
            }
    
            void User_PropertyChanged(object sender, System.ComponentModel.PropertyChangedEventArgs e)
            {
                if (e.PropertyName == "DefaultRows")
                {
                    LoadReports();
                }
            }
        }
    }
    
  19. 运行该解决方案。

  20. 单击**“报表”**链接。

    请注意,在您未登录时,只有 products 表显示在“报表”页中。

  21. 单击**“登录”**链接并以 SalesUser 的身份登录。

    请注意,将显示产品和销售订单表。

    RIA_DisplayReports

  22. 注销并以 CustomerManager 身份登录。

    请注意,将显示产品、销售订单和客户表。

  23. 关闭 Web 浏览器。

您可以通过添加子窗口,来允许用户编辑 DefaultRows 配置文件属性。在更改该值时,您可调用 SaveUser 方法将该值保存到数据源中。您通过针对当前 WebContext 实例的 User 对象的属性检索当前值。

添加用于设置配置文件属性的窗口

  1. 在客户端项目中,向 Views 文件夹中添加一个新项。

  2. 选择**“Silverlight 子窗口”**模板并且将该子窗口命名为 ProfileWindow.xaml。

    添加子窗口

  3. 单击**“添加”**按钮。

  4. 在 ProfileWindow.xaml 文件中,在 Grid.RowDefinitions 元素的后面添加以下 XAML,以便包括用于选择要在报表中显示的行数的 ComboBox

    <StackPanel Orientation="Horizontal" Grid.Row="0">
        <TextBlock Text="Number of rows to display for reports: "></TextBlock>
        <ComboBox x:Name="defaultRows" Height="20" VerticalAlignment="Top">
            <ComboBoxItem Content="1"></ComboBoxItem>
            <ComboBoxItem Content="2"></ComboBoxItem>
            <ComboBoxItem Content="3"></ComboBoxItem>
            <ComboBoxItem Content="4"></ComboBoxItem>
            <ComboBoxItem Content="5"></ComboBoxItem>
            <ComboBoxItem Content="6"></ComboBoxItem>
            <ComboBoxItem Content="7"></ComboBoxItem>
            <ComboBoxItem Content="8"></ComboBoxItem>
            <ComboBoxItem Content="9"></ComboBoxItem>
            <ComboBoxItem Content="10"></ComboBoxItem>
            <ComboBoxItem Content="15"></ComboBoxItem>
            <ComboBoxItem Content="20"></ComboBoxItem>
        </ComboBox>
    </StackPanel>
    
  5. 将针对 ChildWindowTitle 属性设置为 Select Preferences

  6. 在 ProfileWindow.xaml.cs 或 ProfileWindow.xaml.vb 文件中,添加以下代码以便检索和设置配置文件属性。

    Imports System.Windows.Controls
    Imports System.Windows
    
    Partial Public Class ProfileWindow
        Inherits ChildWindow
    
        Public Sub New()
            InitializeComponent()
    
            Dim userDefaultRows = WebContext.Current.User.DefaultRows.ToString()
            For Each cbi As ComboBoxItem In defaultRows.Items
                If (cbi.Content.ToString() = userDefaultRows) Then
                    defaultRows.SelectedItem = cbi
                    Exit For
                End If
            Next
        End Sub
    
        Private Sub OKButton_Click(ByVal sender As Object, ByVal e As RoutedEventArgs) Handles OKButton.Click
            Dim newSelection = Integer.Parse(defaultRows.SelectionBoxItem.ToString())
            If (newSelection <> WebContext.Current.User.DefaultRows) Then
                WebContext.Current.User.DefaultRows = newSelection
                WebContext.Current.Authentication.SaveUser(True)
            End If
            Me.DialogResult = True
        End Sub
    
        Private Sub CancelButton_Click(ByVal sender As Object, ByVal e As RoutedEventArgs) Handles CancelButton.Click
            Me.DialogResult = False
        End Sub
    
    End Class
    
    public partial class ProfileWindow : ChildWindow
    {
        public ProfileWindow()
        {
            InitializeComponent();
    
            string userDefaultRows = WebContext.Current.User.DefaultRows.ToString();
            foreach (ComboBoxItem cbi in defaultRows.Items)
            {
                if (cbi.Content.ToString() == userDefaultRows)
                {
                    defaultRows.SelectedItem = cbi;
                    break;
                }
            }
        }
    
        private void OKButton_Click(object sender, RoutedEventArgs e)
        {
            int newSelection = int.Parse(defaultRows.SelectionBoxItem.ToString());
            if (newSelection != WebContext.Current.User.DefaultRows)
            {
                WebContext.Current.User.DefaultRows = newSelection;
                WebContext.Current.Authentication.SaveUser(true);
            }
            this.DialogResult = true;
        }
    
        private void CancelButton_Click(object sender, RoutedEventArgs e)
        {
            this.DialogResult = false;
        }
    }
    
  7. 对于 Visual Basic,为 System.Windows.ControlsSystem.Windows 命名空间添加 Imports 语句。

  8. 展开 Views\Login 文件夹,然后打开 LoginStatus.xaml 文件。

  9. 若要添加指向配置文件窗口的设置链接,请在注销按钮的前面添加以下 XAML。

    <Button x:Name="SettingsButton" Click="SettingsButton_Click" Content="settings" Style="{StaticResource LoginRegisterLinkStyle}" Margin="0,0,0,0"></Button>
    <TextBlock Text="  |  " Style="{StaticResource SpacerStyle}"/>
    
  10. 在 LoginStatus.xaml.cs 或 LoginStatus.xaml.vb 文件中,添加用于该设置链接的以下单击事件处理程序。

    Private Sub SettingsButton_Click(ByVal sender As Object, ByVal e As RoutedEventArgs)
        Dim settingsWindow As New ProfileWindow
        settingsWindow.Show()
    End Sub
    
    private void SettingsButton_Click(object sender, RoutedEventArgs e)
    {
        ExampleBusinessApplication.Views.ProfileWindow settingsWindow = new ExampleBusinessApplication.Views.ProfileWindow();
        settingsWindow.Show();
    }
    
  11. 运行该解决方案。

  12. 以 CustomerManager 或 SalesUser 的身份登录,并且注意登录状态栏现在包含用于设置的链接。

    RIA_NewLoginStatusBar

  13. 单击**“设置”**链接并且设置要为报表显示的默认行数。

    RIA_ShowProfileSettings

  14. 打开“报表”页并且注意 DataGrid 现在包含您选择的行数。