Register a SA Forums Account here!
JOINING THE SA FORUMS WILL REMOVE THIS BIG AD, THE ANNOYING UNDERLINED ADS, AND STUPID INTERSTITIAL ADS!!!

You can: log in, read the tech support FAQ, or request your lost password. This dumb message (and those ads) will appear on every screen until you register! Get rid of this crap by registering your own SA Forums Account and joining roughly 150,000 Goons, for the one-time price of $9.95! We charge money because it costs us money per month for bills, and since we don't believe in showing ads to our users, we try to make the money back through forum registrations.
 
  • Post
  • Reply
New Yorp New Yorp
Jul 18, 2003

Only in Kenya.
Pillbug

EmmyOk posted:

The guy has a follow up video and he explained why starting a new thread was a bad idea even if you knew the workarounds to make everything work as intended. I think he says the final version that "works" is basically pure look that in this case it works as you want.

Your explanation is really really great and I appreciate it a lot! If possible could explain why the text update doesn't typically get processed first?

e: I'm rushing into work soon or I'd try myself but in the given example code if I appended the label rather than replaced it I would get both pieces of text concatenated at the end after the "freezing"?



Thank you also!

The label will get updated, but then the UI will immediately freeze and turn into a grey block until the blocking operation finishes.

Adbot
ADBOT LOVES YOU

Dietrich
Sep 11, 2001

EmmyOk posted:

The guy has a follow up video and he explained why starting a new thread was a bad idea even if you knew the workarounds to make everything work as intended. I think he says the final version that "works" is basically pure look that in this case it works as you want.

Your explanation is really really great and I appreciate it a lot! If possible could explain why the text update doesn't typically get processed first?

e: I'm rushing into work soon or I'd try myself but in the given example code if I appended the label rather than replaced it I would get both pieces of text concatenated at the end after the "freezing"?



Thank you also!

Because the text update actually involves asynchronous operations at the OS level, which are completely invisible to you in most circumstances and usually occur so fast they're effectively synchronous. When it answers back to the framework code with, say, the window handler for the textbox that the framework has been asked to update, the framework is too consumed with doing your long running operation to process that interrupt and perform the update, so it gets processed once the long running operation completes, then immediately gets updated again with the "it's complete now" message.

Munkeymon
Aug 14, 2003

Motherfucker's got an
armor-piercing crowbar! Rigoddamndicu𝜆ous.




Right, it's just easier for me to think of it that way because I'm way more used to process/thread-level separation, but sometimes there is so assume the worst and hope for the best.

Munkeymon
Aug 14, 2003

Motherfucker's got an
armor-piercing crowbar! Rigoddamndicu𝜆ous.



I'm trying to mess around with libsodium before I add it to our main product's project, so I made a console project, added the libsodium-net package, wrote two lines to see what the default output of an Argon hash looks like and now the project won't build because something in the package's Baseclass.Contrib.Nuget.Output.targets is trying to load a nonexistent DLL :psyduck:

Who the hell do I even report this to?

Full error in case I'm not reading this right:

C:\LiveFireProvingGround\Sodium\packages\Baseclass.Contrib.Nuget.Output.2.1.0\build\net40\Baseclass.Contrib.Nuget.Output.targets(73,5): error MSB4175: The task factory "CodeTaskFactory" could not be loaded from the assembly "C:\Program Files (x86)\Microsoft Visual Studio\2017\Professional\MSBuild\15.0\Bin\Microsoft.Build.Tasks.v15.0.dll". Could not load file or assembly 'file:///C:\Program Files (x86)\Microsoft Visual Studio\2017\Professional\MSBuild\15.0\Bin\Microsoft.Build.Tasks.v15.0.dll' or one of its dependencies. The system cannot find the file specified.

Microsoft.Build.Tasks.Core.dll does exist and is what I assume it's supposed to use, but maybe that other one is missing? gently caress IDK.

Can't get the stupid thing to run in LinqPad, either :sigh:

Dietrich
Sep 11, 2001

Munkeymon posted:

