A better way to set user permissions on elements using bindings - c#

I have a WPF desktop app.
I am also using sql lite to store tables/values.
One of these tables is User Permission Roles.
So.. when a User logs in the code will get a list of roles they have permission to use.
I then go through each record to set whether the button (ie) is enabled for then or not.
so in my ViewModel...
var myUserRoles = DB.CallMyMethodToReturnPermissions(User.Id);
foreach (role in myUserRoles)
{
switch (role.Name)
{
case "CanDelete"
if (role.Enabled)
{
UserPermissions.CanDelete = true;
}
break;
case "CanAdd"
if (role.Enabled)
{
UserPermissions.CanAdd = true;
}
break;
....etc etc etc
}
}
and then in my View(s) something like this...
<Button name="CanDelete "IsEnabled="{Binding UserPermissions.CanDelete}" />
<Button name="CanAdd "IsEnabled="{Binding UserPermissions.CanAdd}" />
....etc etc etc
All this works well. But looking to the future I can see added functionality and therefore User Roles/Permissions being set.
Therefore, should I consider another paradigm or hard code changes when I need to? Or is there a way to drive this from my DB table?
I only ask this question because I am relatively new to WPF and bindings so was wondering whether there are some clever alternatives?

You could create you own implementation of ICommand.
Draft example:
public class PermissionRequiredCommand : ICommand
{
public event EventHandler CanExecuteChanged;
public PermissionRequiredCommand(string role,
Action onExecute,
Action<bool> canExecute,
Func<string, bool> hasPermission)
{
// bla bla
}
public void Execute(object parameter)
{
onExecute();
}
public bool CanExecute()
{
return canExecute() & hasPermission(role);
}
}
public class ViewModel
{
public ICommand Delete {get;}
public ViewModel(Authenticator authenticator)
{
Delete = new RequiresPermissionCommand(Roles.Delete,
Delete,
CanDelete,
roleName => authenticator.HasPermission(roleName));
}
}
public static class Roles
{
public const string Delete = "CanDelete";
}

Are you ever going to change the permission during run time?
I don't know what UserPermissions is. but if it is not a field but a static class then you might run into trouble for this. Since WPF cant really determine whether the property has changed or not if its a static property. But the fact that your binding isn't x:static so I assume it is a property. Which is fine.
other than that the binding will work well with your current structure given that all ViewModels has access to the UserPermissions instance.
I would also recommend looking into RelayCommand and move the CanExecute and click action in the view model. This way you will be able to put more logic to determine whether to enable the button without a converter.

Related

DDD: Is there an elegant way to pass auditing information through while updating an aggregate root?

