This example demonstrates how to access data protected by the Security System from a non-XAF Blazor application. You will also see how to execute Create, Write, and Delete data operations and take security permissions into account.
-
Visual Studio 2022 v17.0+ with the following workloads:
- ASP.NET and web development
- .NET Core cross-platform development
-
Download and run the 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. It will register 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.
If you wish to create a Blazor project with our Blazor Components from scratch, follow the Create a New Blazor Application article.
For detailed information about the ASP.NET Core application configuration, see official Microsoft documentation.
-
Configure the Blazor application in the Program.cs:
var builder = WebApplication.CreateBuilder(args); builder.Services.AddRazorPages(); builder.Services.AddServerSideBlazor(); builder.Services.AddDevExpressBlazor(); builder.Services.AddSession(); var app = builder.Build(); if (app.Environment.IsDevelopment()) { app.UseDeveloperExceptionPage(); } else { app.UseExceptionHandler("/Error"); app.UseHsts(); } app.UseSession(); app.UseHttpsRedirection(); app.UseRouting(); app.UseEndpoints(endpoints => { endpoints.MapFallbackToPage("/_Host"); endpoints.MapBlazorHub(); }); app.Run();
-
Register the business objects that you will access from your code in the Types Info system.
builder.Services.AddSingleton<ITypesInfo>((serviceProvider) => { TypesInfo typesInfo = new TypesInfo(); typesInfo.GetOrAddEntityStore(ti => new XpoTypeInfoSource(ti)); typesInfo.RegisterEntity(typeof(Employee)); typesInfo.RegisterEntity(typeof(PermissionPolicyUser)); typesInfo.RegisterEntity(typeof(PermissionPolicyRole)); return typesInfo; })
-
Register ObjectSpaceProviders that will be used in your application. To do this, implement the
IObjectSpaceProviderFactory
interface.builder.Services.AddScoped<IObjectSpaceProviderFactory, ObjectSpaceProviderFactory>() // ... public class ObjectSpaceProviderFactory : IObjectSpaceProviderFactory { readonly ISecurityStrategyBase security; readonly IXpoDataStoreProvider xpoDataStoreProvider; readonly ITypesInfo typesInfo; public ObjectSpaceProviderFactory(ISecurityStrategyBase security, IXpoDataStoreProvider xpoDataStoreProvider, ITypesInfo typesInfo) { this.security = security; this.typesInfo = typesInfo; this.xpoDataStoreProvider = xpoDataStoreProvider; } IEnumerable<IObjectSpaceProvider> IObjectSpaceProviderFactory.CreateObjectSpaceProviders() { yield return new SecuredObjectSpaceProvider((ISelectDataSecurityProvider)security, xpoDataStoreProvider, typesInfo, null, true); } }
-
Set up database connection settings in your Data Store Provider object. In XPO, it is
IXpoDataStoreProvider
.builder.Services.AddSingleton<IXpoDataStoreProvider>((serviceProvider) => { var connectionString = serviceProvider.GetRequiredService<IConfiguration>().GetConnectionString("ConnectionString"); return XPObjectSpaceProvider.GetDataStoreProvider(connectionString, null, true); });
The
IConfiguration
object is used to access the application configuration appsettings.json file. In appsettings.json, add the connection string."ConnectionStrings": { "ConnectionString": "Data Source=(localdb)\\MSSQLLocalDB;Initial Catalog=XPOTestDB;Integrated Security=True;Connect Timeout=30;Encrypt=False;TrustServerCertificate=False;ApplicationIntent=ReadWrite;MultiSubnetFailover=False" }
-
Register security system and authentication in the Program.cs. AuthenticationStandard authentication, and ASP.NET Core Identity authentication is registered automatically in AspNetCore Security setup.
builder.Services.AddXafAspNetCoreSecurity(builder.Configuration, options => { options.RoleType = typeof(PermissionPolicyRole); options.UserType = typeof(PermissionPolicyUser); options.Events.OnSecurityStrategyCreated = strategy => ((SecurityStrategy)strategy).RegisterXPOAdapterProviders(); }).AddAuthenticationStandard();
-
Call the
UseDemoData
method at the end of the Program.cs to update the database:public static WebApplication UseDemoData(this WebApplication app) { using var scope = app.Services.CreateScope(); var updatingObjectSpaceFactory = scope.ServiceProvider.GetRequiredService<IUpdatingObjectSpaceFactory>(); using var objectSpace = updatingObjectSpaceFactory .CreateUpdatingObjectSpace(typeof(BusinessObjectsLibrary.BusinessObjects.Employee), true)); new Updater(objectSpace).UpdateDatabase(); return app; }
For more details about how to create demo data from code, see the Updater.cs class.
Login.cshtml is a login page that allows you to log into the application.
Login.cshtml.cs class uses IStandardAuthenticationService
from XAF Security System to implement the Login logic. It authenticates user with the AuthenticationStandard authentication and return a ClaimsPrincipal object with all the necessary XAF Security data. That principal is then authenticated to the ASP.NET Core Identity authentication.
readonly IStandardAuthenticationService authenticationStandard;
// ...
public IActionResult OnPost() {
Response.Cookies.Append("userName", Input.UserName ?? string.Empty);
if(ModelState.IsValid) {
ClaimsPrincipal principal = authenticationStandard.Authenticate(new AuthenticationStandardLogonParameters(Input.UserName, Input.Password));
if(principal != null) {
HttpContext.SignInAsync(CookieAuthenticationDefaults.AuthenticationScheme, principal);
return Redirect("/");
}
ModelState.AddModelError("Error", "User name or password is incorrect");
}
return Page();
}
Logout.cshtml.cs class implements the Logout logic
public IActionResult OnGet() {
this.HttpContext.SignOutAsync();
return Redirect("/Login");
}
Index.razor is the main page. It configures the Blazor Data Grid and allows a user to log out.
The OnInitialized
method creates an ObjectSpace instance and gets Employee
and Department
objects.
protected override void OnInitialized() {
objectSpace = objectSpaceFactory.CreateObjectSpace<Employee>();
employees = objectSpace.GetObjectsQuery<Employee>();
departments = objectSpace.GetObjectsQuery<Department>();
base.OnInitialized();
}
The HandleValidSubmit
method saves changes if data is valid.
async Task HandleValidSubmit() {
objectSpace.CommitChanges();
await grid.Refresh();
employee = null;
await grid.CancelRowEdit();
}
The OnRowRemoving
method removes an object.
Task OnRowRemoving(object item) {
objectSpace.Delete(item);
objectSpace.CommitChanges();
return grid.Refresh();
}
To show/hide the New
, Edit
, Delete
actions, use the appropriate CanCreate
, CanEdit
and CanDelete
methods of the Security System.
<DxDataGridCommandColumn Width="100px">
<HeaderCellTemplate>
@if(Security.CanCreate<Employee>()) {
<button class="btn btn-link" @onclick="@(() => StartRowEdit(null))">New</button>
}
</HeaderCellTemplate>
<CellTemplate>
@if(Security.CanWrite(context)) {
<a @onclick="@(() => StartRowEdit(context))" href="javascript:;">Edit </a>
}
@if(Security.CanDelete(context)) {
<a @onclick="@(() => OnRowRemoving(context))" href="javascript:;">Delete</a>
}
</CellTemplate>
</DxDataGridCommandColumn>
The page is decorated with the Authorize attribute to prohibit unauthorized access.
@attribute [Authorize]
To show the *******
text instead of a default value in data grid cells and editors, use SecuredContainer
<DxDataGridColumn Field="@nameof(Employee.FirstName)">
<DisplayTemplate>
<SecuredContainer Context="readOnly" CurrentObject="@context" PropertyName="@nameof(Employee.FirstName)">
@(((Employee)context).FirstName)
</SecuredContainer>
</DisplayTemplate>
</DxDataGridColumn>
//...
<DxFormLayoutItem Caption="First Name">
<Template>
<SecuredContainer Context="readOnly" CurrentObject=@employee PropertyName=@nameof(Employee.FirstName) IsEditor=true>
<DxTextBox @bind-Text=employee.FirstName ReadOnly=@readOnly />
</SecuredContainer>
</Template>
</DxFormLayoutItem>
To show the *******
text instead of the default text, check the Read permission by using the CanRead
method of the Security System.
Use the CanWrite
method of the Security System to check if a user is allowed to edit a property and an editor should be created for this property.
private bool HasAccess => objectSpace.IsNewObject(CurrentObject) ?
SecurityProvider.Security.CanWrite(CurrentObject.GetType(), PropertyName) :
SecurityProvider.Security.CanRead(CurrentObject, PropertyName);