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

在本演练中,您将了解如何在 WCF RIA Services 解决方案中设置服务器和客户端项目以使用身份验证服务。在使用 Silverlight 导航应用程序模板创建解决方案并启用 RIA Services 时,可通过添加身份验证服务来访问 ASP.NET 成员资格框架。身份验证服务可向客户端项目公开服务器项目中的身份验证、角色和配置文件。使用身份验证服务可验证用户凭据、基于角色限制对资源的访问以及存储配置文件属性。

note注意:
Silverlight 业务应用程序会自动实现身份验证服务。有关更多信息,请参见演练:将身份验证服务用于 Silverlight 业务应用程序

必备条件

除 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 应用程序中的身份验证服务,您必须在服务器项目上配置身份验证。在 Web.config 文件中配置身份验证。在配置身份验证后,如果您需要使用 Silverlight 应用程序中的那些功能,则还可以在服务器项目上配置角色和配置文件。在本演练中,您将配置所有三个功能。最后,向客户端添加用于公开已启用的功能的身份验证服务。

配置服务器项目

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

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

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

  3. 选择**“Silverlight 导航应用程序”**模板,并将应用程序命名为 ExampleNavigationApplication

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

    此时将出现**“新建 Silverlight 应用程序”**对话框。

  5. 确保已选中**“在新网站中承载 Silverlight 应用程序”复选框,并将新 Web 项目类型设置为“ASP.NET Web 应用程序项目”**。

  6. 选中**“启用 WCF RIA Services”**复选框。

  7. 单击**“确定”**创建解决方案。

  8. 在服务器项目 (ExampleBusinessApplication.Web) 中,打开 Web.config 文件。

  9. <system.web> 元素中,添加一个 <authentication> 元素,并将 mode 属性设置为“Forms”。

    <authentication mode="Forms"></authentication>
    
  10. <system.web> 元素中,添加一个 <roleManager> 元素,并将 enabled 属性设置为 true

    <roleManager enabled="true"></roleManager>
    
  11. <system.web> 元素中,添加一个 <profile> 元素,并将 enabled 属性设置为 true,还包含一个名为 DefaultRows 的 profile 属性。

    <profile enabled="true">
      <properties>
        <add type="System.Int32" defaultValue="10" name="DefaultRows"/>
      </properties>
    </profile>
    

    完整的 <system.web> 元素应包含以下元素。

    <system.web>
      <compilation debug="true" targetFramework="4.0" />
      <authentication mode="Forms"></authentication>
      <roleManager enabled="true"></roleManager>
      <profile enabled="true">
        <properties>
          <add type="System.Int32" defaultValue="10" name="DefaultRows"/>
        </properties>
      </profile>
    </system.web>
    
  12. 保存 Web.config 文件。

  13. 在**“解决方案资源管理器”中,右击服务器项目,选择“添加”,然后选择“新建项”**。

    此时将出现**“添加新项”**对话框。

  14. 选择**“身份验证域服务”**模板,并将其命名为 AuthenticationDomainService

    RIA_ServicesAddAuth

  15. 单击**“添加”**。

  16. 打开身份验证服务代码文件(AuthenticationDomainService.cs 或 AuthenticationDomainService.vb),并将在 Web.config 文件中定义的 DefaultRows 属性添加到 User 类。

    <EnableClientAccess()> _
    Public Class AuthenticationDomainService
        Inherits AuthenticationBase(Of User)
    
    End Class
    
    Public Class User
        Inherits UserBase
    
        Public Property DefaultRows As Integer
    
    End Class
    
    [EnableClientAccess]
    public class AuthenticationDomainService : AuthenticationBase<User>
    {
    }
    
    public class User : UserBase
    {
        public int DefaultRows { get; set; }
    }
    
  17. 生成解决方案。

在此部分中,您将使用 ASP.NET 网站管理工具来创建用户和角色。在后面的部分中,您将以此用户身份登录。

使用 ASP.NET 网站管理工具添加用户

  1. 若要打开 ASP.NET 网站管理工具,请先在**“解决方案资源管理器”**中选择服务器项目。

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

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

    RIA_OpenAdminTool

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

    RIA_WebAdminSecurity

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

  5. 添加一个名为 Managers 的新角色,然后单击**“添加角色”**按钮。

    WebAdmin_CreateRole

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

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

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

    用户名:CustomerManager

    密码:P@ssword

    电子邮件:someone@example.com

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

    安全答案:蓝色

    Managers 角色:已选定

    WebAdmin_CreateUser

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

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

配置身份验证的客户端

您必须将客户端项目配置为使用身份验证模式,此模式与您在服务器项目中配置的身份验证模式匹配。

配置客户端项目

  1. 在客户端项目中,打开 App.xaml 文件的代码隐藏文件(App.xaml.cs 或 App.xaml.vb)。

  2. 在构造函数中,创建 WebContext 类的新实例。

  3. Authentication 属性设置为 FormsAuthentication 类的新实例,并将 WebContext 实例添加到 ApplicationLifetimeObjects

    Public Sub New()
        InitializeComponent()
    
        Dim webcontext As New WebContext
        webcontext.Authentication = New System.ServiceModel.DomainServices.Client.ApplicationServices.FormsAuthentication
        Me.ApplicationLifetimeObjects.Add(webcontext)
    End Sub
    
    public App()
    {
        this.Startup += this.Application_Startup;
        this.UnhandledException += this.Application_UnhandledException;
    
        InitializeComponent();
    
        WebContext webcontext = new WebContext();
        webcontext.Authentication = new System.ServiceModel.DomainServices.Client.ApplicationServices.FormsAuthentication();
        this.ApplicationLifetimeObjects.Add(webcontext);
    }
    

向客户端添加登录功能

在此部分中,您将添加 Silverlight 控件,用户可利用这些控件来提供用于登录的用户名和密码。您将使用用户凭据添加调用 Login 方法的代码。您还需要根据用户是否已登录来设置可见控件。

为简单起见,本演练中会将登录用户界面添加到主页。您可能需要在应用程序中创建一个单独登录页面。

