MvvmLight Messenger not triggering when var from outside is used - c#

I have a simple Message sender (the "RecordStore"), that sends that Message:
public class RecordStoreUpdatedMessage
{
public BaseModel Model { get; set; }
public RecordStoreUpdatedMessage(BaseModel model)
{
Model = model;
}
}
// somewhere in RecordStore:
var item = new BaseModel();
Messenger.Default.Send(new RecordStoreUpdatedMessage(item));
Then I got a receiver, that registers a callback to this Message:
Messenger.Default.Register<RecordStoreUpdatedMessage>(this, msg => {
Debug.WriteLine("DataTreeItemViewModel: cought RecordStoreUpdatedMessage");
//Debug.WriteLine("and the current item is " + anything);
});
Til there all is good, the Debug.WriteLine fires, I can get everything from RecordStoreUpdateMassage via 'msg'.
BUT
as soon as I introduce and use a local var (no matter what) in the receiver's callback none of the Debug.WriteLines fire anymore (I need that local var to check if the Updated Record really affects me or if I just can ignore it):
string anything = "Test";
Messenger.Default.Register<RecordStoreUpdatedMessage>(this, msg => {
Debug.WriteLine("DataTreeItemViewModel: cought RecordStoreUpdatedMessage");
Debug.WriteLine("and the current item is " + anything);
});
Result: nothing. No Error, no Debug.WriteLine.
Versions:
mvvmLight 5.4.1.1
.Net 4.6.1
Maybe relevant: sender and receiver live in 2 different projects/assemblies
I've studies several questions like Strange behavior with actions, local variables and garbage collection in MVVM light Messenger and mvvmlight messenger strange behaviour, but didn't find one that addresses that tiny difference of using a local var.
Nearly forgot to ask a specific question...:
Why is using a local var hindering the Messenger to fire the callback? What can I do to be able to use a local var in the callback?

I found a solution:
Making the callback not a lambda, but a reference to a method did th trick:
// in Constructor
Messenger.Default.Register<JeffData.Messages.RecordStoreUpdatedMessage>(this, UpdateItem);
private void UpdateItem(JeffData.Messages.RecordStoreUpdatedMessage recordStoreUpdatedMessage)
{
// here I can use ModelType now (a property of this class)
if (recordStoreUpdatedMessage.Model.GetType() == ModelType && recordStoreUpdatedMessage.Model.Id == Model.Id)
{
Debug.WriteLine("DataTreeItemViewModel: cought RecordStoreUpdatedMessage with item: " + recordStoreUpdatedMessage.Model.GetType().ToString());
Model = recordStoreUpdatedMessage.Model;
}
}
Still: I could understand that a local var cannot be used in a callback becouse of scope and GC issues. But not triggering the callback at all is strange...if not a bug.

Related

Why does my app crash when a button is clicked?

