This example demonstrates how to access data protected by the Security System from a non-XAF Windows Forms App (.NET Core) using XPO ORM for data access. You will also learn how to execute Create, Write, and Delete data operations taking into account security permissions.
If you are using EF Core for data access, follow this tutorial.
-
Download and run our Unified Component Installer or add NuGet feed URL to Visual Studio NuGet feeds.
We recommend that you select all products when you run the DevExpress installer. The installer registers local NuGet package sources and item / project templates required for these tutorials. You can uninstall unnecessary components later.
NOTE
If you have a pre-release version of our components, for example, provided with the hotfix, you also have a pre-release version of NuGet packages. These packages will not be restored automatically and you need to update them manually as described in the Updating Packages article using the Include prerelease option.
-
Create a new .NET 6 WinForms application or use an existing application.
-
Add DevExpress NuGet packages to your project:
<PackageReference Include="DevExpress.Win.Grid" Version="22.2.3" /> <PackageReference Include="DevExpress.Persistent.BaseImpl.Xpo" Version="22.2.3" /> <PackageReference Include="DevExpress.ExpressApp.Security.Xpo" Version="22.2.3" />
-
Open the application configuration file (App.config). Add the following line to this file.
<add name="ConnectionString" connectionString="Data Source=(localdb)\MSSQLLocalDB;Initial Catalog=XPOTestDB;Integrated Security=True"/>
-
Create an instance of
TypesInfo
required for the correct operation of the Security System.TypesInfo typesInfo = new TypesInfo();
-
Register the business objects that you will access from your code in the Types Info system.
typesInfo.GetOrAddEntityStore(ti => new XpoTypeInfoSource(ti)); typesInfo.RegisterEntity(typeof(Employee)); typesInfo.RegisterEntity(typeof(PermissionPolicyUser)); typesInfo.RegisterEntity(typeof(PermissionPolicyRole));
-
Call the
CreateDemoData
method at the beginning of theMain
method of Program.cs:private static void CreateDemoData(TypesInfo typesInfo, IXpoDataStoreProvider dataStoreProvider) { using (var objectSpaceProvider = new XPObjectSpaceProvider(dataStoreProvider, typesInfo, null)) { using (var objectSpace = objectSpaceProvider.CreateUpdatingObjectSpace(true)) { new Updater(objectSpace).UpdateDatabase(); } } }
For more details about how to create demo data from code, see the Updater.cs class.
-
Initialize the Security System.
namespace WindowsFormsApplication { static class Program { [STAThread] static void Main() { // ... AuthenticationStandard authentication = new AuthenticationStandard(); SecurityStrategyComplex security = new SecurityStrategyComplex(typeof(PermissionPolicyUser), typeof(PermissionPolicyRole), authentication, typesInfo); security.RegisterXPOAdapterProviders(); // ... } } }
-
Create a SecuredObjectSpaceProvider object. It allows you to create a SecuredObjectSpace to ensure a secured data access.
SecuredObjectSpaceProvider objectSpaceProvider = new SecuredObjectSpaceProvider(security, dataStoreProvider, typesInfo, null);
-
In Program.cs, create MainForm using a custom constructor.
MainForm
is the MDI parent form for EmployeeListForm and EmployeeDetailForm.Application.EnableVisualStyles(); Application.SetCompatibleTextRenderingDefault(false); MainForm mainForm = new MainForm(security, objectSpaceProvider); Application.Run(mainForm);
-
Display the LoginForm on the Form.Load event. If the dialog returns DialogResult.OK,
EmployeeListForm
is created and shown.private void MainForm_Load(object sender, EventArgs e) { ShowLoginForm(); } private void ShowLoginForm(string userName = "User") { using(LoginForm loginForm = new LoginForm(Security, ObjectSpaceProvider, userName)) { DialogResult dialogResult = loginForm.ShowDialog(); if(dialogResult == DialogResult.OK) { CreateListForm(); Show(); } else { Close(); } } } private void CreateListForm() { EmployeeListForm employeeForm = new EmployeeListForm(); employeeForm.MdiParent = this; employeeForm.WindowState = FormWindowState.Maximized; employeeForm.Show(); }
-
Handle the RibbonControl's Logout item.
private void LogoutButtonItem_ItemClick(object sender, DevExpress.XtraBars.ItemClickEventArgs e) { foreach(Form form in MdiChildren) { form.Close(); } string userName = Security.UserName; Security.Logoff(); Hide(); ShowLoginForm(userName); }
-
LoginForm contains two TextBox controls for username and password, and the Login button that attempts to log the user into the security system and returns DialogResult.OK if logon was successful.
private void Login_Click(object sender, EventArgs e) { IObjectSpace logonObjectSpace = objectSpaceProvider.CreateObjectSpace(); string userName = userNameEdit.Text; string password = passwordEdit.Text; security.Authentication.SetLogonParameters(new AuthenticationStandardLogonParameters(userName, password)); try { security.Logon(logonObjectSpace); DialogResult = DialogResult.OK; Close(); } catch(Exception ex) { XtraMessageBox.Show(ex.Message, "Error", MessageBoxButtons.OK, MessageBoxIcon.Error); } }
-
EmployeeListForm contains a DevExpress Grid View that displays a list of all Employees. Handle the Form.Load event and:
- Create a
EFCoreObjectSpace
instance to access protected data and use its data manipulation APIs (for instance, IObjectSpace.GetObjects) OR if you prefer, the familiarUnitOfWork
object accessible through the SecuredObjectSpace.Session property. - Set XPBindingSource.DataSource to the Employees collection obtained from the secured object space.
- Call the CanCreate method to check Create operation availability and thus determine whether the New button can be enabled.
private void EmployeeListForm_Load(object sender, EventArgs e) { security = ((MainForm)MdiParent).Security; objectSpaceProvider = ((MainForm)MdiParent).ObjectSpaceProvider; securedObjectSpace = objectSpaceProvider.CreateObjectSpace(); // The XPO way: // var session = ((SecuredObjectSpace)securedObjectSpace).Session; // // The XAF way: employeeBindingSource.DataSource = securedObjectSpace.GetObjects<Employee>(); newBarButtonItem.Enabled = security.CanCreate<Employee>(); protectedContentTextEdit = new RepositoryItemProtectedContentTextEdit(); }
- Create a
-
Handle the GridView.CustomRowCellEdit event and check Read operation availability.
private void GridView_CustomRowCellEdit(object sender, CustomRowCellEditEventArgs e) { string fieldName = e.Column.FieldName; object targetObject = employeeGridView.GetRow(e.RowHandle); if (!security.CanRead(targetObject, fieldName)) { e.RepositoryItem = protectedContentTextEdit; } }
Note that SecuredObjectSpace returns default values (for instance, null) for protected object properties - it is secure even without any custom UI. Use the SecurityStrategy.CanRead method to determine when to mask default values with the "*******" placeholder in the UI.
-
Handle the FocusedRowObjectChanged event and use the SecurityStrategy.CanDelete method to check Delete operation availability and thus determine if the Delete button can be enabled.
private void EmployeeGridView_FocusedRowObjectChanged(object sender, FocusedRowObjectChangedEventArgs e) { deleteBarButtonItem.Enabled = security.CanDelete(e.Row); }
-
Delete the current object in the deleteBarButtonItem.ItemClick event handler.
private void DeleteBarButtonItem_ItemClick(object sender, DevExpress.XtraBars.ItemClickEventArgs e) { object cellObject = employeeGridView.GetRow(employeeGridView.FocusedRowHandle); securedObjectSpace.Delete(cellObject); securedObjectSpace.CommitChanges(); }
-
Create and show
EmployeeDetailForm
.private void CreateDetailForm(Employee employee = null) { EmployeeDetailForm detailForm = new EmployeeDetailForm(employee); detailForm.MdiParent = MdiParent; detailForm.WindowState = FormWindowState.Maximized; detailForm.Show(); detailForm.FormClosing += DetailForm_FormClosing; }
We need to create
EmployeeDetailForm
in three cases:-
When a user clicks the New button
private void NewBarButtonItem_ItemClick(object sender, DevExpress.XtraBars.ItemClickEventArgs e) { CreateDetailForm(); }
-
When a user clicks the Edit button (passes the current row handle as a parameter to the
CreateDetailForm
method)private void EditEmployee() { Employee employee = employeeGridView.GetRow(employeeGridView.FocusedRowHandle) as Employee; CreateDetailForm(employee); } private void EditBarButtonItem_ItemClick(object sender, DevExpress.XtraBars.ItemClickEventArgs e) { EditEmployee(); }
-
When a user double-clicks a row
private void EmployeeGridView_RowClick(object sender, RowClickEventArgs e) { if(e.Clicks == 2) { EditEmployee(); } }
-
-
EmployeeDetailForm contains detailed information on the Employee object. Perform the following operation in the Form.Load event handler:
- Create a
SecuredObjectSpace
instance to get the current or create new Employee object. - Use the SecurityStrategy.CanDelete method to check Delete operation availability and thus determine if the Delete button can be enabled. The Delete button is always disabled if you create new object.
- Set XPBindingSource.DataSource to the Employee object.
private void EmployeeDetailForm_Load(object sender, EventArgs e) { security = ((MainForm)MdiParent).Security; objectSpaceProvider = ((MainForm)MdiParent).ObjectSpaceProvider; securedObjectSpace = objectSpaceProvider.CreateObjectSpace(); if(employee == null) { employee = securedObjectSpace.CreateObject<Employee>(); } else { employee = securedObjectSpace.GetObject(employee); deleteBarButtonItem.Enabled = security.CanDelete(employee); } employeeBindingSource.DataSource = employee; AddControls(); }
- Create a
-
The
AddControls
method creates controls for all members declared in thevisibleMembers
dictionary.private Dictionary<string, string> visibleMembers; // ... public EmployeeDetailForm(Employee employee) { // ... visibleMembers = new Dictionary<string, string>(); visibleMembers.Add(nameof(Employee.FirstName), "First Name:"); visibleMembers.Add(nameof(Employee.LastName), "Last Name:"); visibleMembers.Add(nameof(Employee.Department), "Department:"); } // ... private void AddControls() { foreach(KeyValuePair<string, string> pair in visibleMembers) { string memberName = pair.Key; string caption = pair.Value; AddControl(dataLayoutControl1.AddItem(), employee, memberName, caption); } }
-
The
AddControl
method creates a control for a specific member. Use the SecurityStrategy.CanRead method to check Read operation availability. If not available, create and disable theProtectedContentEdit
control which displays the "*******" placeholder. Otherwise:- Call the
GetControl
method to create an appropriate control depending of the member type. We use the ComboBoxEdit control for the Department associated property. - Add a binding to the Control.DataBindings collection.
- Use the SecurityStrategy.CanWrite method to check Write operation availability and thus determine whether the control should be enabled.
private void AddControl(LayoutControlItem layout, object targetObject, string memberName, string caption) { layout.Text = caption; Type type = targetObject.GetType(); BaseEdit control; if(security.CanRead(targetObject, memberName)) { control = GetControl(type, memberName); if(control != null) { control.DataBindings.Add( new Binding( "EditValue", employeeBindingSource, memberName, true, DataSourceUpdateMode.OnPropertyChanged ) ); control.Enabled = security.CanWrite(targetObject, memberName); } } else { control = new ProtectedContentEdit(); control.Enabled = false; } dataLayoutControl1.Controls.Add(control); layout.Control = control; } private BaseEdit GetControl(Type type, string memberName) { BaseEdit control = null; ITypeInfo typeInfo = securedObjectSpace.TypesInfo.FindTypeInfo(type); IMemberInfo memberInfo = typeInfo.FindMember(memberName); if(memberInfo != null) { if(memberInfo.IsAssociation) { control = new ComboBoxEdit(); ((ComboBoxEdit)control).Properties.Items.AddRange( securedObjectSpace.GetObjects<Department>() as XPCollection<Department> ); } else { control = new TextEdit(); } } return control; }
- Call the
-
Use
SecuredObjectSpace
to commit all changes to database in the saveBarButtonItem.ItemClick event handler and delete the current object in the deleteBarButtonItem.ItemClick event handler.private void SaveBarButtonItem_ItemClick(object sender, DevExpress.XtraBars.ItemClickEventArgs e) { securedObjectSpace.CommitChanges(); Close(); } private void DeleteBarButtonItem_ItemClick(object sender, DevExpress.XtraBars.ItemClickEventArgs e) { securedObjectSpace.Delete(employee); securedObjectSpace.CommitChanges(); Close(); }