연습: Silverlight 탐색 응용 프로그램에서 인증 서비스 사용
이 연습에서는 인증 서비스와 작동하도록 WCF RIA Services 솔루션에서 서버 및 클라이언트 프로젝트를 설정하는 방법을 알아봅니다. Silverlight 탐색 응용 프로그램 템플릿을 사용하여 솔루션을 만들고 RIA Services 를 사용하도록 설정하는 경우 인증 서비스를 추가하여 ASP.NET 멤버 자격 프레임워크에 액세스할 수 있습니다. 인증 서비스는 인증, 역할 및 프로필을 서버 프로젝트에서 클라이언트 프로젝트로 노출합니다. 인증 서비스를 사용하여 사용자 자격 증명의 유효성을 검사하고 역할을 기반으로 리소스에 대한 액세스를 제한할 수 있으며 프로필 속성을 저장할 수 있습니다.
[!참고] Silverlight 비즈니스 응용 프로그램은 자동으로 인증 서비스를 구현합니다. 자세한 내용은 연습: Silverlight 비즈니스 응용 프로그램에서 인증 서비스 사용을 참조하십시오.
필수 구성 요소
RIA Services 설명서에서 제공하는 이 연습 및 다른 연습을 실행하려면 WCF RIA Services 및 WCF RIA Services 도구 키트 외에도 Visual Studio 2010, Silverlight Developer 런타임 및 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 응용 프로그램 호스트 확인란이 선택되어 있고 새 웹 프로젝트 형식이 ASP.NET 웹 응용 프로그램 프로젝트로 설정되어 있는지 확인합니다.
WCF RIA 서비스 사용 확인란을 선택합니다.
확인을 클릭하여 솔루션을 만듭니다.
서버 프로젝트(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(); } }
솔루션을 실행합니다.
암호로 P@ssword를 사용하여 CustomerManager로 로그인합니다.
로그인 영역이 더 이상 표시되지 않지만 환영 텍스트와 로그아웃 링크가 이제 표시됩니다.
Logout 링크를 클릭하고 웹 브라우저를 닫습니다.
클라이언트에서 새 사용자 추가
인증 서비스에는 새 사용자를 만드는 작업이 포함되어 있지 않습니다. 새 사용자를 등록하려면 빈 도메인 서비스를 만들고 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 클래스에 다음 코드를 추가합니다.
클라이언트 프로젝트에 대해 NewUser 엔터티 클래스가 생성되도록 GetUsers 메서드를 포함해야 합니다. 공용 쿼리 작업을 통해 노출된 클래스만 클라이언트 프로젝트에서 생성됩니다.
사용자를 만든 후 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; } } }
솔루션을 실행합니다.
값을 제공하여 새 사용자를 등록합니다.
웹 브라우저를 닫습니다.
도메인 작업에 대한 액세스 제한
RequiresAuthenticationAttribute 특성 또는 RequiresRoleAttribute 특성을 도메인 작업에 적용하여 도메인 작업에 대한 액세스를 제한합니다. 특성이 없는 도메인 작업은 모든 사용자가 이용할 수 있습니다. 특성을 도메인 작업에 적용해도 사용자가 도메인 작업을 호출할 수 있지만 필수 자격 증명이 없는 사용자의 경우 예외가 발생합니다.
역할에 따라 표시되는 데이터를 제한하려면
솔루션 탐색기에서 서버 프로젝트를 선택하고 모든 파일 표시 단추를 클릭합니다.
App_Data 폴더를 마우스 오른쪽 단추로 클릭하고 프로젝트에 포함을 선택합니다.
App_Data 폴더를 마우스 오른쪽 단추로 클릭하고 추가를 선택한 다음 기존 항목을 선택합니다.
기존 항목 추가 대화 상자에서 AdventureWorksLT 샘플 데이터베이스를 추가합니다.
서버 프로젝트에서 새 항목을 추가하고 ADO.NET 엔터티 데이터 모델 템플릿을 선택합니다.
모델의 이름을 AdventureWorksModel.edmx로 지정하고 추가를 클릭합니다.
엔터티 데이터 모델 마법사가 나타납니다.
데이터베이스에서 생성 옵션을 선택하고 다음을 클릭합니다.
AdventureWorksLT 데이터베이스를 선택하고 다음을 클릭합니다.
데이터베이스 개체 목록에서 Customer, Product 및 SalesOrderHeader 테이블을 선택한 다음 마침을 클릭합니다.
엔터티 데이터 모델이 디자이너에 나타납니다.
솔루션을 빌드합니다.
서버 프로젝트에서 새 항목을 추가하고 도메인 서비스 클래스 템플릿을 선택합니다.
도메인 서비스의 이름을 AdventureWorksDomainService로 지정하고 추가를 클릭합니다.
새 도메인 서비스 클래스 추가 대화 상자에서 Customer, Product 및 SalesOrderHeader 엔터티를 선택합니다.
확인을 클릭하여 도메인 서비스 만들기를 마칩니다.
AdventureWorksDomainService 클래스 파일에서
GetSalesOrderHeader
메서드에 RequiresAuthenticationAttribute 특성을 추가합니다.<RequiresAuthentication()> _ Public Function GetSalesOrderHeaders() As IQueryable(Of SalesOrderHeader) Return Me.ObjectContext.SalesOrderHeaders End Function
[RequiresAuthentication()] public IQueryable<SalesOrderHeader> GetSalesOrderHeaders() { return this.ObjectContext.SalesOrderHeaders; }
GetCustomers
메서드에 RequiresRoleAttribute 특성을 추가하고 필수 역할의 이름을 "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; } }
클라이언트에서 인증 서비스 사용
제한된 권한으로 도메인 작업을 호출하기 전에 사용자에게 필수 자격 증명이 있는지 확인해야 합니다. 그렇지 않으면 예외가 throw됩니다. 이 단원에서는 사용자의 자격 증명을 확인하고 사용자의 자격 증명을 기준으로 1~3개의 DataGrid 컨트롤을 채웁니다. 사용자 프로필의 속성을 기준으로 레코드 수도 검색합니다. 인증되지 않은 사용자에 대해서는 기본값 10이 사용됩니다. 이 단원에는 사용자가 DefaultRows 프로필 속성을 설정하는 방법이 나와 있지 않지만 이후 단원에서 이 속성을 추가합니다.
데이터를 표시하기 위해 Silverlight 페이지를 추가하려면
클라이언트 프로젝트에서 Views 폴더에 새 항목을 추가합니다.
Silverlight 페이지 템플릿을 선택하고 새 페이지의 이름을 Reports.xaml로 지정합니다.
MainPage.xaml 파일을 열고 About 페이지에 연결하는
Link2
라는 HyperlinkButton 뒤에 다음 XAML을 추가하여 Reports 페이지에 대한 링크를 추가합니다.<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>
도구 상자에서
ContentStackPanel
이라는 스택 패널의 끝 태그 바로 앞으로 세 개의 DataGrid 컨트롤을 끌어 옵니다.도구 상자에서 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;
사용자가 Managers 역할에 속하는 경우
GetSalesOrderHeaderQuery
메서드와GetCustomersQuery
메서드를 호출하고 해당 데이터 표를 결과로 채우는LoadRestrictedReports
라는 메서드를 추가합니다.사용자에게 필수 자격 증명이 없을 때 도메인 작업을 호출하면 도메인 작업에서 예외가 반환됩니다. 도메인 작업을 호출하기 전에 자격 증명을 확인하여 이러한 상황을 피할 수 있습니다.
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; } }
사용자가 인증되는지 확인하고 인증되는 경우
LoadRestrictedReports
메서드를 호출하는LoadReports
라는 메서드를 추가합니다. 또한 이 메서드는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; } }
솔루션을 실행합니다.
reports 링크를 클릭합니다.
사이트가 시작될 때 사용자가 로그인하지 않은 경우 제품에 대한 테이블만 Reports 페이지에 표시됩니다.
홈 페이지에서 새 사용자를 등록하고 새 사용자로 로그인합니다.
reports 링크를 클릭합니다.
제품 및 판매 주문에 대한 테이블이 표시됩니다.
로그아웃하고 CustomerManager로 로그인합니다.
제품, 판매 주문 및 고객에 대한 테이블이 표시됩니다.
Adjust Settings 링크를 클릭하고 보고서에 표시할 기본 행 수를 설정합니다.
DataGrid에 이제 선택한 행 수가 포함되어 있습니다.
웹 브라우저를 닫습니다.