I'm trying to mess around with libsodium before I add it to our main product's project, so I made a console project, added the libsodium-net package, wrote two lines to see what the default output of an Argon hash looks like and now the project won't build because something in the package's Baseclass.Contrib.Nuget.Output.targets is trying to load a nonexistent DLL :psyduck:

Who the hell do I even report this to?

Full error in case I'm not reading this right:

C:\LiveFireProvingGround\Sodium\packages\Baseclass.Contrib.Nuget.Output.2.1.0\build\net40\Baseclass.Contrib.Nuget.Output.targets(73,5): error MSB4175: The task factory "CodeTaskFactory" could not be loaded from the assembly "C:\Program Files (x86)\Microsoft Visual Studio\2017\Professional\MSBuild\15.0\Bin\Microsoft.Build.Tasks.v15.0.dll". Could not load file or assembly 'file:///C:\Program Files (x86)\Microsoft Visual Studio\2017\Professional\MSBuild\15.0\Bin\Microsoft.Build.Tasks.v15.0.dll' or one of its dependencies. The system cannot find the file specified.

Microsoft.Build.Tasks.Core.dll does exist and is what I assume it's supposed to use, but maybe that other one is missing? gently caress IDK.

Can't get the stupid thing to run in LinqPad, either :sigh:

Try sodium.core package instead. The packaging on the sodium-net is kinda lovely.

Munkeymon
Aug 14, 2003

Motherfucker's got an
armor-piercing crowbar! Rigoddamndicu𝜆ous.



Dietrich posted:

Try sodium.core package instead. The packaging on the sodium-net is kinda lovely.

Exactly the same error, so at least that rules out the library maintainers

:doh: Baseclass.Contrib.Nuget.Output is a libsodium-net dependency and VS doesn't bother to delete unused dependencies when you remove a package. I was thinking it was a normal part of NuGet.

Packaging on Core sucks, too, turns out!

Or maybe copying a file to the output directory is just a really hard problem to solve in the context of a package manager :downs:

Munkeymon fucked around with this message at 20:46 on Nov 14, 2017

Mr Shiny Pants
Nov 12, 2012

Munkeymon posted:

Exactly the same error, so at least that rules out the library maintainers

:doh: Baseclass.Contrib.Nuget.Output is a libsodium-net dependency and VS doesn't bother to delete unused dependencies when you remove a package. I was thinking it was a normal part of NuGet.

Packaging on Core sucks, too, turns out!

Or maybe copying a file to the output directory is just a really hard problem to solve in the context of a package manager :downs:

Try Paket? I've heard good things about it.

Munkeymon
Aug 14, 2003

Motherfucker's got an
armor-piercing crowbar! Rigoddamndicu𝜆ous.



Mr Shiny Pants posted:

Try Paket? I've heard good things about it.

I think it's a problem with the package itself because there's a closed bug about basically the same issue but I'm thinking maybe they weren't diligent in their testing.

kitten emergency
Jan 13, 2008

get meow this wack-ass crystal prison
i solved my problem, I made a snk instead of a pfx and just replaced the references to the pfx and it works fine :shepface:

Mr Shiny Pants
Nov 12, 2012

Munkeymon posted:

I think it's a problem with the package itself because there's a closed bug about basically the same issue but I'm thinking maybe they weren't diligent in their testing.

Not a lot a packagemanager can do if the package itself is broken.

EssOEss
Oct 23, 2006
128-bit approved
NuGet packaging has turned into a buggy overcomplicated mess to such an extent that I am thinking back to manually managed DLL files in "lib" directories with fondness.

B-Nasty
May 25, 2005

When the Nuget packages are small and self-contained (i.e. no dependencies), it works great. As soon as the packages start referencing others, it becomes DLL hell of the highest order. If you haven't had to deal with: "conflicts between different versions of the same dependent assembly" yet, consider yourself lucky.

The binding redirect thing is a total hack, and I wish Nuget was smarter about warning you when you try to install a package that references a package you have installed, but a different version. Nuget should assume that I do not, under any circumstances, want to break what I have when I install a new package.

dick traceroute
Feb 24, 2010

Open the pod bay doors, Hal.
Grimey Drawer