Suppose I have a CQRS command that looks like below:
public sealed class DoSomethingCommand : IRequest
{
public Guid Id { get; set; }
public Guid UserId { get; set; }
public string A { get; set; }
public string B { get; set; }
}
That's processed in the following command handler:
public sealed class DoSomethingCommandHandler : IRequestHandler<DoSomethingCommand, Unit>
{
private readonly IAggregateRepository _aggregateRepository;
public DoSomethingCommand(IAggregateRepository aggregateRepository)
{
_aggregateRepository = aggregateRepository;
}
public async Task<Unit> Handle(DoSomethingCommand request, CancellationToken cancellationToken)
{
// Find aggregate from id in request
var id = new AggregateId(request.Id);
var aggregate = await _aggregateRepository.GetById(id);
if (aggregate == null)
{
throw new NotFoundException();
}
// Translate request properties into a value object relevant to the aggregate
var something = new AggregateValueObject(request.A, request.B);
// Get the aggregate to do whatever the command is meant to do and save the changes
aggregate.DoSomething(something);
await _aggregateRepository.Save(aggregate);
return Unit.Value;
}
}
I have a requirement to save auditing information such as the "CreatedByUserID" and "ModifiedByUserID". This is a purely technical concern because none of my business logic is dependent on these fields.
I've found a related question here, where there was a suggestion to raise an event to handle this. This would be a nice way to do it because I'm also persisting changes based on the domain events raised from an aggregate using an approach similar to the one described here.
(TL;DR: Add events into a collection in the aggregate for every action, pass the aggregate to a single Save method in the repository, use pattern matching in that repository method to handle each event type stored in the aggregate to persist the changes)
e.g.
The DoSomething behavior from above would look something like this:
public void DoSomething(AggregateValueObject something)
{
// Business logic here
...
// Add domain event to a collection
RaiseDomainEvent(new DidSomething(/* required information here */));
}
The AggregateRepository would then have methods that looked like this:
public void Save(Aggregate aggregate)
{
var events = aggregate.DequeueAllEvents();
DispatchAllEvents(events);
}
private void DispatchAllEvents(IReadOnlyCollection<IEvent> events)
{
foreach (var #event in events)
{
DispatchEvent((dynamic) #event);
}
}
private void Handle(DidSomething #event)
{
// Persist changes from event
}
As such, adding a RaisedByUserID to each domain event seems like a good way to allow each event handler in the repository to save the "CreatedByUserID" or "ModifiedByUserID". It also seems like good information to have when persisting domain events in general.
My question is related to whether there is an easy to make the UserId from the DoSomethingCommand flow down into the domain event or whether I should even bother doing so.
At the moment, I think there are two ways to do this:
Option 1:
Pass the UserId into every single use case on an aggregate, so it can be passed into the domain event.
e.g.
The DoSomething method from above would change like so:
public void DoSomething(AggregateValueObject something, Guid userId)
{
// Business logic here
...
// Add domain event to a collection
RaiseDomainEvent(new DidSomething(/* required information here */, userId));
}
The disadvantage to this method is that the user ID really has nothing to do with the domain, yet it needs to be passed into every single use case on every single aggregate that needs the auditing fields.
Option 2:
Pass the UserId into the repository's Save method instead. This approach would avoid introducing irrelevant details to the domain model, even though the repetition of requiring a userId parameter on all the event handlers and repositories is still there.
e.g.
The AggregateRepository from above would change like so:
public void Save(Aggregate aggregate, Guid userId)
{
var events = aggregate.DequeueAllEvents();
DispatchAllEvents(events, userId);
}
private void DispatchAllEvents(IReadOnlyCollection<IEvent> events, Guid userId)
{
foreach (var #event in events)
{
DispatchEvent((dynamic) #event, Guid userId);
}
}
private void Handle(DidSomething #event, Guid userId)
{
// Persist changes from event and use user ID to update audit fields
}
This makes sense to me as the userId is used for a purely technical concern, but it still has the same repetitiveness as the first option. It also doesn't allow me to encapsulate a "RaisedByUserID" in the immutable domain event objects, which seems like a nice-to-have.
Option 3:
Could there be any better ways of doing this or is the repetition really not that bad?
I considered adding a UserId field to the repository that can be set before any actions, but that seems bug-prone even if it removes all the repetition as it would need to be done in every command handler.
Could there be some magical way to achieve something similar through dependency injection or a decorator?
It will depend on the concrete case. I'll try to explain couple of different problems and their solutions.
You have a system where the auditing information is naturally part of the domain.
Let's take a simple example:
A banking system that makes contracts between the Bank and a Person. The Bank is represented by a BankEmployee. When a Contract is either signed or modified you need to include the information on who did it in the contract.
public class Contract {
public void AddAdditionalClause(BankEmployee employee, Clause clause) {
AddEvent(new AdditionalClauseAdded(employee, clause));
}
}
You have a system where the auditing information is not natural part of the domain.
There are couple of things here that need to be addressed. For example can users only issue commands to your system? Sometimes another system can invoke commands.
Solution: Record all incomming commands and their status after processing: successful, failed, rejected etc.
Include the information of the command issuer.
Record the time when the command occured. You can include the information about the issuer in the command or not.
public interface ICommand {
public Datetime Timestamp { get; private set; }
}
public class CommandIssuer {
public CommandIssuerType Type { get; pivate set; }
public CommandIssuerInfo Issuer {get; private set; }
}
public class CommandContext {
public ICommand cmd { get; private set; }
public CommandIssuer CommandIssuer { get; private set; }
}
public class CommandDispatcher {
public void Dispatch(ICommand cmd, CommandIssuer issuer){
LogCommandStarted(issuer, cmd);
try {
DispatchCommand(cmd);
LogCommandSuccessful(issuer, cmd);
}
catch(Exception ex){
LogCommandFailed(issuer, cmd, ex);
}
}
// or
public void Dispatch(CommandContext ctx) {
// rest is the same
}
}
pros: This will remove your domain from the knowlegde that someone issues commands
cons: If you need more detailed information about the changes and match commands to events you will need to match timestamps and other information. Depending on the complexity of the system this may get ugly
Solution: Record all incomming commands in the entity/aggregate with the corresponding events. Check this article for a detailed example. You can include the CommandIssuer in the events.
public class SomethingAggregate {
public void Handle(CommandCtx ctx) {
RecordCommandIssued(ctx);
Process(ctc.cmd);
}
}
You do include some information from the outside to your aggregates, but at least it's abstracted, so the aggregate just records it. It doesn't look so bad, does it?
Solution: Use a saga that will contain all the information about the operation you are using. In a distributed system, most of the time you will need to do this so it whould be a good solution. In another system it will add complexity and an overhead that you maaaay not wan't to have :)
public void DoSomethingSagaCoordinator {
public void Handle(CommandContext cmdCtx) {
var saga = new DoSomethingSaga(cmdCtx);
sagaRepository.Save(saga);
saga.Process();
sagaRepository.Update(saga);
}
}
I've used all methods described here and also a variation of your Option 2. In my version when a request was handled, the Repositoires had access to a context that conained the user info, so when they saved events this information was included in EventRecord object that had both the event data and the user info. It was automated, so the rest of the code was decoupled from it. I did used DI to inject the contex to the repositories. In this case I was just recording the events to an event log. My aggregates were not event sourced.
I use these guidelines to choose an approach:
If its a distributed system -> go for Saga
If it's not:
Do I need to relate detailed information to the command?
Yes: pass Commands and/or CommandIssuer info to aggregates
If no then:
Does the dabase has good transactional support?
Yes: save Commandsand CommandIssueroutside of aggregates.
No: save Commandsand CommandIssuer in aggreages.

How to handle events that happened on different page

I have a UWP Project with 2 pages so far. The MainPage.xaml is the basic layout of the app ( hamburger menu, search bar, etc.). The other part of this MainPage contains a frame into which the other page LandingPage.xaml is loaded. I want to capture the user input from an AutosuggestBox in the MainPage.xaml and show the results on LandingPage.xaml ( which is in a frame present inside MainPage.xaml).
I tried inheriting the MainPage, but that's not allowed.
While Marian's answer would certainly work, I think it's far from being 'clean' or 'good' code.
First and foremost, you should implement the MVVM pattern in your UWP apps (if you don't do it already) and use a dependency injection framework for that. A very basic, easy to understand one is MVVMLight, while a more sophisticated framework of choice could be Autofac. I advise you to start with the former, it's much quicker to wrap your head around it first.
In MVVM there's a concept that solves just your problem: messengers. I wouldn't like to get into the details here, since there already a lot of very good resources about this written by much smarter people than me. For example this article from the author of MVVMLight himself: https://msdn.microsoft.com/en-us/magazine/jj694937.aspx (I know it's from 2013 and speaks about Windows 8, but fear not, the concepts are just the same.)
The idea is that distinct ViewModels shouldn't have strict dependencies on each other - it makes unit testing (which is one of the main points of doing MVVM in the first place) hard. So in your case, you should have two ViewModels: MainViewModel and LandingViewModel. One for MainPage, and one for LandingPage, respectively. Now you should implement a handler in MainPage's code-behind for AutoSuggestBox's QuerySubmitted event and call a function in MainViewModel. In that function, you would instantiate a new message with the string coming from your AutoSuggestBox (which you can acquire either from doing data binding to it or through the event handler of QuerySubmitted, it's up to you) and send it via the Messenger. In LandingViewModel, you would subscribe to this exact message and then it's again just a matter of few lines to display the received message through data binding on LandingPage.
I know it looks like a lot of hassle for just something very basic like this, especially if you compare it to Marian's straight to the point solution. But trust me, in the long run writing clean code, nicely separated, easily unit testable ViewModels will make up for the additional effort that you have to put into them initially to make them work. After such a system is set up between two ViewModels, adding a third (which I assume you'll need to do soon) is absolutely trivial and can be done very quickly.
If you're not using MVVM I'd suggest adding x:FieldModifier="public" on the AutoSuggestBox and add a public static property to MainPage to store its instance.
MainPage.xaml.cs
public static MainPage Current { get; private set; }
public MainPage()
{
Current = this;
// Rest of your code in ctor
}
Then you can access it using
string text = MainPage.Current.NameOfYourAutoSuggestBox.Text;
Just use a simple message passing mechanism of your own, like this:
public class Messages {
public static Messages Instance { get; } = new Messages();
private readonly List<Subscription> subscriptions;
private Messages() {
subscriptions = new List<Subscription>();
}
public void Send<T>(T message) {
var msgType = message.GetType();
foreach (var sub in subscriptions)
if (sub.Type.IsAssignableFrom(msgType))
sub.Handle(message);
}
public Guid Subscribe<T>(Action<T> action) {
var key = Guid.NewGuid();
lock (subscriptions) {
subscriptions.Add(new Subscription(typeof(T), key, action));
}
return key;
}
public void Unsubscribe(Guid key) {
lock (subscriptions) {
subscriptions.RemoveAll(sub => sub.Key == key);
}
}
public bool IsSubscribed(Guid key) {
lock (subscriptions) {
return subscriptions.Any(sub => sub.Key == key);
}
}
public void Dispose() {
subscriptions.Clear();
}
}
internal sealed class Subscription {
internal Guid Key { get; }
internal Type Type { get; }
private object Handler { get; }
internal Subscription(Type type, Guid key, object handler) {
Type = type;
Key = key;
Handler = handler;
}
internal void Handle<T>(T message) {
((Action<T>)Handler).Invoke(message);
}
}
It's small and simple but it allows the subscription of different messages in parallel, separated by message type. You can subscribe, in a case similar to yours, with:
Messages.Instance.Subscribe<TextChangeArgs>(OnTextChanged);
and your other pages can send their messages using:
Messages.Instance.Send(new TextChangeArgs(...));
From all subscribers, only those interested in this specific message type will receive the message. You can (and, of course, should) also unsubscribe. Some more error handling could also be necessary in a real world scenario.
If necessary, you can add extra functionality like throttling easily (to avoid too many consecutive messages in a given time period).

RelayCommand is not refreshing execute/canexecute changes

I'm newbee in mvvm (and mvvlight of course). I have 3 modelviews (a MainWindow which have a container, and another 2 modelviews (Login and Menu)). In the LoginModelView, when the user login is successfully, this call the MenuViewModel (With Messenger.Default) changing the page in the MainWindow container. All is alright until that, then i call a Message.Default.Send sending a object from LoginModelView to MenuModelView which is correctly listened, catching the object associed and executing the method associated (ConfiguraMenu) wich define a RelayCommand (checked line by line and the method is executed without any exception) but the problem is this RelayCommand is not working until i back to the LoginViewModel and i login again. I try CommandManager.InvalidateRequerySuggested() and is not working either.
This is the code for the LoginViewModel:
//This method is called when the user press the login button. No problem with this
public void ActionVerificaUsuario()
{
Miusuario = db.getUsuario(Txtusuario, Txtpassword);
if (Miusuario.esUsuario())
{
Messenger.Default.Send(new MoveToViewMessage(Page.MenuView));
Messenger.Default.Send((UsuarioModel)Miusuario);
}
}
This code is for the MenuViewModel:
public RelayCommand AbreExeClaseCommand { get; private set; }
public MenuViewModel()
{
Messenger.Default.Register<UsuarioModel>(this, usuario_recibido => {Miusuario = usuario_recibido;ConfiguraMenu(); });
}
private void ConfiguraMenu() {
Mimenu = new MenuModel(Miusuario);
AbreExeClaseCommand = new RelayCommand(() => { Messenger.Default.Send(new MoveToViewMessage(Page.NeverReachedView)); }, () => Mimenu.Sw_reportes);
CommandManager.InvalidateRequerySuggested();
AbreExeClaseCommand.RaiseCanExecuteChanged();
}
I tried to hardcode the CanExecute with true but the Execute is still without work until back and login again.
I hope you can help me (i'm scratching my head for various days with none result).
MvvmLight provides two different RelayCommand classes in two different namespaces:
Galasoft.MvvmLight.Command
Galasoft.MvvmLight.CommandWpf
Make sure, that you are using the correct namespace Galasoft.MvvmLight.CommandWpf in your WPF application.
There was a bug in MVVMLight, which resulted in not working CanExecute() behavior. They fixed it with the new .CommandWpf namespace in MVVMLight Version V5.0.2.
You can also check out this GalaSoft blog post and the change log for further information.
You try to bind the CanExecute to a propertie.
So my guess is you didn't use RaisePropertie Changed in this propertie.
You must have something like:
public class MenuModel : ViewModelBase
{
// Other pieces of code....
private bool _sw_reportes;
public bool Sw_reportes
{
get { return _sw_reportes; }
set { _sw_reportes = value;
RaisePropertyChanged(() => Sw_reportes); }
}
}

Bind a button to a command (Windows Phone 7.5)

I'm working on my windows-phone app which uses some simple data binding. I've already created a app which was based on the MvvM programming method.The app i'm currently working on also works by MvvM method. Because i want to keep my code behind as clean as possible i was looking for a way to make the "button click event" (which normally takes place in the codebehind page) take place in my viewmodel or mainviewmodel.
I have searched the internet in need of a simple explanation for the Icommand interface because i believe that's the way to go. Problem with the explanations I found was that some of them were based on MvvMlight toolkit using the CommandRelay function. I don't want to use the MvvM light toolkit because I want to understand things myself first. The other tutorials I found were written by over enthusiastic developers which give you an overkill of information.
Can someone show me the Most simple version of an Icommand bound to a button works?
In your XAML:
<Button Content="My Button" Command="{Binding MyViewModelCommand}" />
In your view-model:
public class MyViewModel
{
public MyViewModel()
{
MyViewModelCommand = new ActionCommand(DoSomething);
}
public ICommand MyViewModelCommand { get; private set; }
private void DoSomething()
{
// no, seriously, do something here
}
}
INotifyPropertyChanged and other view-model pleasantries elided.
An alternative way to structure the command in your view-model is shown at the bottom of this answer.
Now, you'll need an implementation of ICommand. I suggest starting with something simple like this, and extend or implement other features/commands as necessary:
public class ActionCommand : ICommand
{
private readonly Action _action;
public ActionCommand(Action action)
{
_action = action;
}
public void Execute(object parameter)
{
_action();
}
public bool CanExecute(object parameter)
{
return true;
}
public event EventHandler CanExecuteChanged;
}
Here is an alternative way to layout your view-model:
public class MyViewModel
{
private ICommand _myViewModelCommand;
public ICommand MyViewModelCommand
{
get
{
return _myViewModelCommand
?? (_myViewModelCommand = new ActionCommand(() =>
{
// your code here
}));
}
}
}
To add to Jays answer:
My all time favorite is the DelegateCommand from the Patterns and Practices team # Microsoft. Check out this post for more info.

How to accept input from textbox

I am attempting to implement a model view presenter pattern in C# windows forms. I have tried several different methods but have not figured this out. Is there anyway to "accept" user input from a text box when the user clicks off of the text box? If there is not, what is the typical way to pass data to the presenter? I can use the textbox's on_text_changed event but then I am confused on how to validate the data.
I think its easy here are the MSDN links that illustrate the function of TextBox class with code.
TextBoxBase.Text Property
& validate using:
Control.Validated Event
Hope it helps!
I know this thread is a way too old but still.
public interface IProjectView
{
string textBoxText{get;}
}
implement it in ProjectView
public class ProjectView : IProjectView
{
ProjectPresenter _presenter;
public ProjectView()
{
_presenter = new ProjectPresenter(this);
}
string textBoxText
{
get
{
// Do validation if you want
return textBox1.Text;
}
}
}
class ProjectPresenter
{
IProjectView _view;
public ProjectPresenter(IProjectView view)
{
_view = view;
}
public void AnyMethod()
{
// Access value of textbox as _view.txtTextBoxText
}
}
Code not tested, but should work fine.
Regarding validation, while implementing the property in ProjectView, do the validation before returning value.
You'll want to listen for the Leave event. That'll happen when the textbox loses focus; after that, you can do your validation.

Categories

Resources