逐步解說:搭配 Silverlight 瀏覽應用程式使用驗證服務
在本逐步解說中,您將了解如何在 WCF RIA Services 方案中設定伺服器和用戶端專案使用驗證服務。使用 [Silverlight 瀏覽應用程式] 範本建立方案並啟用 RIA Services 時,您可以加入驗證服務來存取 ASP.NET 成員資格架構。驗證服務會將驗證、角色和設定檔從伺服器專案公開至用戶端專案。使用驗證服務,可以驗證使用者認證、根據角色限制存取資源和儲存設定檔屬性。
注意: |
---|
Silverlight 商務應用程式會自動實作驗證服務。如需詳細資訊,請參閱逐步解說:搭配 Silverlight 商務應用程式使用驗證服務。 |
必要條件
除了 WCF RIA Services 和 WCF RIA Services 工具組之外,在 RIA Services 文件中呈現的這個逐步解說和其他逐步解說還需要正確安裝並設定數個必要程式 (例如 Visual Studio 2010 和 Silverlight 開發人員執行階段與 SDK)。要執行逐步解說還需要安裝並設定 SQL Server 2008 R2 Express with Advanced Services,以及安裝 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 檔案。
在 [方案總管] 中,以滑鼠右鍵按一下伺服器專案,然後選取 [加入],再選取 [新增項目]。
[加入新項目] 對話方塊隨即出現。
選取 [驗證 DomainService] 範本,並將它命名為 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 檔案。
將下列 XAML 加入至名為
ContentText
的 TextBlock 之後:這個 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(); } }
執行方案。
以密碼為 P@ssword 的 CustomerManager 身分登入。
請注意,登入區不再顯示,但歡迎文字和登出連結現在便會顯示。
按一下 [登出] 連結並關閉 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 屬性 (Property) 是以 CustomValidationAttribute 屬性 (Attribute) 定義的。在這個屬性 (Attribute) 中會指定 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."); } } } }
將新項目加入至伺服器專案,然後選取 [DomainService 類別] 範本。
將檔案命名為 RegistrationDomainService.cs 或 RegistrationDomainService.vb,然後按一下 [加入]。
在 [加入新的 DomainService 類別] 對話方塊中,從 [可用的 DataContext/ObjectContext 類別] 清單中選取 [<空的 DomainService 類別>]。
按一下 [確定]。
若要建立一個透過成員資格架構加入新使用者並儲存設定檔屬性的網域作業,請將下列程式碼加入至 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 檔案。
將下列 XAML 加入至
LoginBorder
Border 控制項的結束標記之後,以建立第二個有輸入控制項的 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 陳述式。
加入註冊按鈕 Click 事件的事件處理常式,並加入網域作業的回呼方法。回呼方法包含成功建立使用者帳戶後登入使用者的程式碼。
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 資料表,然後按一下 [完成]。
實體資料模型就會出現在設計工具中。
建置方案。
在伺服器專案中加入新項目,然後選取 [DomainService 類別] 範本。
將網域服務命名為 AdventureWorksDomainService,然後按一下 [加入]。
在 [加入新的 DomainService 類別] 對話方塊中選取 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 控制項填入三個 DataGrid 控制項。您也將根據使用者設定檔的屬性來擷取記錄數目。未經驗證的使用者使用的預設記錄數目為 10。本節不包含讓使用者設定 DefaultRows 設定檔屬性,但此部分將在後面小節說明。
若要加入要顯示資料的 Silverlight 頁面
在用戶端專案中,將新項目加入至 [Views] 資料夾。
選取 [Silverlight 頁面] 範本,並將新頁面命名為 Reports.xaml。
開啟 MainPage.xaml 檔案,將下列 XAML 加在名為
Link2
的 HyperlinkButton (連結到 [關於] 頁面) 後面,以便為 [報表] 頁面加入連結。<Rectangle x:Name="Divider2" Style="{StaticResource DividerStyle}"/> <HyperlinkButton x:Name="Link3" Style="{StaticResource LinkStyle}" NavigateUri="/Reports" TargetName="ContentFrame" Content="reports"/>
開啟 Reports.xaml 檔案,並在
Grid
標記內加入下列 XAML,以符合網站中其他頁面的格式,並包含將啟動用於編輯設定檔屬性之視窗的 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。
建立名為 context 的 AdventureWorksDomainContext 的執行個體,並建立名為 numberOfRows 的變數 (內含要擷取的資料列數目)。
Private context As New AdventureWorksDomainContext Private numberOfRows As Integer = 10
private AdventureWorksDomainContext context = new AdventureWorksDomainContext(); int numberOfRows = 10;
加入名為
LoadRestrictedReports
的方法,這個方法會在使用者屬於 Managers 角色的情況下呼叫GetSalesOrderHeaderQuery
方法和GetCustomersQuery
方法,並將結果填入對應的資料格。如果在使用者沒有必要的認證時呼叫網域作業,則網域作業會傳回例外狀況。呼叫網域作業之前先檢查認證,即可避免這種情況發生。
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; }
加入 PropertyChanged 事件的事件處理常式,這個事件會在
DefaultRows
屬性變更時呼叫LoadReports
。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 瀏覽器。