Essential posted:

Thanks for the info! Yeah I have the same question as Opulent Ceremony, can you elaborate on what that means? What exactly is "sql as function parameters" referring to? Are you talking about passing in a connection object, command object, something else? I may be missing the mark here entirely but the db side of this is quite the mystery to me right now.

Sorry for the delay guys! Yeah, I was talking about specific bindings. I'm not at work right now, I may be mis-remembering using bindings for Azure Sql. I can check in the morning and post back :)

We definitely had issues coding directly at Cosmos DB, and we solved that by using the provided bindings.

Essential
Aug 14, 2003

dick traceroute posted:

Sorry for the delay guys! Yeah, I was talking about specific bindings. I'm not at work right now, I may be mis-remembering using bindings for Azure Sql. I can check in the morning and post back :)

We definitely had issues coding directly at Cosmos DB, and we solved that by using the provided bindings.

No sweat and thanks for any info you can provide! Yes, please post back when you can ;)

TheBlackVegetable
Oct 29, 2006

B-Nasty posted:

When the Nuget packages are small and self-contained (i.e. no dependencies), it works great. As soon as the packages start referencing others, it becomes DLL hell of the highest order. If you haven't had to deal with: "conflicts between different versions of the same dependent assembly" yet, consider yourself lucky.

The binding redirect thing is a total hack, and I wish Nuget was smarter about warning you when you try to install a package that references a package you have installed, but a different version. Nuget should assume that I do not, under any circumstances, want to break what I have when I install a new package.

I had a crash in production that was caused by an update to a dependency of a dependency that was incompatible (they had broken their API contract and only changed a minor version number, yay semantic versioning)

So now I use Paket and pin down the version of all packages I use, then grab all of their dependencies (from the paket.lock file) and pin those versions down too. If you're using nuget and are having problems with transitive dependencies, I recommend switching to paket. Unless nuget has had an update that I'm not aware of, it is total poo poo for managing conflicting dependencies.

B-Nasty
May 25, 2005


Looks cool. I had heard of it before, but never really dug into the documentation. I really like the transitive dependencies all listed, hierarchically, in a single file.

Half the issue with what I'll dub NugetHell is that it doesn't give you readily actionable error messages. You do (sometimes) get a compiler warning, but to dig into what happened, you have to compile at a high verbosity and try to interpret what dependencies are in conflict and then manually look at what they reference. I'm so beaten down that I typically commit between individual Nuget package updates/additions so I can quickly isolate the offender and compare diffs to see why it's ruining my day.

chippy
Aug 16, 2006

OK I DON'T GET IT
So I've run into two really annoying problems with Entity Framework and lazy loading this morning that I can't believe are still an issue in EF 6.

1. You can't just null out a navigation property, you have to have accessed it first, triggering lazy loading, before you can set it to null.
2. A navigation property with the [Required] attribute fails validation on calling SaveChanges(), if it hasn't been accessed (i.e. lazy loaded) at some point.

Does anyone know of any nice, clean solutions for these, other than to manually access the getters, or to use Include() when accessing them? For point 1, I've just implemented custom setter logic that access the property first if the value is null, but I can't find a nice solution to problem 2 at all.

According to the top answer on https://stackoverflow.com/questions/6038541/ef-validation-failing-on-update-when-using-lazy-loaded-required-properties , #2 is symptomatic of how the change tracker works. Which is really annoying.

The weird thing is, MSDN explicitly states that #1 shouldn't be a problem in EF 6:

quote:

To delete the relationship, set the navigation property to null. If you are working with the Entity Framework that is based on .NET 4.0, then the related end needs to be loaded before you set it to null. For example:
context.Entry(course).Reference(c => c.Department).Load();
course.Department = null;
Starting with the Entity Framework 5.0, that is based on .NET 4.5, you can set the relationship to null without loading the related end. You can also set the current value to null using the following method.
context.Entry(course).Reference(c => c.Department).CurrentValue = null;


I think exposing the foreign key properties on the classes would help, but unfortunately someone set a precedent of not doing that on this project, and I don't really want to break the pattern.