登录和注销用户

  1. 在**“解决方案资源管理器”**中,在客户端项目中展开 Views 文件夹。

  2. 打开 Home.xaml 文件。

  3. 在名为 ContentTextTextBlock 的后,添加以下 XAML。

    此 XAML 包含一个用于提供用户名的 TextBox、一个用于提供密码的 PasswordBox、一个用于提交登录请求的 Button、一个 TextBlock 以及一个用于注销的 HyperlinkButton,这些项只在用户登录后显示。

    <TextBlock x:Name="WelcomeText" Style="{StaticResource ContentTextStyle}" Visibility="Collapsed"></TextBlock>
    <HyperlinkButton x:Name="LogoutButton" 
                     Content="Logout" 
                     Click="LogoutButton_Click" 
                     Visibility="Collapsed">
    </HyperlinkButton>
    <Border x:Name="LoginBorder" 
            Margin="10,10,0,0" 
            BorderThickness="2" 
            BorderBrush="Black" 
            HorizontalAlignment="Left" 
            CornerRadius="15" 
            Padding="10" 
            Background="BlanchedAlmond" 
            Width="300">
        <Grid HorizontalAlignment="Left">
            <Grid.RowDefinitions>
                <RowDefinition></RowDefinition>
                <RowDefinition Height="30" ></RowDefinition>
                <RowDefinition Height="30"></RowDefinition>
                <RowDefinition></RowDefinition>
                <RowDefinition></RowDefinition>
            </Grid.RowDefinitions>
            <Grid.ColumnDefinitions>
                <ColumnDefinition></ColumnDefinition>
                <ColumnDefinition></ColumnDefinition>
            </Grid.ColumnDefinitions>
            <TextBlock Grid.Row="0" 
                       Grid.ColumnSpan="2" 
                       Grid.Column="0" 
                       FontWeight="Bold" 
                       HorizontalAlignment="Left" 
                       VerticalAlignment="Center" 
                       Text="Log In Existing User">
            </TextBlock>
            <TextBlock Grid.Row="1" 
                       Grid.Column="0" 
                       HorizontalAlignment="Right" 
                       VerticalAlignment="Center" 
                       Text="User Name: ">
            </TextBlock>
            <TextBox x:Name="UserName" 
                     VerticalAlignment="Center" 
                     Grid.Row="1" 
                     Grid.Column="1"  
                     Width="100">
            </TextBox>
            <TextBlock Grid.Row="2" 
                       HorizontalAlignment="Right" 
                       Grid.Column="0" 
                       VerticalAlignment="Center" 
                       Text="Password: ">
            </TextBlock>
            <PasswordBox x:Name="Password" 
                         VerticalAlignment="Center" 
                         Grid.Row="2" 
                         Grid.Column="1"  
                         Width="100">
            </PasswordBox>
            <TextBlock x:Name="LoginResult" 
                       TextWrapping="Wrap" 
                       Visibility="Collapsed" 
                       Grid.Row="3" 
                       Grid.ColumnSpan="2" 
                       Foreground="Red">
            </TextBlock>
            <Button x:Name="LoginButton" 
                    Margin="0,5,0,0" 
                    Grid.Row="4" 
                    Grid.Column="1" 
                    Content="Log In" 
                    Click="LoginButton_Click">
            </Button>
        </Grid>
    </Border>
    
  4. 打开主页的代码隐藏文件(Home.xaml.cs 或 Home.xaml.vb)。

  5. System.ServiceModel.DomainServices.Client.ApplicationServices 命名空间添加 usingImports 语句。

  6. 将下面的代码添加到 Home 类。

    此代码包含用于登录和注销的事件处理程序、已完成登录和注销操作的回调方法以及基于用户是否已经身份验证来设置控件的可见性的方法。

    Protected Overrides Sub OnNavigatedTo(ByVal e As System.Windows.Navigation.NavigationEventArgs)
        SetControlVisibility(WebContext.Current.User.IsAuthenticated)
    End Sub
    
    Private Sub LoginButton_Click(ByVal sender As Object, ByVal e As RoutedEventArgs)
        Dim lp As LoginParameters = New LoginParameters(UserName.Text, Password.Password)
        WebContext.Current.Authentication.Login(lp, AddressOf Me.LoginOperation_Completed, Nothing)
        LoginButton.IsEnabled = False
        LoginResult.Text = ""
    End Sub
    
    Private Sub LoginOperation_Completed(ByVal lo As LoginOperation)
        If (lo.HasError) Then
            LoginResult.Text = lo.Error.Message
            LoginResult.Visibility = System.Windows.Visibility.Visible
            lo.MarkErrorAsHandled()
        ElseIf (lo.LoginSuccess = False) Then
            LoginResult.Text = "Login failed. Please check user name and password."
            LoginResult.Visibility = System.Windows.Visibility.Visible
        ElseIf (lo.LoginSuccess = True) Then
            SetControlVisibility(True)
        End If
        LoginButton.IsEnabled = True
    End Sub
    
    Private Sub SetControlVisibility(ByVal isAuthenticated As Boolean)
        If (isAuthenticated) Then
            LoginBorder.Visibility = System.Windows.Visibility.Collapsed
            WelcomeText.Text = "Welcome " + WebContext.Current.User.Name
            WelcomeText.Visibility = System.Windows.Visibility.Visible
            LogoutButton.Visibility = System.Windows.Visibility.Visible
        Else
            LoginBorder.Visibility = System.Windows.Visibility.Visible
            WelcomeText.Visibility = System.Windows.Visibility.Collapsed
            LogoutButton.Visibility = System.Windows.Visibility.Collapsed
        End If
    End Sub
    
    Private Sub LogoutButton_Click(ByVal sender As Object, ByVal e As RoutedEventArgs)
        WebContext.Current.Authentication.Logout(AddressOf Me.LogoutOperation_Completed, Nothing)
    End Sub
    
    Private Sub LogoutOperation_Completed(ByVal lo As LogoutOperation)
        If (Not (lo.HasError)) Then
            SetControlVisibility(False)
        Else
            Dim ew As ErrorWindow = New ErrorWindow("Logout failed.", "Please try logging out again.")
            ew.Show()
            lo.MarkErrorAsHandled()
        End If
    End Sub
    
    protected override void OnNavigatedTo(NavigationEventArgs e)
    {
        SetControlVisibility(WebContext.Current.User.IsAuthenticated);
    }
    
    private void LoginButton_Click(object sender, RoutedEventArgs e)
    {
        LoginParameters lp = new LoginParameters(UserName.Text, Password.Password);
        WebContext.Current.Authentication.Login(lp, this.LoginOperation_Completed, null);
        LoginButton.IsEnabled = false;
        LoginResult.Text = "";
    }
    
    private void LoginOperation_Completed(LoginOperation lo)
    {
        if (lo.HasError)
        {
            LoginResult.Text = lo.Error.Message;
            LoginResult.Visibility = System.Windows.Visibility.Visible;
            lo.MarkErrorAsHandled();
        }
        else if (lo.LoginSuccess == false)
        {
            LoginResult.Text = "Login failed. Please check user name and password.";
            LoginResult.Visibility = System.Windows.Visibility.Visible;
        }
        else if (lo.LoginSuccess == true)
        {
            SetControlVisibility(true);
        }
        LoginButton.IsEnabled = true;
    }
    
    private void SetControlVisibility(bool isAuthenticated)
    {
        if (isAuthenticated)
        {
            LoginBorder.Visibility = System.Windows.Visibility.Collapsed;
            WelcomeText.Text = "Welcome " + WebContext.Current.User.Name;
            WelcomeText.Visibility = System.Windows.Visibility.Visible;
            LogoutButton.Visibility = System.Windows.Visibility.Visible;
        }
        else
        {
            LoginBorder.Visibility = System.Windows.Visibility.Visible;
            WelcomeText.Visibility = System.Windows.Visibility.Collapsed;
            LogoutButton.Visibility = System.Windows.Visibility.Collapsed;
        }
    }
    
    private void LogoutButton_Click(object sender, RoutedEventArgs e)
    {
        WebContext.Current.Authentication.Logout(this.LogoutOperation_Completed, null);
    }
    
    private void LogoutOperation_Completed(LogoutOperation lo)
    {
    
        if (!lo.HasError)
        {
            SetControlVisibility(false);
        }
        else
        {
            ErrorWindow ew = new ErrorWindow("Logout failed.", "Please try logging out again.");
            ew.Show();
            lo.MarkErrorAsHandled();
        }
    }
    
  7. 运行该解决方案。

    RS_CustomLoginNav

  8. 以 CustomerManager 身份登录,密码为 P@ssword。

    请注意,将不再显示登录区域,而显示欢迎文本和注销链接。

    RIA_CustomLoggedInNav

  9. 单击**“注销”**链接并关闭 Web 浏览器。