I am trying to make a friends function into my Unity game. Each friend will have their own line with their name and a few buttons (challenge, about, etc.).
I have a friend row prefab and I instantiate it into the parent list for each friend.
It works just fine, until I click the challenge button, which whould call a method that takes in two parameters: the UId of the friend, and their username (two strings).
I am using Firebase Realtime Database for database.
void RetrieveFriendList(object sender, ValueChangedEventArgs args) {
foreach(Transform childTransform in listParent.GetComponentInChildren<Transform>()) {
GameObject.Destroy(childTransform.gameObject);
}
friends.Clear();
foreach (DataSnapshot s in args.Snapshot.Children) {
friends.Add(s.Key);
GameObject newRow = Instantiate(friendRowPrefab);
newRow.transform.Find("Deny").gameObject.GetComponent<Button>().onClick.RemoveAllListeners();
newRow.transform.Find("Challenge").gameObject.GetComponent<Button>().onClick.RemoveAllListeners();
newRow.transform.SetParent(listParent.transform);
newRow.transform.localScale = new Vector3(1f, 1f, 1f);
newRow.transform.Find("Text_Name").gameObject.GetComponent<TMPro.TMP_Text>().text = s.Child("username").Value.ToString();
string retrievedStatus = s.Child("type").Value.ToString();
if (retrievedStatus == "sent") {
newRow.transform.Find("Status").gameObject.GetComponent<TMPro.TMP_Text>().text = "Friend request sent";
} else if (retrievedStatus == "request") {
newRow.transform.Find("Status").gameObject.GetComponent<TMPro.TMP_Text>().text = "Incoming friend request";
newRow.transform.Find("Accept").gameObject.SetActive(true);
newRow.transform.Find("Deny").gameObject.SetActive(true);
newRow.transform.Find("Accept").gameObject.GetComponent<Button>().onClick.AddListener(delegate { AcceptFriendRequest(s.Key); });
newRow.transform.Find("Deny").gameObject.GetComponent<Button>().onClick.AddListener(delegate { DenyFriendRequest(s.Key); });
} else if (retrievedStatus == "friends") {
newRow.transform.Find("Challenge").gameObject.SetActive(true);
Debug.Log(s.Key + " - " + s.Child("username").Value.ToString());
newRow.transform.Find("Challenge").gameObject.GetComponent<Button>().onClick.AddListener(delegate { ChallengeFriend(s.Key, s.Child("username").Value.ToString()); }); //this is the line that causes the crash
newRow.transform.Find("About").gameObject.SetActive(true);
newRow.transform.Find("Status").gameObject.SetActive(false);
}
}
FirebaseDatabase.DefaultInstance.GetReference("users").Child(auth.CurrentUser.UserId).Child("friends").ValueChanged -= RetrieveFriendList;
}
What's likely happening is that the underlying C++ representation of your database snapshot is being cleaned up before your button accesses it. See this bug.
The easiest thing to do would be to find this line:
newRow.transform.Find("Challenge").gameObject.GetComponent<Button>().onClick.AddListener(delegate { ChallengeFriend(s.Key, s.Child("username").Value.ToString()); }); //this is the line that causes the crash
and turn it into something like:
var challengeKey = s.Key;
var challengeUsername = s.Child("username").Value.ToString();
newRow.transform.Find("Challenge").gameObject.GetComponent<Button>().onClick.AddListener(delegate { ChallengeFriend(challengeKey, challengeUsername); });
This way you retrieve the values you need (key and username) at the time of the callback rather than in the context of the button click (at some arbitrary point in the future after this function has returned). If you still get a crash, you may have to .Clone or .CopyTo the data -- but I believe that once an object is retrieved from the underlying snapshot it should be a full on C# object obeying the expected C# GC rules.
You also may experience null reference exceptions if the snapshot hits a local cache first -- so make sure you have null checks around everything (generally a good practice whenever you're hitting the web).

Removing a command from DiscordBot CommandService

I am trying to figure out how to remove a command from the discord bot after it has been created. Here is how I create the command:
_commandService.CreateCommand("create").Parameter("message", ParameterType.Multiple).Do(async e =>
{
var message = e.Args.Skip(1).Aggregate("", (current, t) => current + (t + " "));;
_commandService.CreateCommand("hello").Do(async cc =>
{
await e.User.SendMessage(customCommand.Message);
});
});
The _commandService object is of type Discord.Commands.CommandService
Now, I want to be able to run:
_commandService.CreateCommand("delete").Parameter("message", ParameterType.Multiple).Do(async e =>
{
_commandService.DeleteCommand("hello");
});
However, no such method exists, nor am I able to access the commands inside _commandService object as everything is read only get;
Does anyone know how I can delete the command without having to restart the bot?
It's possible, but as of discord.net 1.0 you need to use the Modules system to do it. Unfortunately, it greatly complicates things. Hopefully they'll add a proper DeleteCommand(string commandName) in a future update.
Why you need to do this (this section not needed if you don't care about the discord.net source): The
class CommandMap (it stores the commands, unsurprisingly) exposes a method RemoveCommand that does what you're looking to do. The only reference to an object of this class in the source is in the private method RemoveModuleInternal in CommandService. This is exposed in one of two public methods: RemoveModuleAsync(ModuleInfo module) or RemoveModuleAsync<T>(). There is no other way to affect commands as of the 1.0 release.
How to do it: Get the ModuleInfo object for your module first. Preferably, the module you create will only contain the command you want to delete for reasons that should be obvious pretty soon. When you use CreateModuleAsync(string primaryAlias, Action<ModuleBuilder> buildFunc) (or one of the other methods used to add modules) you'll get the ModuleInfo object back. This does mean you need to use a ModuleBuilder instead of the simple commandService.CreateCommand method you use. Read up on how to do that here... if the process still confuses you, it's an excellent topic for another question.
You need to keep track of the ModuleInfo object that CreateModuleAsync returns in some manner (the method I would use is below) and then your second command becomes:
// private ModuleInfo createInfo
_commandService.CreateCommand("delete").Parameter("message", ParameterType.Multiple).Do(async e =>
{
if (createInfo != null)
{
await _commandService.DeleteModuleAsync(createInfo);
}
});
Do note that the entire module instance is getting deleted... that's why your "create" command should be the only thing in it.
An alternate solution (although significantly less elegant) if this whole Module business seems too complicated would be to store a boolean and simply toggle it to simulate the deletion of the command. That is:
// bool createNotDeleted = true;
_commandService.CreateCommand("create").Parameter("message", ParameterType.Multiple).Do(async e =>
{
if (createNotDeleted)
{
var message = e.Args.Skip(1).Aggregate("", (current, t) => current + (t + " "));;
_commandService.CreateCommand("hello").Do(async cc =>
{
await e.User.SendMessage(customCommand.Message);
});
}
else
{
// error handling
}
});
and
_commandService.CreateCommand("delete").Parameter("message", ParameterType.Multiple).Do(async e =>
{
if (createNotDeleted)
{
createNotDeleted = false
// return something indicating command is deleted
}
else
{
// error handling
}
});

Nopcommerce Update entity issue

Using NopCommerce 3.8, Visual Studio 2015 proff.
I have created a plugin that is responsible for making restful calls to my Web API that exposes a different DB to that of Nop.
The process is run via a nop Task, it successfully pulls the data back and i can step through and manipulate as i see fit, no issues so far.
Issue comes when i try to update a record on the product table, i perform the update... but nothing happens no change, no error.
I believe this is due to the Context having no idea about my newly instantiated product object, however I'm drawing a blank on what i need to do in relation to my particular example.
Similar questions usually reference a "model" object that is part of the parameter of the method call, "model" has the method ToEntity which seems to be the answer in similar question in stack.
However my example doesn't have the ToEntity class/method possibly because my parameter is actually a list of products. To Clarify here my code.
Method in RestClient.cs
public async Task<List<T>> GetAsync()
{
try
{
var httpClient = new HttpClient();
var json = await httpClient.GetStringAsync(ApiControllerURL);
var taskModels = JsonConvert.DeserializeObject<List<T>>(json);
return taskModels;
}
catch (Exception e)
{
return null;
}
}
Method in my Service Class
public async Task<List<MWProduct>> GetProductsAsync()
{
RestClient<MWProduct> restClient = new RestClient<MWProduct>(ApiConst.Products);
var productsList = await restClient.GetAsync();
InsertSyncProd(productsList.Select(x => x).ToList());
return productsList;
}
private void InsertSyncProd(List<MWProduct> inserted)
{
var model = inserted.Select(x =>
{
switch (x.AD_Action)
{
case "I":
//_productService.InsertProduct(row);
break;
case "U":
UpdateSyncProd(inserted);
.....
Then the method to bind and update
private void UpdateSyncProd(List<MWProduct> inserted)
{
var me = inserted.Select(x =>
{
var productEnt = _productRepos.Table.FirstOrDefault(ent => ent.Sku == x.Sku.ToString());
if(productEnt != null)
{
productEnt.Sku = x.Sku.ToString();
productEnt.ShortDescription = x.ShortDescription;
productEnt.FullDescription = x.FullDescription;
productEnt.Name = x.Name;
productEnt.Height = x.Pd_height != null ? Convert.ToDecimal(x.Pd_height) : 0;
productEnt.Width = x.Pd_width != null ? Convert.ToDecimal(x.Pd_width) : 0;
productEnt.Length = x.Pd_depth != null ? Convert.ToDecimal(x.Pd_depth) : 0;
productEnt.UpdatedOnUtc = DateTime.UtcNow;
}
//TODO: set to entity so context nows and can update
_productService.UpdateProduct(productEnt);
return productEnt;
});
}
So as you can see, I get the data and pass data through to certain method based on a result. From that list in the method I iterate over, and pull the the entity from the table, then update via the product service using that manipulated entity.
So what am I missing here, I'm sure its 1 step, and i think it may be either be because 1) The context still has no idea about the entity in question, or 2) Its Incorrect calls.
Summary
Update is not updating, possibly due to context having no knowledge OR my methodology is wrong. (probably both).
UPDATE:
I added some logger.inertlog all around my service, it runs through fine, all to the point of the call of update. But again I check the product and nothing has changed in the admin section.
plugin
I have provided the full source as i think maybe this has something to do with the rest of the code setup possibly?
UPDATE:
Added the following for testin on my execute method.
var myprod = _productRepos.GetById(4852);
myprod.ShortDescription = "db test";
productRepos.Update(myprod);
This successfully updates the product description. I moved my methods from my service into the task class but still no luck. The more i look at it the more im thinking that my async is killing off the db context somehow.
Turned of async and bound the getbyid to a new product, also removed the lambda for the switch and changed it to a foreach loop. Seems to finally update the results.
Cannot confirm if async is the culprit, currently the web api seems to be returning the same result even though the data has changed (some wierd caching by deafult in .net core? ) so im creating a new question for that.
UPDATE: It appears that the issue stems from poor debugging of async. Each instance I am trying to iterate over an await call, simply put im trying to iterate over a collection that technically may or may not be completed yet. And probably due to poor debugging, I was not aware.
So answer await your collection Then iterate after.

Filter Change Notifications in Active Directory: Create, Delete, Undelete

I am currently using the Change Notifications in Active Directory Domain Services in .NET as described in this blog. This will return all events that happen on an selected object (or in the subtree of that object). I now want to filter the list of events for creation and deletion (and maybe undeletion) events.
I would like to tell the ChangeNotifier class to only observe create-/delete-/undelete-events. The other solution is to receive all events and filter them on my side. I know that in case of the deletion of an object, the atribute list that is returned will contain the attribute isDeleted with the value True. But is there a way to see if the event represents the creation of an object? In my tests the value for usnchanged is always usncreated+1 in case of userobjects and both are equal for OUs, but can this be assured in high-frequency ADs? It is also possible to compare the changed and modified timestamp. And how can I tell if an object has been undeleted?
Just for the record, here is the main part of the code from the blog:
public class ChangeNotifier : IDisposable
{
static void Main(string[] args)
{
using (LdapConnection connect = CreateConnection("localhost"))
{
using (ChangeNotifier notifier = new ChangeNotifier(connect))
{
//register some objects for notifications (limit 5)
notifier.Register("dc=dunnry,dc=net", SearchScope.OneLevel);
notifier.Register("cn=testuser1,ou=users,dc=dunnry,dc=net", SearchScope.Base);
notifier.ObjectChanged += new EventHandler<ObjectChangedEventArgs>(notifier_ObjectChanged);
Console.WriteLine("Waiting for changes...");
Console.WriteLine();
Console.ReadLine();
}
}
}
static void notifier_ObjectChanged(object sender, ObjectChangedEventArgs e)
{
Console.WriteLine(e.Result.DistinguishedName);
foreach (string attrib in e.Result.Attributes.AttributeNames)
{
foreach (var item in e.Result.Attributes[attrib].GetValues(typeof(string)))
{
Console.WriteLine("\t{0}: {1}", attrib, item);
}
}
Console.WriteLine();
Console.WriteLine("====================");
Console.WriteLine();
}
LdapConnection _connection;
HashSet<IAsyncResult> _results = new HashSet<IAsyncResult>();
public ChangeNotifier(LdapConnection connection)
{
_connection = connection;
_connection.AutoBind = true;
}
public void Register(string dn, SearchScope scope)
{
SearchRequest request = new SearchRequest(
dn, //root the search here
"(objectClass=*)", //very inclusive
scope, //any scope works
null //we are interested in all attributes
);
//register our search
request.Controls.Add(new DirectoryNotificationControl());
//we will send this async and register our callback
//note how we would like to have partial results
IAsyncResult result = _connection.BeginSendRequest(
request,
TimeSpan.FromDays(1), //set timeout to a day...
PartialResultProcessing.ReturnPartialResultsAndNotifyCallback,
Notify,
request
);
//store the hash for disposal later
_results.Add(result);
}
private void Notify(IAsyncResult result)
{
//since our search is long running, we don't want to use EndSendRequest
PartialResultsCollection prc = _connection.GetPartialResults(result);
foreach (SearchResultEntry entry in prc)
{
OnObjectChanged(new ObjectChangedEventArgs(entry));
}
}
private void OnObjectChanged(ObjectChangedEventArgs args)
{
if (ObjectChanged != null)
{
ObjectChanged(this, args);
}
}
public event EventHandler<ObjectChangedEventArgs> ObjectChanged;
#region IDisposable Members
public void Dispose()
{
foreach (var result in _results)
{
//end each async search
_connection.Abort(result);
}
}
#endregion
}
public class ObjectChangedEventArgs : EventArgs
{
public ObjectChangedEventArgs(SearchResultEntry entry)
{
Result = entry;
}
public SearchResultEntry Result { get; set; }
}
I participated in a design review about five years back on a project that started out using AD change notification. Very similar questions to yours were asked. I can share what I remember, and don't think things have change much since then. We ended up switching to DirSync.
It didn't seem possible to get just creates & deletes from AD change notifications. We found change notification resulted enough events monitoring a large directory that notification processing could bottleneck and fall behind. This API is not designed for scale, but as I recall the performance/latency were not the primary reason we switched.
Yes, the usn relationship for new objects generally holds, although I think there are multi-dc scenarios where you can get usncreated == usnchanged for a new user, but we didn't test that extensively, because...
The important thing for us was that change notification only gives you reliable object creation detection under the unrealistic assumption that your machine is up 100% of the time! In production systems there are always some case where you need to reboot and catch up or re-synchronize, and we switched to DirSync because it has a robust way to handle those scenarios.
In our case it could block email to a new user for an indeterminate time if an object create were missed. That obviously wouldn't be good, we needed to be sure. For AD change notifications, getting that resync right that would have some more work and hard to test. But for DirSync, its more natural, and there's a fast-path resume mechanism that usually avoids resync. For safety I think we triggered a full re-synchronize every day.
DirSync is not as real-time as change notification, but its possible to get ~30-second average latency by issuing the DirSync query once a minute.

How to expose IObservable<T> properties without using Subject<T> backing field

In this answer to a question about Subject<T> Enigmativity mentioned:
as an aside, you should try to avoid using subjects at all. The
general rule is that if you're using a subject then you're doing
something wrong.
I often use subjects as backing fields for IObservable properties, which would have probably been .NET events in the days before Rx. e.g. instead of something like
public class Thing
{
public event EventHandler SomethingHappened;
private void DoSomething()
{
Blah();
SomethingHappened(this, EventArgs.Empty);
}
}
I might do
public class Thing
{
private readonly Subject<Unit> somethingHappened = new Subject<Unit>();
public IObservable<Unit> SomethingHappened
{
get { return somethingHappened; }
}
private void DoSomething()
{
Blah();
somethingHappened.OnNext(Unit.Default);
}
}
So, if I want to avoid using Subject what would be the correct way of doing this kind of thing? Or I should I stick to using .NET events in my interfaces, even when they'll be consumed by Rx code (so probably FromEventPattern)?
Also, a bit more details on why using Subject like this is a bad idea would be helpful.
Update: To make this question a bit more concrete, I'm talking about using Subject<T> as a way to get from non-Rx code (maybe you're working with some other legacy code) into the Rx world. So, something like:
class MyVolumeCallback : LegacyApiForSomeHardware
{
private readonly Subject<int> volumeChanged = new Subject<int>();
public IObservable<int> VolumeChanged
{
get
{
return volumeChanged.AsObservable();
}
}
protected override void UserChangedVolume(int newVolume)
{
volumeChanged.OnNext(newVolume);
}
}
Where, instead of using events, the LegacyApiForSomeHardware type makes you override virtual methods as a way of getting "this just happened" notifications.
For one thing, someone can cast the SomethingHappened back to an ISubject and feed things into it from the outside. At the very least, apply AsObservable to it in order to hide the subject-ness of the underlying object.
Also, subject broadcasting of callbacks doesn't do strictly more than a .NET event. For example, if one observer throws, the ones that are next in the chain won't be called.
static void D()
{
Action<int> a = null;
a += x =>
{
Console.WriteLine("1> " + x);
};
a += x =>
{
Console.WriteLine("2> " + x);
if (x == 42)
throw new Exception();
};
a += x =>
{
Console.WriteLine("3> " + x);
};
a(41);
try
{
a(42); // 2> throwing will prevent 3> from observing 42
}
catch { }
a(43);
}
static void S()
{
Subject<int> s = new Subject<int>();
s.Subscribe(x =>
{
Console.WriteLine("1> " + x);
});
s.Subscribe(x =>
{
Console.WriteLine("2> " + x);
if (x == 42)
throw new Exception();
});
s.Subscribe(x =>
{
Console.WriteLine("3> " + x);
});
s.OnNext(41);
try
{
s.OnNext(42); // 2> throwing will prevent 3> from observing 42
}
catch { }
s.OnNext(43);
}
In general, the caller is dead once an observer throws, unless you protect every On* call (but don't swallow exceptions arbitrarily, as shown above). This is the same for multicast delegates; exceptions will swing back at you.
Most of the time, you can achieve what you want to do without a subject, e.g. by using Observable.Create to construct a new sequence. Such sequences don't have an "observer list" that results from multiple subscriptions; each observer has its own "session" (the cold observable model), so an exception from an observer is nothing more than a suicide command in a confined area rather than blowing yourself up in the middle of a square.
Essentially, subjects are best used at the edges of the reactive query graph (for ingress streams that need to be addressable by another party that feeds in the data, though you could use regular .NET events for this and bridge them to Rx using FromEvent* methods) and for sharing subscriptions within a reactive query graph (using Publish, Replay, etc. which are Multicast calls in disguise, using a subject). One of the dangers of using subjects - which are very stateful due to their observer list and potential recording of messages - is to use them when trying to write a query operator using subjects. 99.999% of the time, such stories have a sad ending.
In an answer on the Rx forum, Dave Sexton (of Rxx), said as part an answer to something:
Subjects are the stateful components of Rx. They are useful for when
you need to create an event-like observable as a field or a local
variable.
Which is exactly what's happening with this question, he also wrote an in-depth follow up blog post on To Use Subject Or Not To Use Subject? which concludes with:
When should I use a subject?
When all of the following are true:
you don't have an observable or anything that can be converted into one.
you require a hot observable.
the scope of your observable is a type.
you don't need to define a similar event and no similar event already exists.
Why should I use a subject in that case?
Because you've got no choice!
So, answering the inner question of "details on why using Subject like this is a bad idea" - it's not a bad idea, this is one of the few places were using a Subject is the correct way to do things.
While I can't speak for Enigmativity directly, I imagine it's because it's very low-level, something you don't really need to use directly; everything that's offered by the Subject<T> class can be achieved by using the classes in the System.Reactive.Linq namespace.
Taking the example from the Subject<T> documentation:
Subject<string> mySubject = new Subject<string>();
//*** Create news feed #1 and subscribe mySubject to it ***//
NewsHeadlineFeed NewsFeed1 = new NewsHeadlineFeed("Headline News Feed #1");
NewsFeed1.HeadlineFeed.Subscribe(mySubject);
//*** Create news feed #2 and subscribe mySubject to it ***//
NewsHeadlineFeed NewsFeed2 = new NewsHeadlineFeed("Headline News Feed #2");
NewsFeed2.HeadlineFeed.Subscribe(mySubject);
This is easily achieved with the Merge extension method on the Observable class:
IObservable<string> feeds =
new NewsHeadlineFeed("Headline News Feed #1").HeadlineFeed.Merge(
new NewsHeadlineFeed("Headline News Feed #2").HeadlineFeed);
Which you can then subscribe to normally. Using Subject<T> just makes the code more complex. If you're going to use Subject<T> then you should be doing some very low-level processing of observables where the extension methods fail you.
One approach for classes which have simple one-off events, is to provide a ToObservable method which creates a meaningful cold observable based on an event.
This is more readable than using the Observable factory methods, and allows developers who don't use Rx to make use of the API.
public IObservable<T> ToObservable()
{
return Observable.Create<T>(observer =>
{
Action notifier = () =>
{
switch (Status)
{
case FutureStatus.Completed:
observer.OnNext(Value);
observer.OnCompleted();
break;
case FutureStatus.Cancelled:
observer.OnCompleted();
break;
case FutureStatus.Faulted:
observer.OnError(Exception);
break;
}
};
Resolve += notifier;
return () => Resolve -= notifier;
});
}

Categories