Hi, @fatih uyanık. Welcome to Microsoft Q&A.
It is recommended to put database-related business logic in the service layer.You could define a separate service for the table with the ISBN
attribute. The service contains the query operation on the database, here is the operation to check whether the ISBN
exists.
Then load FluentValidation
and the service defined above in ValidationService
to perform FluentValidation
model validation and ISBN
verification operations.
The error message could define a separate data structure as the return value of ValidationService
.
Get ValidationService
through dependency injection in ViewModel
and perform validation, get error information, and control the display of error information in ViewModel.
For dependency injection, you could refer to Using Microsoft.Extensions.DependencyInjection
Reference Projects
Project Structure(Assume that the table with ISBN is People
)
Install CommunityToolkit.Mvvm
, FluentValidation
, Microsoft.Extensions.DependencyInjection
through NuGet.
People.cs
public class People
{
public int Id { get; set; }
public string Name { get; set; }
public string ISBN { get; set; }
}
PeopleService.cs
public interface PeopleService
{
public bool CheckISBN(People people);
}
PeopleServiceImpl.cs
public class PeopleServiceImpl : PeopleService
{
public bool CheckISBN(People people)
{
//TODO: Verify the database
return false;
}
}
ValidationService.cs
public interface ValidationService
{
public ValidationResult ValidatePeople(People people);
}
ValidationServiceImpl.cs
public class ValidationServiceImpl : ValidationService
{
//Import two services through dependency injection for verification
private readonly PeopleService _peopleService;
private readonly IValidator<People> _validator;
public ValidationServiceImpl(PeopleService peopleService, IValidator<People> validator)
{
_peopleService = peopleService;
_validator = validator;
}
//Centrally verify these two services
public ValidationResult ValidatePeople(People people)
{
var message = "";
var result1 = _peopleService.CheckISBN(people);
if (!result1)
{
message += "ISBN not found \n";
}
var result2 = _validator.Validate(people);
if (!result2.IsValid)
{
foreach (var failure in result2.Errors)
{
message += $"Property {failure.PropertyName} failed validation. Error was: {failure.ErrorMessage} \n";
}
}
if(result1&&result2.IsValid)
{
return ValidationResult.Success();
}
else
{
return ValidationResult.Failure(message);
}
}
}
PeopleValidator.cs
public class PeopleValidator:AbstractValidator<People>
{
public PeopleValidator() {
RuleFor(people => people.Id)
.NotEmpty().WithMessage("Id cannot be empty");
RuleFor(people => people.Name)
.NotEmpty().WithMessage("Name cannot be empty")
.MinimumLength(2).WithMessage("Minimum 2 characters for the name");
RuleFor(people => people.ISBN)
.NotEmpty().WithMessage("ISBN cannot be empty");
}
}
ValidationResult.cs(The data structure used for message return. You could also design it according to your preferences.)
public class ValidationResult
{
public bool IsSuccess { get; set; }
public string Message { get; set; }
public static ValidationResult Success() => new ValidationResult { IsSuccess = true };
public static ValidationResult Failure(string message) => new ValidationResult { IsSuccess = false,Message = message };
}
MainWindow.xaml
<Grid>
<Frame x:Name="MyPage"></Frame>
</Grid>
MainWindow.xaml.cs
public partial class MainWindow : Window
{
public MainWindow(IServiceProvider serviceProvider)
{
InitializeComponent();
var page1 = serviceProvider.GetRequiredService<Page1>();
MyPage.Navigate(page1);
}
}
Page1.xaml
<Grid>
<Button Content="Click Me" Command="{Binding relayCommand}" Width="200" Height="100" ></Button>
</Grid>
Page1.xaml.cs
public partial class Page1 : Page
{
public Page1(Page1ViewModel page1ViewModel)
{
InitializeComponent();
this.DataContext = page1ViewModel;
}
}
Page1ViewModel.cs
public class Page1ViewModel:ObservableObject
{
//Obtaining services through dependency injection
private readonly ValidationService _validationService;
public RelayCommand relayCommand { get; set; }
public Page1ViewModel(ValidationService validationService)
{
relayCommand = new RelayCommand(Fun);
_validationService = validationService;
}
//Get the returned results and display them
public void Fun()
{
var result = _validationService.ValidatePeople(new People());
MessageBox.Show(result.Message);
}
}
App.xaml.cs(Configure dependency injection here)
public partial class App : Application
{
public IServiceProvider _serviceProvider { get;private set; }
public App()
{
var serviceCollection = new ServiceCollection();
ConfigureServices(serviceCollection);
_serviceProvider = serviceCollection.BuildServiceProvider();
}
protected override void OnStartup(StartupEventArgs e)
{
var mainWindow = _serviceProvider.GetRequiredService<MainWindow>();
mainWindow.Show();
base.OnStartup(e);
}
private void ConfigureServices(IServiceCollection services)
{
services.AddTransient<ValidationService,ValidationServiceImpl>();
services.AddTransient<PeopleService, PeopleServiceImpl>();
services.AddTransient<IValidator<People>, PeopleValidator>();
services.AddTransient<Page1ViewModel>();
services.AddTransient<Page1>();
services.AddTransient<MainWindow>();
}
}
Finally, you should delete StartupUri="MainWindow.xaml"
in App.xaml
to avoid repeated startup.
If the answer is the right solution, please click "Accept Answer" and kindly upvote it. If you have extra questions about this answer, please click "Comment".
Note: Please follow the steps in our documentation to enable e-mail notifications if you want to receive the related email notification for this thread.