从客户端添加新用户

此身份验证服务不包含用于创建新用户的操作。若要注册新用户,请创建一个空域服务,并添加一个用于将用户添加到 ASP.NET 成员资格框架的操作。

配置用来添加新用户的服务器项目

  1. 在此服务器项目中,添加一个名为 NewUser 的新类文件。

  2. 通过将以下代码添加到 NewUser 类来定义用于注册新用户的属性。

    Imports System.ComponentModel.DataAnnotations
    
    Public Class NewUser
    
        <Key()> _
        <Required()> _
        <RegularExpression("^[a-zA-Z0-9_]*$", ErrorMessage:="Invalid user name. It must contain only alphanumeric characters")> _
        Public Property UserName As String
    
        <Key()> _
        <Required()> _
        <RegularExpression("^([\w-\.]+)@((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.)|(([\w-]+\.)+))([a-zA-Z]{2,4}|[0-9]{1,3})(\]?)$", ErrorMessage:="Invalid email. An email must use the format username@mycompany.com")> _
        Public Property Email As String
    
        <Required()> _
        <RegularExpression("^.*[^a-zA-Z0-9].*$", ErrorMessage:="A password needs to contain at least one special character e.g. @ or #")> _
        <StringLength(50, MinimumLength:=7, ErrorMessage:="Invalid password. It must be contain at least 7 characters and no more than 50 characters.")> _
        Public Property Password As String
    
        <Required()> _
        <CustomValidation(GetType(RegistrationValidator), "IsPasswordConfirmed")> _
        Public Property ConfirmPassword As String
    
        <Range(1, 20)> _
        Public Property RecordsToShow As Integer
    
        <Required()> _
        Public Property SecurityQuestion As String
    
        <Required()> _
        Public Property SecurityAnswer As String
    End Class
    
    using System;
    using System.ComponentModel.DataAnnotations;
    
    namespace ExampleNavigationApplication.Web
    {
        public class NewUser
        {
            [Key]
            [Required()]
            [RegularExpression("^[a-zA-Z0-9_]*$", ErrorMessage="Invalid user name. It must contain only alphanumeric characters")]
            public string UserName { get; set; }
    
            [Key]
            [Required()]
            [RegularExpression(@"^([\w-\.]+)@((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.)|(([\w-]+\.)+))([a-zA-Z]{2,4}|[0-9]{1,3})(\]?)$", ErrorMessage="Invalid email. An email must use the format username@mycompany.com")]
            public string Email { get; set; }
    
            [Required()]
            [RegularExpression("^.*[^a-zA-Z0-9].*$", ErrorMessage="A password needs to contain at least one special character e.g. @ or #")]
            [StringLength(50, MinimumLength = 7, ErrorMessage="Invalid password. It must be contain at least 7 characters and no more than 50 characters.")]
            public string Password { get; set; }
    
            [Required()]
            [CustomValidation(typeof(RegistrationValidator), "IsPasswordConfirmed")]
            public string ConfirmPassword { get; set; }
    
            [Range(1, 20)]
            public int RecordsToShow { get; set; }
    
            [Required()]
            public string SecurityQuestion { get; set; }
    
            [Required()]
            public string SecurityAnswer { get; set; }
        }
    }
    

    上一步骤中的 ConfirmPassword 属性是使用 CustomValidationAttribute 特性定义的。在此特性中,它指定 RegistrationValidator 类和一个名为 IsPasswordConfirmed 的方法。此时必须定义此自定义验证类。

  3. 添加一个名为 RegistrationValidator.shared.cs 或 RegistrationValidator.shared.vb 的新类。

  4. 将以下代码添加到 RegistrationValidator.shared 文件。

    Imports System.ComponentModel.DataAnnotations
    
    Public Class RegistrationValidator
        Public Shared Function IsPasswordConfirmed(ByVal confirmPassword As String, ByVal context As ValidationContext) As ValidationResult
            Dim data As Web.NewUser = CType(context.ObjectInstance, Web.NewUser)
            If (data.Password = confirmPassword) Then
                Return ValidationResult.Success
            Else
                Return New ValidationResult("Please confirm your password by providing it again.")
            End If
        End Function
    End Class
    
    using System.ComponentModel.DataAnnotations;
    
    namespace ExampleNavigationApplication.Web
    {
        public class RegistrationValidator
        {
            public static ValidationResult IsPasswordConfirmed(string confirmPassword, ValidationContext context)
            {
                NewUser data = (NewUser)context.ObjectInstance;
                if (data.Password == confirmPassword)
                {
                    return ValidationResult.Success;
                }
                else
                {
                    return new ValidationResult("Please confirm your password by providing it again.");
                }
            }
        }
    }
    
  5. 向服务器项目添加一个新项,并选择“域服务类”模板。

  6. 将此文件命名为 RegistrationDomainService.cs 或 RegistrationDomainService.vb,然后单击**“添加”**按钮。

  7. 在“添加新的域服务类”对话框中,从**“可用 DataContexts/ObjectContexts”列表选择“<空域服务类>”**。

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

  9. 若要创建一个通过成员资格框架添加新用户的域操作并保存配置文件属性,请将以下代码添加到 RegistrationDomainService 类。

    必须包含 GetUsers 方法以确保为客户端项目生成 NewUser 实体类。客户端项目中仅生成通过公共查询操作公开的类。

    创建用户后,将设置名为 DefaultRows 的配置文件属性。在此示例中,将配置文件属性设置为用于创建用户的域操作的一部分。在下一个部分中,您将添加代码以从客户端项目设置配置文件属性。

    Option Compare Binary
    Option Infer On
    Option Strict On
    Option Explicit On
    
    Imports System
    Imports System.Collections.Generic
    Imports System.ComponentModel
    Imports System.ComponentModel.DataAnnotations
    Imports System.Linq
    Imports System.ServiceModel.DomainServices.Hosting
    Imports System.ServiceModel.DomainServices.Server
    Imports System.Web.Profile
    
    <EnableClientAccess()>  _
    Public Class RegistrationDomainService
        Inherits DomainService
    
        Public Sub AddUser(ByVal user As NewUser)
            Dim createStatus As MembershipCreateStatus
            Membership.CreateUser(user.UserName, user.Password, user.Email, user.SecurityQuestion, user.SecurityAnswer, True, Nothing, createStatus)
    
            If (createStatus <> MembershipCreateStatus.Success) Then
                Throw New DomainException(createStatus.ToString())
            End If
    
            Dim profile = ProfileBase.Create(user.UserName, True)
            profile.SetPropertyValue("DefaultRows", user.RecordsToShow)
            profile.Save()
        End Sub
    
        Public Function GetUsers() As IEnumerable(Of NewUser)
            Throw New NotSupportedException()
        End Function
    End Class
    
    namespace ExampleNavigationApplication.Web
    {
        using System;
        using System.Collections.Generic;
        using System.ServiceModel.DomainServices.Hosting;
        using System.ServiceModel.DomainServices.Server;
        using System.Web.Security;
        using System.Web.Profile;
    
    
        [EnableClientAccess()]
        public class RegistrationDomainService : DomainService
        {
            public void AddUser(NewUser user)
            {
                MembershipCreateStatus createStatus;
                Membership.CreateUser(user.UserName, user.Password, user.Email, user.SecurityQuestion, user.SecurityAnswer, true, null, out createStatus);
    
                if (createStatus != MembershipCreateStatus.Success)
                {
                    throw new DomainException(createStatus.ToString());
                }
    
                ProfileBase profile = ProfileBase.Create(user.UserName, true);
                profile.SetPropertyValue("DefaultRows", user.RecordsToShow);
                profile.Save();
            }
    
            public IEnumerable<NewUser> GetUsers()
            {
                throw new NotSupportedException();
            }
        }  
    }
    