chippy fucked around with this message at 12:25 on Nov 16, 2017

Munkeymon
Aug 14, 2003

Motherfucker's got an
armor-piercing crowbar! Rigoddamndicu𝜆ous.



The Libsodium.Core guys told me they don't really support not-Core, but Baseclass.Contrib.Nuget.Output fixed their poo poo so there's no build error when using libsodium-net. Yay!

I did have to delete my whole test project and start over, though, because somehow it got stuck on the wrong Sodium wrapper DLL (I'm guessing here) when I switched NuGet packages. ~just NuGet things~ I guess, but I'm glad I did this experimentation in a throwaway solution.

fankey
Aug 31, 2001

Given a list of IP addresses, I'm using the following to determine the first address to response with a valid HTTP response
code:
    static async Task<string> DownloadCheck(HttpClient client, string ip)
    {
      try
      {
        var rsp = await client.GetAsync( "http://"+ip, HttpCompletionOption.ResponseHeadersRead);
        if (rsp.StatusCode != System.Net.HttpStatusCode.OK) ip = null;
      }
      catch( TaskCanceledException)
      {
        // expected...
      }
      catch(Exception)
      {
        // not able to contact ip
        ip = null;
      }
      return ip;
    }

    static async Task<string> GetBestIpAsyncInternal(IEnumerable<string> ips)
    {
      using (HttpClient client = new HttpClient() { Timeout = TimeSpan.FromSeconds(2) })
      {
        IEnumerable<Task<string>> requests = ips.Select(ip => DownloadCheck(client, ip));
        Task<string> winner = await Task.WhenAny(requests);
        return await winner;
      }
    }
The issue is that if a given address fails and returns null before a valid response then my code returns null. Ideally I could call something like Task.WhenAnyEqual(requests, (ip)=> ip != null ) but such a thing doesn't exist. Any ideas how to accomplish this? I don't want to use Task.WhenAll because I don't want to wait around for some calls to timeout before returning a response.

mystes
May 31, 2006

fankey posted:

Given a list of IP addresses, I'm using the following to determine the first address to response with a valid HTTP response
code:
    static async Task<string> DownloadCheck(HttpClient client, string ip)
    {
      try
      {
        var rsp = await client.GetAsync( "http://"+ip, HttpCompletionOption.ResponseHeadersRead);
        if (rsp.StatusCode != System.Net.HttpStatusCode.OK) ip = null;
      }
      catch( TaskCanceledException)
      {
        // expected...
      }
      catch(Exception)
      {
        // not able to contact ip
        ip = null;
      }
      return ip;
    }

    static async Task<string> GetBestIpAsyncInternal(IEnumerable<string> ips)
    {
      using (HttpClient client = new HttpClient() { Timeout = TimeSpan.FromSeconds(2) })
      {
        IEnumerable<Task<string>> requests = ips.Select(ip => DownloadCheck(client, ip));
        Task<string> winner = await Task.WhenAny(requests);
        return await winner;
      }
    }
The issue is that if a given address fails and returns null before a valid response then my code returns null. Ideally I could call something like Task.WhenAnyEqual(requests, (ip)=> ip != null ) but such a thing doesn't exist. Any ideas how to accomplish this? I don't want to use Task.WhenAll because I don't want to wait around for some calls to timeout before returning a response.
If you don't need to be able to cancel the remaining requests, couldn't you use parallel linq or something?

NiceAaron
Oct 19, 2003

Devote your hearts to the cause~

fankey posted:

Given a list of IP addresses, I'm using the following to determine the first address to response with a valid HTTP response
code:
    static async Task<string> DownloadCheck(HttpClient client, string ip)
    {
      try
      {
        var rsp = await client.GetAsync( "http://"+ip, HttpCompletionOption.ResponseHeadersRead);
        if (rsp.StatusCode != System.Net.HttpStatusCode.OK) ip = null;
      }
      catch( TaskCanceledException)
      {
        // expected...
      }
      catch(Exception)
      {
        // not able to contact ip
        ip = null;
      }
      return ip;
    }

    static async Task<string> GetBestIpAsyncInternal(IEnumerable<string> ips)
    {
      using (HttpClient client = new HttpClient() { Timeout = TimeSpan.FromSeconds(2) })
      {
        IEnumerable<Task<string>> requests = ips.Select(ip => DownloadCheck(client, ip));
        Task<string> winner = await Task.WhenAny(requests);
        return await winner;
      }
    }
The issue is that if a given address fails and returns null before a valid response then my code returns null. Ideally I could call something like Task.WhenAnyEqual(requests, (ip)=> ip != null ) but such a thing doesn't exist. Any ideas how to accomplish this? I don't want to use Task.WhenAll because I don't want to wait around for some calls to timeout before returning a response.

Break it down into an easier problem: First write that WhenAnyEqual method, then you can use it. It should be easier to write if you're only thinking about Tasks, not http requests and so on.

I'm thinking that you could call Task.WhenAny in a loop. Each time Task.WhenAny completes, you check if the returned Task passes the condition. If it does, you're done. If it doesn't, remove it from the collection of Tasks and call Task.WhenAny again.

Munkeymon
Aug 14, 2003

Motherfucker's got an
armor-piercing crowbar! Rigoddamndicu𝜆ous.



Couldn't you just ips.First(ip => DownloadCheck(client, ip) != null) ?

NihilCredo
Jun 6, 2011

iram omni possibili modo preme:
plus una illa te diffamabit, quam multæ virtutes commendabunt

Munkeymon posted:

Couldn't you just ips.First(ip => DownloadCheck(client, ip) != null) ?

That would just return the first IP in the list because the function would return a non-null Task<string>, no?

Munkeymon
Aug 14, 2003

Motherfucker's got an
armor-piercing crowbar! Rigoddamndicu𝜆ous.



NihilCredo posted:

That would just return the first IP in the list because the function would return a non-null Task<string>, no?

:doh: right, async. So I guess you could strip out all the sync stuff and return ips.AsParallel().First(etc) but maybe there's a way to keep it all async/await?

Mr Shiny Pants
Nov 12, 2012
Would the TCP timeout of 21 seconds not be long enough to not worry about this?

Munkeymon
Aug 14, 2003

Motherfucker's got an
armor-piercing crowbar! Rigoddamndicu𝜆ous.



I'm still kinda fighting NuGet trying to get the unmanaged libsodium DLL into my output. It works fine in a toy project with one Project, but we have multiple solutions that all have a different consumer of the user service and I'd prefer it if I didn't have to add a NuGet reference to libsodium-net for them all to work*. I found https://stackoverflow.com/questions/15816769/dependent-dll-is-not-getting-copied-to-the-build-output-folder-in-visual-studio which I'm alright with except that I don't think that's possible with the DLL being loaded dynamically based on architecture as it is in the .Net wrapper. Am I stuck doing copies in every consumer project's pre-build?


*note: I have not tested that, so it might not work, either

raminasi
Jan 25, 2005

a last drink with no ice

Munkeymon posted:

:doh: right, async. So I guess you could strip out all the sync stuff and return ips.AsParallel().First(etc) but maybe there's a way to keep it all async/await?

AsParallel doesn’t reorder anything, so that won’t work either. There may be some clever construct in Rx that will help you (“here’s a collection of things to do, give me an observable stream of the results as they come back”) but I’m not sure. The repeated Task.WhenAny suggested upthread is probably the first thing I’d try; the second would involve using a BlockingCollection and manually spawning a consumer.

ljw1004
Jan 18, 2005

rum

fankey posted:

Given a list of IP addresses, I'm using the following to determine the first address to response with a valid HTTP response.

Untested, coding in my text editor...

code:
static async Task CheckAsync(string url, TaskCompletionSource<string> tcs, CancellationToken cancel) {
  var rsp = await client.GetAsync(url, HttpCompletionOption.ResponseHeadersRead, cancel);
  if (rsp.StatusCode == System.Net.HttpStatusCode.OK) tcs.TrySetResult(url);
}

static Task<string> GetBestUrlAsync(IEnumerable<string> urls, CancellationToken cancel) {
  var tcs = new TaskCompletionSource<string>();
  cancel.Register(() => tcs.TrySetCanceled());
  urls.Select(url => CheckAsync(url, tcs, cancel)).ToList(); // ignoring the returned Tasks
  return tcs.Task;
}
Note: instead of putting cancellation into the method as you had done, I let it be provided as a parameter to GetBestUrl. I think this is a better design, more compositional. It's easy enough for the caller to create a cancellation token that will expire after 2000ms.

Note: in case of timeout, I thought it'd be cleaner to throw a Cancelled exception rather than to return null. That's how all other async APIs work.

Note: if all of the urls fail, then it would have been nicer for GetBestUrlAsync to fail immediately rather than wait until the cancellation expires. I didn't bother doing that. It would be easy enough: first, each CheckAsync would have to "try / catch (Exception ex) {}" to swallow all exceptions. Second, you'd need to "await Task.WhenAny(tcs.Task, Task.WhenAll(allUrlTasks))" and then "return tcs.Task.IsCompleted ? await tcs.Task : new OperationCancelledException()".

Note: it would have been nicer to cancel all pending requests the moment we get our first answer (rather than, as in this code, just letting each one eventually timeout). I didn't bother doing that. It would be easy enough: you'd create your own CancellationTokenSource that you can signal as soon as you get an answer, and then you'd create "CancellationTokenSource.CreateLinkedTokenSource(...)" and pass this on to all the tasks.

Note: the .ToList() is there just as standard LINQ practice. If you omitted it, then the .Select() call would return a lazy IEnumerable that would only invoke CheckAsync upon demand. We call .ToList() to make that demand.

Note: it felt really sketchy to do this upon IPs, and to do string concatenation with "http://" and the IP. I figured URLs would be more general and meaningful. What if "http://1.2.3.4" was served by one dead webserver but "http://1.2.3.4/path" was served by a different responsive webserver? I guess I don't know what you're going to do with the information about quickest-IP-to-respond, so it's hard to say. But my suspicions are raised...

Edit: the HttpClient team at Microsoft say it's best practice to have as few instances of HttpClient as possible, even down to sharing a single static instance across the whole app. They say this works better because it can cache or something like that. I vaguely remember a few bug reports a couple of years ago where folks said this wasn't working for them, but I'd hope those have been fixed by now.

ljw1004 fucked around with this message at 18:43 on Nov 22, 2017

NiceAaron
Oct 19, 2003

Devote your hearts to the cause~

NiceAaron posted:

Break it down into an easier problem: First write that WhenAnyEqual method, then you can use it. It should be easier to write if you're only thinking about Tasks, not http requests and so on.

I'm thinking that you could call Task.WhenAny in a loop. Each time Task.WhenAny completes, you check if the returned Task passes the condition. If it does, you're done. If it doesn't, remove it from the collection of Tasks and call Task.WhenAny again.

Since people are still discussing this (and I'm bored) I'll provide a more concrete code example of what I was thinking of (also untested and coding in the forum post text editor)

code:

public async Task<Task<T>> WhenAnyMatch<T>(IEnumerable<Task<T>> tasks, Func<T, bool> predicate)
{
	var collection = tasks.ToList();
	while (collection.Any())
	{
		Task<T> nextTask = (Task<T>)(await Task.WhenAny(collection));
		if (nextTask.Status == TaskStatus.RanToCompletion) // i.e. not cancelled or faulted
		{
			var nextResult = await nextTask;
			if (predicate(nextResult))
			{
				// This task matches the predicate
				return nextTask;
			}
		}

		// This task was either cancelled/faulted or it didn't match the predicate, so remove it from the collection and go back to the beginning of the loop to wait for the next one
		collection.Remove(nextTask);
	}

	// None of the task results matched
	return null; // or throw an exception, or return a Task<T> that returns default(T), or however you want to handle it
}

Using this method to solve the IP address problem is left as an exercise to the reader.

NihilCredo
Jun 6, 2011

iram omni possibili modo preme:
plus una illa te diffamabit, quam multæ virtutes commendabunt

Today I learnt about the FirstChanceException event.

So many lines of logging code...

LongSack
Jan 17, 2003

Question for the EF experts. I’ve searched (maybe I’m not using the correct keywords) and looked I’m my EF book without much luck. How do I “back out” or revert any unsaved changes?

Given this situation - user right-clicks on an item in a listbox/listview and chooses “Edit” ... new window pops up with the details of the object, with data-bound fields. User makes changes, then realizes that they have the wrong item, and presses the Cancel button. Since the data bindings are two-way, the object has been changed. If the user instead clicked the OK button, I would call the SaveChanges() method, but they clicked Cancel. How do I tell EF to discard any changes? I haven’t found a good answer to this question, but it seems like something that would have to be there, so maybe I’m just not looking in a way that reveals the answer.

Night Shade
Jan 13, 2013

Old School
dbContext.Entry(obj).ReloadAsync() - note that this is not actually a reset changes, it's a refetch from database so if the DB has updates the object will get them.

You might also be able to wire up something to splat from dbContext.Entry(obj).OriginalValues if you don't want to update from the DB but it doesn't look like there's an easy way to do that.

LongSack
Jan 17, 2003

Night Shade posted:

dbContext.Entry(obj).ReloadAsync() - note that this is not actually a reset changes, it's a refetch from database so if the DB has updates the object will get them.

You might also be able to wire up something to splat from dbContext.Entry(obj).OriginalValues if you don't want to update from the DB but it doesn't look like there's an easy way to do that.

Thanks. Refetch is fine, I’m not doing any apps with multiple users accessing the database.

It just seems weird to me that there’s not a method like RevertChanges() to go with SaveChanges()

Quebec Bagnet
Apr 28, 2009

mess with the honk
you get the bonk
Lipstick Apathy
First time I've used async methods in a long time and I don't know which of two alternative approaches is better. I have two operations to do: read a file from disk, and deserialize it from JSON. Reading the file can potentially be done asynchronously, but AFAIK deserializing with Newtonsoft.JSON is only synchronous.

C# code:
// Method 1
private Task<Butt> ReadButtAsync(string path, CancellationToken cancellationToken)
{
    return Task.Run(() =>
    {
        using(var reader = new StreamReader(path))
        using(var jsonReader = new JsonTextReader(reader))
        {
            return new JsonSerializer().Deserialize<Butt>(jsonReader)
        }
    }, cancellationToken);
}
C# code:
// Method 2
private async Task<Butt> ReadButtAsync(string path, CancellationToken cancellationToken)
{
    string content;
    using(var reader = new StreamReader(path))
        content = reader.ReadToEndAsync();

    using(var jsonReader = new JsonTextReader(new StringReader(content)))
    {
        return new JsonSerializer().Deserialize<Butt>(jsonReader)
    }
}
My intuition from last time I heavily used C# was that method #2 is preferred, because everything should be made async as far down as possible. But method #1 was what I first came up with and it seems to work fine so far. Does #1 have potential for deadlock even if the synchronous I/O is wrapped in an asynchronous block? (Context here is that I'm building a web API on ASP.NET Core, if there's any considerations specific to that environment and/or Kestrel.)

hirvox
Sep 8, 2009

chmods please posted:

First time I've used async methods in a long time and I don't know which of two alternative approaches is better. I have two operations to do: read a file from disk, and deserialize it from JSON. Reading the file can potentially be done asynchronously, but AFAIK deserializing with Newtonsoft.JSON is only synchronous.

My intuition from last time I heavily used C# was that method #2 is preferred, because everything should be made async as far down as possible. But method #1 was what I first came up with and it seems to work fine so far. Does #1 have potential for deadlock even if the synchronous I/O is wrapped in an asynchronous block? (Context here is that I'm building a web API on ASP.NET Core, if there's any considerations specific to that environment and/or Kestrel.)
#2 should perform better on higher loads, because the thread can be released to perform other tasks while the stream is being read. #1 will use up a thread until both the stream read and deserialization are complete.

BTW, Newtonsoft.JSON used to have asynchronous methods, but they were wrappers similar to method 1. When designing a public API, the method shouldn't lie about being asynchronous. If it's doing a significant amount of synchronous processing, keeping the method signature synchronous will allow the caller to decide whether they'll want to use a separate thread or not.

EssOEss
Oct 23, 2006
128-bit approved

LongSack posted:

Given this situation - user right-clicks on an item in a listbox/listview and chooses “Edit” ... new window pops up with the details of the object, with data-bound fields. User makes changes, then realizes that they have the wrong item, and presses the Cancel button. Since the data bindings are two-way, the object has been changed. If the user instead clicked the OK button, I would call the SaveChanges() method, but they clicked Cancel. How do I tell EF to discard any changes?

I would use a separate DataContext for the Edit dialog and throw away this DataContext (along with any data) once the dialog is closed (regardless of whether with a save or cancel action).

If you are currently using a mechanism of "one data context for the entire app" I suppose this might be foreign but I would recommend you move away from that into "one data context per operation" because your life will generally be much easier this way.

Funking Giblet
Jun 28, 2004

Jiglightful!

EssOEss posted:

I would use a separate DataContext for the Edit dialog and throw away this DataContext (along with any data) once the dialog is closed (regardless of whether with a save or cancel action).

If you are currently using a mechanism of "one data context for the entire app" I suppose this might be foreign but I would recommend you move away from that into "one data context per operation" because your life will generally be much easier this way.

I would go further that if you find any re-use of a DataContext, stop what you are doing right now and eliminate it and make sure you have a new one per operation, that is one for read (and hopefully, you map to a ViewModel), and a separate one for writing. Otherwise you are introducing a multitude of potential issues.

raminasi
Jan 25, 2005

a last drink with no ice

chmods please posted:

My intuition from last time I heavily used C# was that method #2 is preferred, because everything should be made async as far down as possible. But method #1 was what I first came up with and it seems to work fine so far. Does #1 have potential for deadlock even if the synchronous I/O is wrapped in an asynchronous block?

hirvox covered why #2 is preferred, but I’ll add that the async deadlocking footgun that I think you’re referring to manifests when you try to jam asynchrony into a synchronous workflow by using Result or Wait, rather than the opposite, which is what you’re doing here.

chippy
Aug 16, 2006

OK I DON'T GET IT

Funking Giblet posted:

I would go further that if you find any re-use of a DataContext, stop what you are doing right now and eliminate it and make sure you have a new one per operation, that is one for read (and hopefully, you map to a ViewModel), and a separate one for writing. Otherwise you are introducing a multitude of potential issues.

I'm all for one per request but what's the rationale behind separate contexts for reading and writing?

Disclaimer: I've only used EF with MVC/WebApi apps, oh, and a couple of Windows services, but not WinForms or WPF, so maybe this is some difference I'm not aware of.

chippy fucked around with this message at 18:39 on Nov 24, 2017

Adbot
ADBOT LOVES YOU

LongSack
Jan 17, 2003

EssOEss posted:

I would use a separate DataContext for the Edit dialog and throw away this DataContext (along with any data) once the dialog is closed (regardless of whether with a save or cancel action).

If you are currently using a mechanism of "one data context for the entire app" I suppose this might be foreign but I would recommend you move away from that into "one data context per operation" because your life will generally be much easier this way.

What’s the overhead for this? I only went with one long-lived context because I assumed that there was a lot of overhead with context creation. Using multiple short-lived contexts would solve a couple other problems as well, like this:

I go to add a new ticket, the window pops up, I enter the task and work order numbers, hit the drop-down for the Requester, and realize that it’s a new one so it isn’t in the list. No problem, I click the “...” button next to the combo box which pops up the window to maintain requesters. I add the new person, click ok and ... oops, the view model in the Requester maintenance window calls SaveChanges() when the I click “Done” so now the partially entered Ticket is saved. Now, if I click Cancel on the “Add Ticket” window, I have to delete the ticket and catch (and ignore) any exceptions.

  • 1
  • 2
  • 3
  • 4
  • 5
  • Post
  • Reply