I am relativly new at MVVM, and have run into a problem. We are writing a database application in WPF using the MVVM-Light framework. The specs of the program state we must be able to have multiple instances of the ClaimView open at once.
To open new windows we are sending a Message from the ViewModel that is caught in the View, and opens the new window. We are using Enumerated tokens to identify the correct recipient to get the request.
Now, if I have 2 instances of the ClaimView open at once, and I call the Messanger, it opens 2 of the same windows, because both Views are recieveing the message.
We have tried running each instance of the ViewModel on a seperate thread, and verified by outputing the ManagedThreadId, and the message is still being recieved by both instances.
We have unregistered the Registered Message also, so that is not the problem.
Any help would be appreciated.
New Answer
As pointed out by the OP (Daryl), my original answer (see below) was not quite right, so I'm providing a new answer in case someone with the same problem comes across this later:
It makes sense that if you have two instances of something that are registering for the same message type with the same token, both instances will receive the message. The solution is to provide a token that is unique to each View-ViewModel pair.
Instead of just using a plain enum value as your token, you can place your enum value in a class, like this:
public class UniqueToken
{
public MessengerToken Token { get; private set; }
public UniqueToken(MessengerToken token)
{
Token = token;
}
}
Then in your ViewModel, add a new property to store one of these unique tokens:
// add a property to your ViewModel
public UniqueToken OpenWindowToken { get; private set; }
// place this in the constructor of your ViewModel
OpenWindowToken = new UniqueToken(MessengerToken.OpenWindow);
// in the appropriate method, send the message
Messenger.Send(message, OpenWindowToken);
Finally, in your View, you can now grab the unique token and use it to register for the OpenWindow message:
var viewModel = (MyViewModel)DataContext;
var token = viewModel.OpenWindowToken;
Messenger.Register<TMessage>(this, token, message => OpenWindow(message));
It is necessary for both the ViewModel and View to use a single instance of UniqueToken, because the messenger will only send a message if the receiver token and sender token are the exact same object, not just instances with the same property values.
Original Answer (not quite correct)
I think there may be a typo in your question: You say that to open a new window, you send a message from the ViewModel to the View, but then later you say both ViewModels are receiving the message. Did you mean both Views are receiving the message?
In any case, it makes sense that if you have two instances of something that are registering for the same message type with the same token, both instances will receive the message.
To solve this, you will first need each instance of your ViewModel to have a unique ID. This could accomplished with a Guid. Something like:
// add a property to your ViewModel
public Guid Id { get; private set; }
// place this in the constructor of your ViewModel
Id = Guid.NewGuid();
Then you would need your token to be an object that has two properties: one for the guid and one for the enum value:
public class UniqueToken
{
public Guid Id { get; private set; }
public MessengerToken Token { get; private set; }
public UniqueToken(Guid id, MessengerToken token)
{
Id = id;
Token = token;
}
}
Then when you register in your View (or is it your ViewModel?), you need to grab the Guid from the ViewModel. This could work like this:
var viewModel = (MyViewModel)DataContext;
var id = viewModel.Id;
var token = new UniqueToken(id, MessengerToken.OpenWindow);
Messenger.Register<TMessage>(this, token, message => OpenWindow(message));
Finally, in your ViewModel, you need to do something like this:
var token = new UniqueToken(Id, MessengerToken.OpenWindow);
Messenger.Send(message, token);
Edit
After typing all that out, it occurred to me that you don't really need an Id property on the ViewModel. You could just use the ViewModel itself as the unique identifier. So, for UniqueToken, you could just replace public Guid Id with public MyViewModel ViewModel, and it should still work.
Related
I'm using .NET Core with Newtonsoft.Json. I have a UserModel class that has a List<Claim> property
public class UserModel
{
public string GUID { get; set; }
public bool isActive { get; set; }
public string Username { get; set; }
public string Password { get; set; }
public List<Claim> Claims { get; set; }
}
and I'm trying to parse the JSON request into this object class like so:
public IActionResult Testpost([FromBody]JObject body)
{
if (body == null) return BadRequest();
UserModel user = JsonConvert.DeserializeObject<UserModel>(body.ToString());
return Ok(user);
}
but deserializing JSON into an object like Claim class which I don't have access to throws an exception
Newtonsoft.Json.JsonSerializationException: 'Unable to find a constructor to use for type System.Security.Claims.Claim. A class should either have a default constructor, one constructor with arguments or a constructor marked with the JsonConstructor attribute. Path 'Claims
because it is not able to decide on a constructor
According to online sources I can create a custom converter class that can manage the UserModel object creation but I would like to avoid this.
Is it possible to deserialize a JSON object into my UserModel class and tell the JsonConvert.DeserializeObject to use a specific Claim constructor like Claim(String, String) for parsing the Claims?
EDIT:
as mentioned by #PaulG i have already check the answer for How to programmatically choose a constructor during deserialization?
however the accepted solution used Creates a new class that implements the JsonConverter class then manually parses the body of the JObject request. Moreover, the answer shows how to deal with Claims but not with complex objects where claims are nested as properties
reading another solution in the thread it shows how to create a class that directly implements the constructor needed like so:
class MyClaim : Claim {
public MyClaim(string type, string value):
base(type, value){}
}
but this will require me to keep note on the difference between Claim and MyClaim when writing my code. the JSON converter may not be able to assume which constructor to use but i should be able to tell it which one. or is it by design and i have to suck it up and write extra code just for this?
because the alternative for me would be something like this:
public IActionResult CreatePublicUser([FromBody]JObject body)
{
string Username = body["Username"].ToString();
string Password = body["Password"].ToString();
var Claims = body["Claims"].Children();
List<Claim> UserClaims = new List<Claim>();
foreach (var c in Claims)
{
UserClaims.Add(
new Claim(
c["Type"].ToString(),
c["Value"].ToString()
)
);
}
UserModel NewUser = (new UserBuilder())
.WithUserName(Username)
.WithPassword(Password)
.WithClaims(UserClaims)
.Build();
return Ok(NewUser)
}
I suggest that you take a different approach altogether which is a common practice as well. Define a model only for the interaction with client, e.g.
public class UserModelWeb
{
public List<ClaimWeb> Claims { get; set; }
}
These DTO objects will be used only for the data conversion from JSON and never in the business logic layer. Then you can map your web models to the business logic that you will be using later. This will allow you to not be dependent on internal classes when you read external data. I.e. if you suddenly add a new field, it will be populated from external (and probably untrusted) source. This clear separation of concerns will not allow this since you will have to explicitly define a field in a web model.
Example for your case: let's say you will have later an internal field in the database that only you can edit: "InternalNote". If you add that field to the model, anyone can post the data and edit the field while your intention was only to allow yourself to edit it.
Additionally this will solve your problem since you won't need to cast to other classes.
P.S. You can use your class directly in action methods:
MyAction([FromBody]UserModelWeb user)
It should be deserialized from json right away.
I am receiving an error message, "Sequence contains no elements" while trying to update a table in SQL from Angular 7 to an AspNet Core controller by passing two model parameters using an "http.post".
I am passing the data from the form to the class models with no problem because I can see the payload data in the browser console. However, when trying to pass the models as parameters in my api service to the controller, all of the parameters in the model are null. I usually don't have an issue when passing one model parm thru, but passing two of them to get to my controller with a [FromBody] doesn't seem to want to work for me.
I tried to wrap the models in curly brackets to pass them, to no avail:
UpdateService(serviceAddress: ServiceAddressModel, contact: ContactModel) {
let reqHeader = new HttpHeaders();
let body = { svc: serviceAddress, cnt: contact };
reqHeader.append('Content-Type', 'application/json');
return this.http.post(this.baseurl + 'api/customermanagement/update-service-address-info', body, { headers: reqHeader });
When I view the request / response in the browser console, I can see the data within the payload, so I know that the data is ready to pass.
My controller is set up as follows:
[Route("update-service-address-info")]
public bool UpdateServiceAddressAccount([FromBody] ServiceAddressEntity svc_id, [FromBody] ContactEntity cnt_id)
{
return serviceAddressService.UpdateServiceAddressAccount(svc_id, cnt_id);
}
Using breakpoints in this call shows null for all values.
If I can properly pass the parameters to my interface, I should be good-to-go. I am sensing that I am not structuring the parameters properly in the http.post body.
Your request body, { svc: serviceAddress, cnt: contact } is received as a json string, e.g. {"svc":{"serviceAddressProperty1":"value",...},"cnt":{"contactProperty1":"value",...}}. The parameters to your action method are bound via the default model binding mechanism (unless you provide your own custom model binding implementation). The default mechanism attempts to create instances by binding from the top level of the json object received with the request. enter code here
In simpler terms, lets assume you class ServiceAddressModel is defined like this:
public class ServiceAddressModel
{
public string Name { get; set; }
public string Property2 { get; set; }
}
the model binder looks for properties with the names "name" and "property2" at the top level of the json tree. If found, these are bound to the Name and Property2 properties of the created instance.
In your case, wrapping your models in a class that can make svc_id and cnt_id the top level properties would work fine. Like this example:
public class MyRequest
{
public ServiceAddressModel svc_id { get; set; }
public ContactEntity cnt_id { get; set; }
}
Then you can declare your action like
[Route("update-service-address-info")]
public bool UpdateServiceAddressAccount([FromBody] MyRequest request)
{
return serviceAddressService.UpdateServiceAddressAccount(request.svc_id, request.cnt_id);
}
Snake casing, camel casing should be allowed by default (you will have to try it, I havent tested that part). That is, if you declare your properties as SvcId and CntId (if you prefer more natural C# naming conventions) it should be able to bind correctly from JSONs with "svc_id" or "cnt_id".
Another option would be to implement custom model binders, but that might be a longer and more complex route.
Hope this helps.
Just try to pass the value like this and see
let body = { svc_id: serviceAddress, cnt_id: contact };
I have a method in Web API that I used the object as input, but when I try to run the API using URI the fields inside the object are Null.
this is my method:
[HttpGet]
[Route("AddUser/{user}")]
public async Task<string> CreateUser([FromUri]AddUser user)
{
//LoansApiTrace.Trace.Verbose(EventId.Start, () => string.Format("{0}: {1}", "AddUser", user.));
string Exception = await Repository.AddUserAsync(user);
return Exception;
}
This is AddUser object:
public class AddUser
{
public string UserEmailAddress { get; set; }
public string PasswordHash { get; set; }
public string Salt { get; set; }
public string RemoteRefNumber { get; set; }
}
and this is the URI:
http://localhost:59509/Adduser/user=test#yahoo.com,pass,salt,remref/
it goes to the method but UserEmailAddress , PasswordHash ,..all 4 are empty.
This is a really a bad practice to pass secret data through URI like you're doing. Then I will not attempt to give a solution for that to work.
The best practice is to pass that kind of data through your request body and use Http POST method :
[HttpPost]
[Route("AddUser/{userId}")]
public async Task<string> CreateUser(string userId, [FromBody]AddUser user)
{
// Find a user by userId
// Then update the user data.
}
you use an URI like this => http://localhost:59509/Adduser/12345 where 12345 is the user id.
you need to make sure that the selected HTTP method is POST
you need to write the data of AddUser into the request body
It also recommanded to use HTTPS when user need to send that type of data.
Consider Using POST If Applicable
While it may not be do-able, you may want to consider adding these fields within a <form> and simply posting them to the server in the body as opposed to using the actual URL itself. Passwords, salts and hashes generally aren't something that you want to get passed around like that.
If You Must Use A GET
Have you tried passing the values in as proper query-string parameters instead?
http://localhost:59509/Adduser/user?UserEmailAddress=test#yahoo.com&PasswordHash=abc&Salt=123&RemoteRefNumber=foo
This should set the following properties based on your current routes:
user = "user"
UserEmailAddress = "test#yahoo.com"
PasswordHash = "abc"
Salt = "123"
RemoteRefNumber = "foo"
MVC has to have some idea of how to bind these properties to those on your class, so unless the names match as expected, it will not know how to map something like "user" to "UserEmailAddress". As mentioned earlier, this isn't ideal and can present all sorts of security issues (so only use something like this on prototype / non-production environments).
I'm working on a REST API implementation in MVVM for Windows Universal Apps.
It is going fine, but I would like to solve one thing.
I have a Post and a Comment class on Model level, and they have Downvote and Upvote functions. The API calls are implemented in the ViewModel, including the calls that tell the server to do the downvote-upvote.
I would like to have the Models Downvote/Upvote functions to trigger the ViewModel's appropriate calls. Is it possible or am I going around it the wrong direction?
You're stuck where most programmers who begin with MVVM are stucked. You just look only at MVVM and strictly ignoring everything else.
What you see is: Model, ViewModel and View and you try to put all your business logic into one of these.
But the Model part is more than POCO Objects with a little bit of logic. Services also belong to the model. This is where you encapsulate all business logic which doesn't belong into a certain model.
You could implement a PostService class and an CommentService class, which both implement the UpVote/DownVote functionality and call these services from your ViewModel.
public interface ICommentService
{
void UpVote(Post post, Comment comment);
void DownVote(Post post, Comment comment);
}
public class CommentRestService
{
IRestClient client;
public CommentRestService(IRestClient client)
{
this.client = client;
}
public void UpVote(Post post, Comment comment)
{
var postId = post.Id;
var commentId = comment.Id;
var request = ...; // create your request and send it
var response = request.GetResponse();
// successfully submitted
if(response.Status == 200)
{
comment.VoteStatus = VoteType.Up;
comment.Score += 1;
}
}
public void DownVote(Post post, Comment comment)
{
var postId = post.Id;
var commentId = comment.Id;
var request = ...; // create your request and send it
var response = request.GetResponse();
// successfully submitted
if(response.Status == 200)
{
comment.VoteStatus = VoteType.Down;
comment.Score -= 1;
}
}
}
In your ViewModel you simply pass the service via Dependency Injection or get it via ServiceLocator and then use it's method, rather than calling UpVote/DownVote on the model.
// in ViewModel
// get via ServiceLocator or DI
ICommentService commentService = ...;
commentService.UpVote(this.Post, this.SelectedComment);
You could also implement this methods on the Model, to encapsulate the actions into the Comment class, i.e. by making Score and VoteStatus "private set;"
public class Comment
{
public string Comment { get; set; }
public Post Post { get; private set; }
public VoteType VoteStatus { get; private set; }
public int Score { get; private set; }
public void UpVote(ICommentService commentService)
{
// for this you'd change your Up/Vote method to return only true/false and not
// change the state of your model. On more complex operation, return an CommentResult
// containing all necessary information to update the comment class
if(commentService.UpVote(this.Post, this))
{
// only update the model, if the service operation was successful
this.Score++;
this.VoteStatus = VoteType.Up;
}
}
}
And call it via
SelectedComment.UpVote(commentService);
Later method is preferred, as you have more control of the Comment object and the Comment's state can only be modified via Comment and it's methods class. This prevents from accidentally changing this value somewhere else in code and receive inconsistent state (i.e. changing VoteStatus without incrementing the Score value).
Model
--> data and it's associated up-/ downvote information.
ViewModel
--> Implementation of up-/ downvoting.
View
--> triggers the up-/ downvote (e.g. commands).
Dependencies are chained like this View -> ViewModel-> Model. Thats the pattern.
I created a static class to hold user messages such as "Item successfully added" or "Password successfully changed", etc. Inside the class is a static dictionary which holds all the messages. The key of the dictionary is the UserId. I then have an Action which renders in _Layout.cshtml so that the messages will follow the user even if they are redirected off the page.
For example, I might allow a user to "Add" an item, and then once the item is added successfully, it will redirect to the List page for that item and then display the message "Item successfully added."
This worked great until I deployed to my production site and I noticed that the messages were "lagging". I would add an item and then it would redirect to the list page, but the message would not display. I would then navigate to somewhere else in the application and then the message would display on that page.
Any ideas why this would be happening?
Here's the code for my UserMessageManager
public static class UserMessageManager
{
private static readonly Dictionary<int, Queue<UserMessage>> UserMessages = new Dictionary<int, Queue<UserMessage>>();
public static void Add(int userId, string message)
{
if (string.IsNullOrWhiteSpace(message))
return;
if (!UserMessages.Keys.Contains(userId))
{
UserMessages.Add(userId, new Queue<UserMessage>());
}
UserMessages[userId].Enqueue(new UserMessage { Message = message});
}
public static List<UserMessage> Get(int userId)
{
if (!UserMessages.Keys.Contains(userId))
{
UserMessages.Add(userId, new Queue<UserMessage>());
}
var messages = new List<UserMessage>();
while (UserMessages[userId].Any())
{
messages.Add(UserMessages[userId].Dequeue());
}
return messages;
}
}
public class UserMessage
{
public string Message { get; set; }
}
EDIT: After some playing around, the messages will sometimes even "bunch up". I will add a messages to the dictionary after creating a few "items" and the messages will suddenly all display at once at a seemingly random time.
Firstly, if this is for a real site, I wouldn't take this approach anyway. If you want information persisted, use something persistent (e.g. a database). Otherwise when the AppDomain is recycled or the whole server goes down (you do have redundancy, right?) you'll lose all the messages. This approach also doesn't load balance nicely.
Secondly, it's just possible that you're seeing the results of the standard collection classes not being thread-safe. Without any synchronization or other memory barriers, it's possible that one thread isn't seeing the data written by another. It would also be entirely possible for two threads to write to the dictionary (or write to the same list) at the same time. You could just add some synchronization, e.g. have a lock object and lock on it for the entirety of each method. I'd start using a database instead though...