配置用来添加新用户的客户端项目

  1. 打开 Home.xaml 文件。

  2. LoginBorderBorder 控件的结束标记后面,添加以下 XAML 以创建另一个具有输入控件的 Border 控件,以便收集用于创建新用户的信息。

    这些控件接受用户名、密码、密码确认、电子邮件地址、安全提示问题、安全答案以及报告上显示的报告数所对应的值。显示的记录数将另存为配置文件属性。所有其他值都用于通过 ASP.NET 成员资格框架创建用户。

    <Border x:Name="RegisterBorder" 
            Margin="10,10,0,0" 
            BorderThickness="2" 
            BorderBrush="Black" 
            HorizontalAlignment="Left" 
            CornerRadius="15" 
            Padding="10" 
            Background="BurlyWood" 
            Width="400">
        <Grid HorizontalAlignment="Left">
            <Grid.RowDefinitions>
                <RowDefinition></RowDefinition>
                <RowDefinition Height="30" ></RowDefinition>
                <RowDefinition Height="30"></RowDefinition>
                <RowDefinition Height="30"></RowDefinition>
                <RowDefinition Height="30"></RowDefinition>
                <RowDefinition Height="30"></RowDefinition>
                <RowDefinition Height="30"></RowDefinition>
                <RowDefinition Height="30"></RowDefinition>
                <RowDefinition></RowDefinition>
                <RowDefinition></RowDefinition>
            </Grid.RowDefinitions>
            <Grid.ColumnDefinitions>
                <ColumnDefinition></ColumnDefinition>
                <ColumnDefinition></ColumnDefinition>
            </Grid.ColumnDefinitions>
    
            <TextBlock Grid.Row="0" 
                       Grid.ColumnSpan="2" 
                       Grid.Column="0" 
                       FontWeight="Bold" 
                       HorizontalAlignment="Left" 
                       VerticalAlignment="Center" 
                       Text="Register New User">
            </TextBlock>
            <TextBlock Grid.Row="1" 
                       Grid.Column="0" 
                       HorizontalAlignment="Right" 
                       VerticalAlignment="Center" 
                       Text="User Name: ">
            </TextBlock>
            <TextBox x:Name="NewUsername" 
                     VerticalAlignment="Center" 
                     HorizontalAlignment="Left" 
                     Grid.Row="1" 
                     Grid.Column="1"  
                     Width="100">
            </TextBox>
            <TextBlock Grid.Row="2" 
                       HorizontalAlignment="Right" 
                       Grid.Column="0" 
                       VerticalAlignment="Center" 
                       Text="Password: ">
            </TextBlock>
            <PasswordBox x:Name="NewPassword" 
                         VerticalAlignment="Center" 
                         HorizontalAlignment="Left" 
                         Grid.Row="2" 
                         Grid.Column="1"  
                         Width="100">
            </PasswordBox>
            <TextBlock Grid.Row="3" 
                       HorizontalAlignment="Right" 
                       Grid.Column="0" 
                       VerticalAlignment="Center" 
                       Text="Confirm Password: ">
            </TextBlock>
            <PasswordBox x:Name="NewConfirmPassword" 
                         HorizontalAlignment="Left" 
                         VerticalAlignment="Center" 
                         Grid.Row="3" 
                         Grid.Column="1"  
                         Width="100">
            </PasswordBox>
            <TextBlock Grid.Row="4" 
                       HorizontalAlignment="Right" 
                       Grid.Column="0" 
                       VerticalAlignment="Center" 
                       Text="Email: ">
            </TextBlock>
            <TextBox x:Name="NewEmail" 
                     VerticalAlignment="Center" 
                     Grid.Row="4" 
                     Grid.Column="1"  
                     Width="200">
            </TextBox>
            <TextBlock Grid.Row="5" 
                       HorizontalAlignment="Right" 
                       Grid.Column="0" 
                       VerticalAlignment="Center" 
                       Text="Records to show: ">
            </TextBlock>
            <ComboBox Grid.Row="5" 
                      Grid.Column="1" 
                      x:Name="DefaultRows" 
                      HorizontalAlignment="Left" 
                      Width="50" 
                      Height="20" 
                      VerticalAlignment="Center">
                <ComboBoxItem Content="1"></ComboBoxItem>
                <ComboBoxItem Content="3"></ComboBoxItem>
                <ComboBoxItem Content="5"></ComboBoxItem>
                <ComboBoxItem Content="10" IsSelected="True"></ComboBoxItem>
                <ComboBoxItem Content="15"></ComboBoxItem>
                <ComboBoxItem Content="20"></ComboBoxItem>
            </ComboBox>
            <TextBlock Grid.Row="6" 
                       HorizontalAlignment="Right" 
                       Grid.Column="0" 
                       VerticalAlignment="Center" 
                       Text="Security Question: ">
            </TextBlock>
            <TextBox x:Name="SecurityQuestion" 
                     VerticalAlignment="Center" 
                     Grid.Row="6" 
                     Grid.Column="1"  
                     Width="200">
            </TextBox>
            <TextBlock Grid.Row="7" 
                       HorizontalAlignment="Right" 
                       Grid.Column="0" 
                       VerticalAlignment="Center" 
                       Text="Security Answer: ">
            </TextBlock>
            <TextBox x:Name="SecurityAnswer" 
                     VerticalAlignment="Center" 
                     Grid.Row="7" 
                     Grid.Column="1"  
                     Width="200">
            </TextBox>
            <TextBlock x:Name="registerResult" 
                       TextWrapping="Wrap" 
                       Visibility="Collapsed" 
                       Grid.Row="8" 
                       Grid.ColumnSpan="2" 
                       Foreground="Red">
            </TextBlock>
            <Button x:Name="RegisterButton" 
                    Click="RegisterButton_Click" 
                    Margin="0,5,0,0" 
                    Grid.Row="9" 
                    Grid.Column="1" 
                    Content="Register" >
            </Button>
        </Grid>
    </Border>
    
  3. 打开 Home.xaml.cs(或 Home.xaml.vb)代码隐藏文件。

  4. System.ServiceModel.DomainServices.ClientSystem.ComponentModel.DataAnnotationsExampleNavigationApplication.Web 命名空间添加 usingImports 语句。

  5. 为注册按钮单击事件添加事件处理程序,并为域操作添加回调方法。此回调方法包含用于在成功创建用户帐户后登录用户的代码。

    Private Sub RegisterButton_Click(ByVal sender As Object, ByVal e As RoutedEventArgs)
        RegisterButton.IsEnabled = False
        Dim context = New RegistrationDomainContext()
        Dim nu = New NewUser()
        Try
            nu.UserName = NewUsername.Text
            nu.Password = NewPassword.Password
            nu.Email = NewEmail.Text
            nu.ConfirmPassword = NewConfirmPassword.Password
            nu.RecordsToShow = Integer.Parse(DefaultRows.SelectionBoxItem.ToString())
            nu.SecurityQuestion = SecurityQuestion.Text
            nu.SecurityAnswer = SecurityAnswer.Text
            context.NewUsers.Add(nu)
    
            context.SubmitChanges(AddressOf RegisterUser_Completed, Nothing)
        Catch ve As ValidationException
            registerResult.Visibility = System.Windows.Visibility.Visible
            registerResult.Text = ve.Message
            RegisterButton.IsEnabled = True
        End Try
    End Sub
    
    Private Sub RegisterUser_Completed(ByVal so As SubmitOperation)
        If (so.HasError) Then
            Dim ew = New ErrorWindow("Registration failed.", "Please try registering again.")
            ew.Show()
            so.MarkErrorAsHandled()
        Else
            Dim lp = New LoginParameters(NewUsername.Text, NewPassword.Password)
            WebContext.Current.Authentication.Login(lp, AddressOf Me.LoginOperation_Completed, Nothing)
            NewUsername.Text = ""
            NewPassword.Password = ""
            NewConfirmPassword.Password = ""
            NewEmail.Text = ""
            DefaultRows.SelectedIndex = 0
            SecurityQuestion.Text = ""
            SecurityAnswer.Text = ""
        End If
        RegisterButton.IsEnabled = True
    End Sub
    
    private void RegisterButton_Click(object sender, RoutedEventArgs e)
    {
        RegisterButton.IsEnabled = false;
        RegistrationDomainContext context = new RegistrationDomainContext();
        NewUser nu = new NewUser();
        try
        {
            nu.UserName = NewUsername.Text;
            nu.Password = NewPassword.Password;
            nu.Email = NewEmail.Text;
            nu.ConfirmPassword = NewConfirmPassword.Password;
            nu.RecordsToShow = int.Parse(DefaultRows.SelectionBoxItem.ToString());
            nu.SecurityQuestion = SecurityQuestion.Text;
            nu.SecurityAnswer = SecurityAnswer.Text;
            context.NewUsers.Add(nu);
    
            context.SubmitChanges(RegisterUser_Completed, null);
        }
        catch (ValidationException ve)
        {
            registerResult.Visibility = System.Windows.Visibility.Visible;
            registerResult.Text = ve.Message;
            RegisterButton.IsEnabled = true;
        }
    }
    
    private void RegisterUser_Completed(SubmitOperation so)
    {
        if (so.HasError)
        {
            ErrorWindow ew = new ErrorWindow("Registration failed.", "Please try registering again.");
            ew.Show();
            so.MarkErrorAsHandled();
        }
        else
        {
            LoginParameters lp = new LoginParameters(NewUsername.Text, NewPassword.Password);
            WebContext.Current.Authentication.Login(lp, this.LoginOperation_Completed, null);
            NewUsername.Text = "";
            NewPassword.Password = "";
            NewConfirmPassword.Password = "";
            NewEmail.Text = "";
            DefaultRows.SelectedIndex = 0;
            SecurityQuestion.Text = "";
            SecurityAnswer.Text = "";
        }
        RegisterButton.IsEnabled = true;
    }
    
  6. 修改 SetControlVisibility 方法以设置 RegisterBorder 的可见性,如下面的代码所示。

    Private Sub SetControlVisibility(ByVal isAuthenticated As Boolean)
        If (isAuthenticated) Then
            LoginBorder.Visibility = System.Windows.Visibility.Collapsed
            RegisterBorder.Visibility = Windows.Visibility.Collapsed
            WelcomeText.Text = "Welcome " + WebContext.Current.User.Name
            WelcomeText.Visibility = System.Windows.Visibility.Visible
            LogoutButton.Visibility = System.Windows.Visibility.Visible
        Else
            LoginBorder.Visibility = System.Windows.Visibility.Visible
            RegisterBorder.Visibility = Windows.Visibility.Visible
            WelcomeText.Visibility = System.Windows.Visibility.Collapsed
            LogoutButton.Visibility = System.Windows.Visibility.Collapsed
        End If
    End Sub
    
    private void SetControlVisibility(bool isAuthenticated)
    {
        if (isAuthenticated)
        {
            LoginBorder.Visibility = System.Windows.Visibility.Collapsed;
            RegisterBorder.Visibility = System.Windows.Visibility.Collapsed;
            WelcomeText.Text = "Welcome " + WebContext.Current.User.Name;
            WelcomeText.Visibility = System.Windows.Visibility.Visible;
            LogoutButton.Visibility = System.Windows.Visibility.Visible;
        }
        else
        {
            LoginBorder.Visibility = System.Windows.Visibility.Visible;
            RegisterBorder.Visibility = System.Windows.Visibility.Visible;
            WelcomeText.Visibility = System.Windows.Visibility.Collapsed;
            LogoutButton.Visibility = System.Windows.Visibility.Collapsed;
        }
    }
    

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

    Imports System.ServiceModel.DomainServices.Client.ApplicationServices
    Imports System.ServiceModel.DomainServices.Client
    Imports System.ComponentModel.DataAnnotations
    
    Partial Public Class Home
        Inherits Page
    
        Public Sub New()
            InitializeComponent()
        End Sub
    
        Protected Overrides Sub OnNavigatedTo(ByVal e As System.Windows.Navigation.NavigationEventArgs)
            SetControlVisibility(WebContext.Current.User.IsAuthenticated)
        End Sub
    
        Private Sub LoginButton_Click(ByVal sender As Object, ByVal e As RoutedEventArgs)
            Dim lp As LoginParameters = New LoginParameters(UserName.Text, Password.Password)
            WebContext.Current.Authentication.Login(lp, AddressOf Me.LoginOperation_Completed, Nothing)
            LoginButton.IsEnabled = False
            LoginResult.Text = ""
        End Sub
    
        Private Sub LoginOperation_Completed(ByVal lo As LoginOperation)
            If (lo.HasError) Then
                LoginResult.Text = lo.Error.Message
                LoginResult.Visibility = System.Windows.Visibility.Visible
                lo.MarkErrorAsHandled()
            ElseIf (lo.LoginSuccess = False) Then
                LoginResult.Text = "Login failed. Please check user name and password."
                LoginResult.Visibility = System.Windows.Visibility.Visible
            ElseIf (lo.LoginSuccess = True) Then
                SetControlVisibility(True)
            End If
            LoginButton.IsEnabled = True
        End Sub
    
        Private Sub SetControlVisibility(ByVal isAuthenticated As Boolean)
            If (isAuthenticated) Then
                LoginBorder.Visibility = System.Windows.Visibility.Collapsed
                RegisterBorder.Visibility = Windows.Visibility.Collapsed
                WelcomeText.Text = "Welcome " + WebContext.Current.User.Name
                WelcomeText.Visibility = System.Windows.Visibility.Visible
                LogoutButton.Visibility = System.Windows.Visibility.Visible
            Else
                LoginBorder.Visibility = System.Windows.Visibility.Visible
                RegisterBorder.Visibility = Windows.Visibility.Visible
                WelcomeText.Visibility = System.Windows.Visibility.Collapsed
                LogoutButton.Visibility = System.Windows.Visibility.Collapsed
            End If
        End Sub
    
        Private Sub LogoutButton_Click(ByVal sender As Object, ByVal e As RoutedEventArgs)
            WebContext.Current.Authentication.Logout(AddressOf Me.LogoutOperation_Completed, Nothing)
        End Sub
    
        Private Sub LogoutOperation_Completed(ByVal lo As LogoutOperation)
            If (Not (lo.HasError)) Then
                SetControlVisibility(False)
            Else
                Dim ew As ErrorWindow = New ErrorWindow("Logout failed.", "Please try logging out again.")
                ew.Show()
                lo.MarkErrorAsHandled()
            End If
        End Sub
    
        Private Sub RegisterButton_Click(ByVal sender As Object, ByVal e As RoutedEventArgs)
            RegisterButton.IsEnabled = False
            Dim context = New RegistrationDomainContext()
            Dim nu = New NewUser()
            Try
                nu.UserName = NewUsername.Text
                nu.Password = NewPassword.Password
                nu.Email = NewEmail.Text
                nu.ConfirmPassword = NewConfirmPassword.Password
                nu.RecordsToShow = Integer.Parse(DefaultRows.SelectionBoxItem.ToString())
                nu.SecurityQuestion = SecurityQuestion.Text
                nu.SecurityAnswer = SecurityAnswer.Text
                context.NewUsers.Add(nu)
    
                context.SubmitChanges(AddressOf RegisterUser_Completed, Nothing)
            Catch ve As ValidationException
                registerResult.Visibility = System.Windows.Visibility.Visible
                registerResult.Text = ve.Message
                RegisterButton.IsEnabled = True
            End Try
        End Sub
    
        Private Sub RegisterUser_Completed(ByVal so As SubmitOperation)
            If (so.HasError) Then
                Dim ew = New ErrorWindow("Registration failed.", "Please try registering again.")
                ew.Show()
                so.MarkErrorAsHandled()
            Else
                Dim lp = New LoginParameters(NewUsername.Text, NewPassword.Password)
                WebContext.Current.Authentication.Login(lp, AddressOf Me.LoginOperation_Completed, Nothing)
                NewUsername.Text = ""
                NewPassword.Password = ""
                NewConfirmPassword.Password = ""
                NewEmail.Text = ""
                DefaultRows.SelectedIndex = 0
                SecurityQuestion.Text = ""
                SecurityAnswer.Text = ""
            End If
            RegisterButton.IsEnabled = True
        End Sub
    End Class
    
    using System;
    using System.Windows;
    using System.Windows.Controls;
    using System.Windows.Navigation;
    using System.ServiceModel.DomainServices.Client.ApplicationServices;
    using System.ServiceModel.DomainServices.Client;
    using System.ComponentModel.DataAnnotations;
    using ExampleNavigationApplication.Web;
    
    namespace ExampleNavigationApplication
    {
        public partial class Home : Page
        {
            public Home()
            {
                InitializeComponent();
    
            }
    
            // Executes when the user navigates to this page.
            protected override void OnNavigatedTo(NavigationEventArgs e)
            {
                SetControlVisibility(WebContext.Current.User.IsAuthenticated);
            }
    
            private void LoginButton_Click(object sender, RoutedEventArgs e)
            {
                LoginParameters lp = new LoginParameters(UserName.Text, Password.Password);
                WebContext.Current.Authentication.Login(lp, this.LoginOperation_Completed, null);
                LoginButton.IsEnabled = false;
                LoginResult.Text = "";
            }
    
            private void LoginOperation_Completed(LoginOperation lo)
            {
                if (lo.HasError)
                {
                    LoginResult.Text = lo.Error.Message;
                    LoginResult.Visibility = System.Windows.Visibility.Visible;
                    lo.MarkErrorAsHandled();
                }
                else if (lo.LoginSuccess == false)
                {
                    LoginResult.Text = "Login failed. Please check user name and password.";
                    LoginResult.Visibility = System.Windows.Visibility.Visible;
                }
                else if (lo.LoginSuccess == true)
                {
                    SetControlVisibility(true);
                }
                LoginButton.IsEnabled = true;
            }
    
            private void SetControlVisibility(bool isAuthenticated)
            {
                if (isAuthenticated)
                {
                    LoginBorder.Visibility = System.Windows.Visibility.Collapsed;
                    RegisterBorder.Visibility = System.Windows.Visibility.Collapsed;
                    WelcomeText.Text = "Welcome " + WebContext.Current.User.Name;
                    WelcomeText.Visibility = System.Windows.Visibility.Visible;
                    LogoutButton.Visibility = System.Windows.Visibility.Visible;
                }
                else
                {
                    LoginBorder.Visibility = System.Windows.Visibility.Visible;
                    RegisterBorder.Visibility = System.Windows.Visibility.Visible;
                    WelcomeText.Visibility = System.Windows.Visibility.Collapsed;
                    LogoutButton.Visibility = System.Windows.Visibility.Collapsed;
                }
            }
    
            private void LogoutButton_Click(object sender, RoutedEventArgs e)
            {
                WebContext.Current.Authentication.Logout(this.LogoutOperation_Completed, null);
            }
    
            private void LogoutOperation_Completed(LogoutOperation lo)
            {
    
                if (!lo.HasError)
                {
                    SetControlVisibility(false);
                }
                else
                {
                    ErrorWindow ew = new ErrorWindow("Logout failed.", "Please try logging out again.");
                    ew.Show();
                    lo.MarkErrorAsHandled();
                }
            }
    
            private void RegisterButton_Click(object sender, RoutedEventArgs e)
            {
                RegisterButton.IsEnabled = false;
                RegistrationDomainContext context = new RegistrationDomainContext();
                NewUser nu = new NewUser();
                try
                {
                    nu.UserName = NewUsername.Text;
                    nu.Password = NewPassword.Password;
                    nu.Email = NewEmail.Text;
                    nu.ConfirmPassword = NewConfirmPassword.Password;
                    nu.RecordsToShow = int.Parse(DefaultRows.SelectionBoxItem.ToString());
                    nu.SecurityQuestion = SecurityQuestion.Text;
                    nu.SecurityAnswer = SecurityAnswer.Text;
                    context.NewUsers.Add(nu);
    
                    context.SubmitChanges(RegisterUser_Completed, null);
                }
                catch (ValidationException ve)
                {
                    registerResult.Visibility = System.Windows.Visibility.Visible;
                    registerResult.Text = ve.Message;
                    RegisterButton.IsEnabled = true;
                }
            }
    
            private void RegisterUser_Completed(SubmitOperation so)
            {
                if (so.HasError)
                {
                    ErrorWindow ew = new ErrorWindow("Registration failed.", "Please try registering again.");
                    ew.Show();
                    so.MarkErrorAsHandled();
                }
                else
                {
                    LoginParameters lp = new LoginParameters(NewUsername.Text, NewPassword.Password);
                    WebContext.Current.Authentication.Login(lp, this.LoginOperation_Completed, null);
                    NewUsername.Text = "";
                    NewPassword.Password = "";
                    NewConfirmPassword.Password = "";
                    NewEmail.Text = "";
                    DefaultRows.SelectedIndex = 0;
                    SecurityQuestion.Text = "";
                    SecurityAnswer.Text = "";
                }
                RegisterButton.IsEnabled = true;
            }
        }
    }
    
  7. 运行该解决方案。

    RIA_LoginandRegister

  8. 提供用于注册新用户的值。

  9. 关闭 Web 浏览器。

