演练:将身份验证服务用于 Silverlight 导航应用程序
在本演练中,您将了解如何在 WCF RIA Services 解决方案中设置服务器和客户端项目以使用身份验证服务。在使用 Silverlight 导航应用程序模板创建解决方案并启用 RIA Services 时,可通过添加身份验证服务来访问 ASP.NET 成员资格框架。身份验证服务可向客户端项目公开服务器项目中的身份验证、角色和配置文件。使用身份验证服务可验证用户凭据、基于角色限制对资源的访问以及存储配置文件属性。
注意: |
---|
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 应用程序中的那些功能,则还可以在服务器项目上配置角色和配置文件。在本演练中,您将配置所有三个功能。最后,向客户端添加用于公开已启用的功能的身份验证服务。
配置服务器项目
在 Visual Studio 2010 中,依次选择**“文件”、“新建”和“项目”**。
此时将出现**“新建项目”**对话框。
选择**“Silverlight”**项目类型。
选择**“Silverlight 导航应用程序”**模板,并将应用程序命名为 ExampleNavigationApplication。
单击**“确定”**。
此时将出现**“新建 Silverlight 应用程序”**对话框。
确保已选中**“在新网站中承载 Silverlight 应用程序”复选框,并将新 Web 项目类型设置为“ASP.NET Web 应用程序项目”**。
选中**“启用 WCF RIA Services”**复选框。
单击**“确定”**创建解决方案。
在服务器项目 (ExampleBusinessApplication.Web) 中,打开 Web.config 文件。
在
<system.web>
元素中,添加一个<authentication>
元素,并将mode
属性设置为“Forms”。<authentication mode="Forms"></authentication>
在
<system.web>
元素中,添加一个<roleManager>
元素,并将enabled
属性设置为 true。<roleManager enabled="true"></roleManager>
在
<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>
保存 Web.config 文件。
在**“解决方案资源管理器”中,右击服务器项目,选择“添加”,然后选择“新建项”**。
此时将出现**“添加新项”**对话框。
选择**“身份验证域服务”**模板,并将其命名为 AuthenticationDomainService。
单击**“添加”**。
打开身份验证服务代码文件(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; } }
生成解决方案。
在此部分中,您将使用 ASP.NET 网站管理工具来创建用户和角色。在后面的部分中,您将以此用户身份登录。
使用 ASP.NET 网站管理工具添加用户
若要打开 ASP.NET 网站管理工具,请先在**“解决方案资源管理器”**中选择服务器项目。
在**“项目”菜单上,选择“ASP.NET 配置”**。
如果“项目”菜单中未显示“ASP.NET 配置”选项,则可能是因为您选择了客户端项目。
在 ASP.NET 网站管理工具中选择**“安全性”**选项卡。
在**“角色”部分中,单击“创建或管理角色”**链接。
添加一个名为 Managers 的新角色,然后单击**“添加角色”**按钮。
在右下角,单击**“上一步”**按钮。
在**“用户”部分中,单击“创建用户”**链接。
使用以下值创建新用户并选中“Managers”角色复选框。
用户名:CustomerManager
密码:P@ssword
电子邮件:someone@example.com
安全提示问题:最喜欢什么颜色?
安全答案:蓝色
Managers 角色:已选定
单击**“创建用户”**按钮。
关闭 ASP.NET 网站管理工具。
配置身份验证的客户端
您必须将客户端项目配置为使用身份验证模式,此模式与您在服务器项目中配置的身份验证模式匹配。
配置客户端项目
在客户端项目中,打开 App.xaml 文件的代码隐藏文件(App.xaml.cs 或 App.xaml.vb)。
在构造函数中,创建 WebContext 类的新实例。
将 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 方法的代码。您还需要根据用户是否已登录来设置可见控件。
为简单起见,本演练中会将登录用户界面添加到主页。您可能需要在应用程序中创建一个单独登录页面。
登录和注销用户
在**“解决方案资源管理器”**中,在客户端项目中展开 Views 文件夹。
打开 Home.xaml 文件。
在名为
ContentText
的 TextBlock 的后,添加以下 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>
打开主页的代码隐藏文件(Home.xaml.cs 或 Home.xaml.vb)。
为 System.ServiceModel.DomainServices.Client.ApplicationServices 命名空间添加 using 或 Imports 语句。
将下面的代码添加到 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(); } }
运行该解决方案。
以 CustomerManager 身份登录,密码为 P@ssword。
请注意,将不再显示登录区域,而显示欢迎文本和注销链接。
单击**“注销”**链接并关闭 Web 浏览器。
从客户端添加新用户
此身份验证服务不包含用于创建新用户的操作。若要注册新用户,请创建一个空域服务,并添加一个用于将用户添加到 ASP.NET 成员资格框架的操作。
配置用来添加新用户的服务器项目
在此服务器项目中,添加一个名为 NewUser 的新类文件。
通过将以下代码添加到 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 的方法。此时必须定义此自定义验证类。
添加一个名为 RegistrationValidator.shared.cs 或 RegistrationValidator.shared.vb 的新类。
将以下代码添加到 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."); } } } }
向服务器项目添加一个新项,并选择“域服务类”模板。
将此文件命名为 RegistrationDomainService.cs 或 RegistrationDomainService.vb,然后单击**“添加”**按钮。
在“添加新的域服务类”对话框中,从**“可用 DataContexts/ObjectContexts”列表选择“<空域服务类>”**。
单击**“确定”**。
若要创建一个通过成员资格框架添加新用户的域操作并保存配置文件属性,请将以下代码添加到 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(); } } }
配置用来添加新用户的客户端项目
打开 Home.xaml 文件。
在
LoginBorder
Border 控件的结束标记后面,添加以下 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>
打开 Home.xaml.cs(或 Home.xaml.vb)代码隐藏文件。
为 System.ServiceModel.DomainServices.Client、System.ComponentModel.DataAnnotations 和 ExampleNavigationApplication.Web 命名空间添加 using 或 Imports 语句。
为注册按钮单击事件添加事件处理程序,并为域操作添加回调方法。此回调方法包含用于在成功创建用户帐户后登录用户的代码。
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; }
修改 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; } } }
运行该解决方案。
提供用于注册新用户的值。
关闭 Web 浏览器。
限制对域操作的访问
您可以通过将 RequiresAuthenticationAttribute 特性或 RequiresRoleAttribute 特性应用于域操作来限制对域操作的访问。没有特性的域操作对所有用户都可用。将特性应用于域操作不会阻止用户调用域操作;但没有所需凭据的用户将收到异常。
按角色限制显示的数据
在**“解决方案资源管理器””中,选择服务器项目,然后单击“显示所有文件”**按钮。
右击 App_Data 文件夹,然后选择**“包括在项目中”**。
右击 App_Data 文件夹,选择**“添加”,然后选择“现有项”**。
在**“添加现有项”**对话框中,添加 AdventureWorksLT 示例数据库。
在服务器项目中,添加一个新项并选择**“ADO.NET 实体数据模型”**模板。
将该模型命名为 AdventureWorksModel.edmx,然后单击**“添加”**。
此时将出现**“实体数据模型向导”**。
选择**“从数据库生成”选项,然后单击“下一步”**。
选择 AdventureWorksLT 数据库,然后单击**“下一步”**。
从数据库对象列表中,选择 Customer、Product 和 SalesOrderHeader 表,然后单击**“完成”**。
实体数据模型将出现在设计器中。
生成解决方案。
在服务器项目中,添加一个新项并且选择**“域服务类”**模板。
将域服务命名为 AdventureWorksDomainService,然后单击**“添加”**。
在**“添加新的域服务类”**对话框中,选择 Customer、Product 和 SalesOrderHeader 实体。
单击**“确定”**以完成域服务的创建。
在 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; }
将 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 页以显示数据
在客户端项目中,向 Views 文件夹中添加一个新项。
选择**“Silverlight 页”**模板并将新页命名为 Reports.xaml。
打开 MainPage.xaml 文件,然后通过在链接到“关于”页的名为
Link2
的 HyperlinkButton 后添加以下 XAML 来为“报告”页添加链接。<Rectangle x:Name="Divider2" Style="{StaticResource DividerStyle}"/> <HyperlinkButton x:Name="Link3" Style="{StaticResource LinkStyle}" NavigateUri="/Reports" TargetName="ContentFrame" Content="reports"/>
打开 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>
将三个 DataGrid 控件从**“工具箱”**拖出,然后刚好放置于名为
ContentStackPanel
的堆叠面板的结束标记之前。在您从**“工具箱”**拖动 DataGrid 控件时,会将对 System.Windows.Controls.Data 程序集的引用添加到项目中,并会将 System.Windows.Controls 命名空间的前缀添加到该页。
命名 DataGrid 控件
ProductsGrid
、SalesOrdersGrid
和CustomersGrid
,并将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>
打开 Reports.xaml.cs 或 Reports.xaml.vb 文件,并为 System.ServiceModel.DomainServices.Client 和 ExampleNavigationApplication.Web 命名空间添加 using 或 Imports 语句。
创建名为 AdventureWorksDomainContext 的上下文实例,并创建一个名为 numberOfRows 的变量,该变量包含要检索的行数。
Private context As New AdventureWorksDomainContext Private numberOfRows As Integer = 10
private AdventureWorksDomainContext context = new AdventureWorksDomainContext(); int numberOfRows = 10;
添加一个名为
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; } }
添加一个名为
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; }
如果属性
DefaultRows
已发生更改,则为调用LoadReports
的 PropertyChanged 事件添加事件处理程序。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(); } }
将下面的代码添加到构造函数中。此代码将加载报告,并使设置链接对经过身份验证的用户可见。
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; } }
为设置链接上的 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 对象的属性检索当前值。
添加用于设置配置文件属性的窗口
在客户端项目中,向 Views 文件夹中添加一个新项。
选择**“Silverlight 子窗口”**模板,并将其命名为 ProfileWindow.xaml。
单击**“添加”**按钮。
在 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>
在 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; } }
运行该解决方案。
单击**“报告”**链接。
请注意,如果网站已启动但您未登录,则“报告”页上只会显示产品表。
在主页上注册一个新用户,并以该用户的身份登录。
单击**“报告”**链接。
请注意,将显示产品和销售订单表。
注销并以 CustomerManager 身份登录。
请注意,将显示产品、销售订单和客户表。
单击**“调整设置”**链接并设置要为报告显示的默认行数。
请注意,DataGrid 此时将包含您选择的行数。
关闭 Web 浏览器。