限制对域操作的访问

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

按角色限制显示的数据

  1. 在**“解决方案资源管理器””中,选择服务器项目,然后单击“显示所有文件”**按钮。

  2. 右击 App_Data 文件夹,然后选择**“包括在项目中”**。

  3. 右击 App_Data 文件夹,选择**“添加”,然后选择“现有项”**。

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

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

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

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

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

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

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

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

  10. 生成解决方案。

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

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

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

    RIA_CreateDSForAuth

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

  15. 在 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;
    }
    
  16. 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;
        }
    }
    

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

在使用受限权限调用域操作之前,您应检查用户是否具有所需凭据;否则,将会引发异常。在此部分中,您将检查用户凭据并基于用户凭据填充一到三个 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="reports"/>
    
  4. 打开 Reports.xaml 文件,然后将以下 XAML 添加到 Grid 标记中,以匹配网站中其他页面的格式,并包含一个 HyperlinkButton 控件,该控件将启动用来编辑配置文件属性的窗口。

    <ScrollViewer x:Name="PageScrollViewer" Style="{StaticResource PageScrollViewerStyle}">
    
        <StackPanel x:Name="ContentStackPanel">
    
            <TextBlock x:Name="HeaderText" 
                       Style="{StaticResource HeaderTextStyle}"
                       Text="Reports"/>
            <TextBlock x:Name="ContentText" 
                       Style="{StaticResource ContentTextStyle}"
                       Text="Display reports based on user permissions"/>
            <HyperlinkButton x:Name="SettingsButton" 
                             Content="Adjust Settings" 
                             Click="SettingsButton_Click" 
                             Visibility="Collapsed">
            </HyperlinkButton>
    
        </StackPanel>
    
    </ScrollViewer>
    
  5. 将三个 DataGrid 控件从**“工具箱”**拖出,然后刚好放置于名为 ContentStackPanel 的堆叠面板的结束标记之前。

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

  6. 命名 DataGrid 控件 ProductsGridSalesOrdersGridCustomersGrid,并将 Margin 设置为 5。

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

    <navigation:Page 
          x:Class="ExampleNavigationApplication.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:data="clr-namespace:System.Windows.Controls;assembly=System.Windows.Controls.Data"
          xmlns:mc="https://schemas.openxmlformats.org/markup-compatibility/2006"
          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">
    
                    <TextBlock x:Name="HeaderText" 
                               Style="{StaticResource HeaderTextStyle}"
                               Text="Reports"/>
                    <TextBlock x:Name="ContentText" 
                               Style="{StaticResource ContentTextStyle}"
                               Text="Display reports based on user permissions"/>
            <HyperlinkButton x:Name="SettingsButton" 
                                     Content="Adjust Settings" 
                                     Click="SettingsButton_Click" 
                                     Visibility="Collapsed">
                    </HyperlinkButton>
                    <data:DataGrid Name="ProductsGrid" Margin="5" />
                    <data:DataGrid Name="SalesOrdersGrid" Margin="5" />
                    <data:DataGrid Name="CustomersGrid" Margin="5" />
                </StackPanel>
    
            </ScrollViewer>
        </Grid>
    </navigation:Page>
    
  7. 打开 Reports.xaml.cs 或 Reports.xaml.vb 文件,并为 System.ServiceModel.DomainServices.ClientExampleNavigationApplication.Web 命名空间添加 usingImports 语句。

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

    Private context As New AdventureWorksDomainContext
    Private numberOfRows As Integer = 10
    
    private AdventureWorksDomainContext context = new AdventureWorksDomainContext();
    int numberOfRows = 10;
    
  9. 添加一个名为 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;
        }
    }
    
  10. 添加一个名为 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;
    }
    
  11. 如果属性 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();
        }
    }
    
  12. 将下面的代码添加到构造函数中。此代码将加载报告,并使设置链接对经过身份验证的用户可见。

    Public Sub New()
        InitializeComponent()
        LoadReports()
        If (WebContext.Current.User.IsAuthenticated) Then
            SettingsButton.Visibility = System.Windows.Visibility.Visible
        End If
    End Sub
    
    public Reports()
    {
        InitializeComponent();
        LoadReports();
        if (WebContext.Current.User.IsAuthenticated)
        {
            SettingsButton.Visibility = System.Windows.Visibility.Visible;
        }
    }
    
  13. 为设置链接上的 Click 事件添加事件处理程序。

    后一个步骤中将添加 ProfileWindow

    Private Sub SettingsButton_Click(ByVal sender As Object, ByVal e As RoutedEventArgs)
        Dim settingsWindow = New ProfileWindow()
        settingsWindow.Show()
    End Sub
    
    private void SettingsButton_Click(object sender, RoutedEventArgs e)
    {
        Views.ProfileWindow settingsWindow = new Views.ProfileWindow();
        settingsWindow.Show();
    }
    

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

    Imports System.ServiceModel.DomainServices.Client
    Imports ExampleNavigationApplication.Web
    
    Partial Public Class Reports
        Inherits Page
    
        Private context As New AdventureWorksDomainContext
        Private numberOfRows As Integer = 10
    
        Public Sub New()
            InitializeComponent()
            LoadReports()
            If (WebContext.Current.User.IsAuthenticated) Then
                SettingsButton.Visibility = System.Windows.Visibility.Visible
            End If
        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 SettingsButton_Click(ByVal sender As Object, ByVal e As RoutedEventArgs)
            Dim settingsWindow = New ProfileWindow()
            settingsWindow.Show()
        End Sub
    End Class
    
    using System;
    using System.Windows;
    using System.Windows.Controls;
    using System.Windows.Navigation;
    using System.ServiceModel.DomainServices.Client;
    using ExampleNavigationApplication.Web;
    
    namespace ExampleNavigationApplication.Views
    {
        public partial class Reports : Page
        {
            private AdventureWorksDomainContext context = new AdventureWorksDomainContext();
            int numberOfRows = 10;
    
            public Reports()
            {
                InitializeComponent();
                LoadReports();
                if (WebContext.Current.User.IsAuthenticated)
                {
                    SettingsButton.Visibility = System.Windows.Visibility.Visible;
                }
            }
    
            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 User_PropertyChanged(object sender, System.ComponentModel.PropertyChangedEventArgs e)
            {
                if (e.PropertyName == "DefaultRows")
                {
                    LoadReports();
                }
            }
    
            private void SettingsButton_Click(object sender, RoutedEventArgs e)
            {
                Views.ProfileWindow settingsWindow = new Views.ProfileWindow();
                settingsWindow.Show();
            }
        }
    }
    

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

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

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

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

    添加子窗口

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

  4. 在 ProfileWindow.xaml 文件中,添加以下 XAML 以包含一个用于选择报告中要显示的行数的 ComboBox

    <controls:ChildWindow x:Class="ExampleNavigationApplication.Views.ProfileWindow"
               xmlns="https://schemas.microsoft.com/winfx/2006/xaml/presentation"
               xmlns:x="https://schemas.microsoft.com/winfx/2006/xaml"
               xmlns:controls="clr-namespace:System.Windows.Controls;assembly=System.Windows.Controls"
               Width="300" Height="200"
               Title="ProfileWindow">
      <Grid x:Name="LayoutRoot" Margin="2">
        <Grid.RowDefinitions>
          <RowDefinition />
          <RowDefinition Height="Auto" />
        </Grid.RowDefinitions>
    
        <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="3"></ComboBoxItem>
            <ComboBoxItem Content="5"></ComboBoxItem>
            <ComboBoxItem Content="10"></ComboBoxItem>
            <ComboBoxItem Content="15"></ComboBoxItem>
            <ComboBoxItem Content="20"></ComboBoxItem>
          </ComboBox>
        </StackPanel>
    
        <Button x:Name="CancelButton" 
                Content="Cancel" 
                Click="CancelButton_Click" 
                Width="75" 
                Height="23" 
                HorizontalAlignment="Right" 
                Margin="0,12,0,0" 
                Grid.Row="1" />
        <Button x:Name="OKButton" 
                Content="OK" 
                Click="OKButton_Click" 
                Width="75" 
                Height="23" 
                HorizontalAlignment="Right" 
                Margin="0,12,79,0" 
                Grid.Row="1" />
      </Grid>
    </controls:ChildWindow>
    
  5. 在 ProfileWindow.xaml.cs 或 ProfileWindow.xaml.vb 代码隐藏文件中,添加以下代码以检索和设置配置文件属性。

    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;
        }
    }
    
  6. 运行该解决方案。

  7. 单击**“报告”**链接。

    请注意,如果网站已启动但您未登录,则“报告”页上只会显示产品表。

  8. 在主页上注册一个新用户,并以该用户的身份登录。

  9. 单击**“报告”**链接。

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

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

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

  11. 单击**“调整设置”**链接并设置要为报告显示的默认行数。

    RIA_ShowProfileSettings

    请注意,DataGrid 此时将包含您选择的行数。

  12. 关闭 Web 浏览器。

另请参见

任务

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