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
Rocko Bonaparte
Mar 12, 2002

Every day is Friday!
I don't really know but I did look it up. I guess I want something like that. It needs to take over what is being displayed so they can't go around it and clicky clicky on other save buttons while the dialog is shown.

Adbot
ADBOT LOVES YOU

Bruegels Fuckbooks
Sep 14, 2004

Now, listen - I know the two of you are very different from each other in a lot of ways, but you have to understand that as far as Grandpa's concerned, you're both pieces of shit! Yeah. I can prove it mathematically.

Rocko Bonaparte posted:

I don't really know but I did look it up. I guess I want something like that. It needs to take over what is being displayed so they can't go around it and clicky clicky on other save buttons while the dialog is shown.

In ICommand, there's a method "CanExecute." When you return false in that method, buttons bound to that command will gray out. You could set "isPromptingForSave" when the save dlg is shown.

Bruegels Fuckbooks fucked around with this message at 18:21 on Nov 2, 2020

Rocko Bonaparte
Mar 12, 2002

Every day is Friday!
I'm just struggling to the control to display at all. I saw it when I explicitly just crammed it into the XAML but I haven't been able to make it appear any other way.

Edit: For now I'm just trying to compare and contrast what this tutorial does with what I did and can do.

Rocko Bonaparte fucked around with this message at 04:28 on Nov 3, 2020

Bruegels Fuckbooks
Sep 14, 2004

Now, listen - I know the two of you are very different from each other in a lot of ways, but you have to understand that as far as Grandpa's concerned, you're both pieces of shit! Yeah. I can prove it mathematically.

Rocko Bonaparte posted:

I'm just struggling to the control to display at all. I saw it when I explicitly just crammed it into the XAML but I haven't been able to make it appear any other way.
Either
a) Use a pop-up control within the xaml. The pop up xaml control will let you do inline "modal like" controls.
b) Make a new window class (new xaml etc.) containing the control and call showDialog() on it when you want it to prompt.

b) sounds like what you're going for to me.

Rocko Bonaparte
Mar 12, 2002

Every day is Friday!
Those would be the normal, adult thing to do, but I don't think I can actually do either one when I carry this over to NoesisGUI. I definitely can't do a window. Right now I'm using a Window while just beating it up in Visual Studio, but it'll become a UserControl afterwards. It sounds like with NoesisGUI that I can't do a normal popup either but I'm not well-versed with WPF enough to know what exactly that might even mean.

If I search according to their stuff, I see something like putting the popup description at the end of everything and setting it to be hidden. Something that wants to show it then changes its visibility state. I actually was trying that myself last night, but the problem came down to plumbing. My buttons have their own ViewModel and are oblivious to the window. I would have to hook them up. My first attempt at that somehow causes all of the save buttons to completely disappear. I'm trying it again now, but that data linkage just seems gross.

Are there are any tricks for restoring the windows data context for a binding or something? The save button's context is the save button's data, so I don't see that window. I was thinking I could pass it in as a command parameter. Something like this pseudoXAML:

code:
<Window Name="TopmostWindow">
  <DataTemplate x:Key="SaveGameButton" DataType="local:SaveData">
	<Button Name="SaveButton" Command="{Binding ShowConfirmationDialog}" CommandParameter="{Binding TopmostWindow}">
	</Button>
  </DataTemplate>
  <local:DialogPrompt Visibility="Hidden"/>
</Window>  

Bruegels Fuckbooks
Sep 14, 2004

Now, listen - I know the two of you are very different from each other in a lot of ways, but you have to understand that as far as Grandpa's concerned, you're both pieces of shit! Yeah. I can prove it mathematically.

Rocko Bonaparte posted:

Those would be the normal, adult thing to do, but I don't think I can actually do either one when I carry this over to NoesisGUI. I definitely can't do a window. Right now I'm using a Window while just beating it up in Visual Studio, but it'll become a UserControl afterwards. It sounds like with NoesisGUI that I can't do a normal popup either but I'm not well-versed with WPF enough to know what exactly that might even mean.

If I search according to their stuff, I see something like putting the popup description at the end of everything and setting it to be hidden. Something that wants to show it then changes its visibility state. I actually was trying that myself last night, but the problem came down to plumbing. My buttons have their own ViewModel and are oblivious to the window. I would have to hook them up. My first attempt at that somehow causes all of the save buttons to completely disappear. I'm trying it again now, but that data linkage just seems gross.

Are there are any tricks for restoring the windows data context for a binding or something? The save button's context is the save button's data, so I don't see that window. I was thinking I could pass it in as a command parameter. Something like this pseudoXAML:

code:
<Window Name="TopmostWindow">
  <DataTemplate x:Key="SaveGameButton" DataType="local:SaveData">
	<Button Name="SaveButton" Command="{Binding ShowConfirmationDialog}" CommandParameter="{Binding TopmostWindow}">
	</Button>
  </DataTemplate>
  <local:DialogPrompt Visibility="Hidden"/>
</Window>  

You can pass the window as a command parameter - give the window a name in the XAML, and pass it by name e.g. "CommandParameter = "{Binding ElementName=TopWindow}".

For your view model problem, you can have nested viewmodels, and set the data context to those within the xaml. e.g. You can have a mainwindowviewmodel, and you can have a viewmodel for the grid within the mainwindowviewmodel. So you can do something like <Grid DataContext="{Binding GridVm}"> if GridVm is a property within the mainwindowviewmodel, and mainwindowviewmodel is the window's dataccontext.

LongSack
Jan 17, 2003

Sorry getting back to this so late, but I've been out of town... First of all, if this comes across as argumentative, I apologize in advance - it's not meant to be. I just want to understand.

SirViver posted:

The locator pattern is an anti-pattern because it's essentially a static god object that gets you everything everywhere and in doing so hides each and every dependency of a class behind a runtime error.

Just imagine trying to instantiate a class that uses the locator pattern when starting from a blank slate (i.e. no services registered with the locator at all). With classic constructor injection you see every dependency right there and then before even compiling. You provide those dependencies and it will work. With locator you instantiate it and then only during runtime it will fail with the first missing service. Then you add that and it will fail again with the next one. Then after some trial and error it finally works until you call a method on that class that itself also uses the locator to load yet another missing dependency. Repeat ad-infinitum.

E: Can't really answer the question what's good to use for DI in WPF in practice as I was successfully able to avoid having to use it so far, but I can't see why basic stuff like TinyIoC won't work in principle.

My Locator class is simply a thin veneer over a bog standard Microsoft DI ServiceProvider. All it does differently is expose services as properties, so that instead of using _provider.GetRequiredService<Foo>(), you can use the exposed property: Locator.Foo. Since it does use standard MS DI, it can - and does - use constructor injection, in fact my Data Access Layer classes get their DbContext via constructor injection. Also, it's not a static class (although the Tools class is). If you want to take a look at an example, there's one here.

As for the rest, all of that could be said of ASP.NET Core and it's use of DI. The ServiceProvider is essentially a god object that gets you everything, and if you forget to register a service it will hand you a run-time error.

Here's the problem I'm trying to work around: The MainViewModel is a singleton. It gets instantiated at app startup and lives until the app ends. It also needs to instantiate all or almost all of the other ViewModels. If someone wants to create a new widget, they click a button or choose a menu option, and they get a WidgetWindow with a WidgetViewModel. If I use constructor injection to give the MainViewModel all of it's dependent ViewModels, then they get instantiated at app startup, too. And all of their dependencies do as well. So essentially, the entire data footprint of the application is built at app start.

This is a bad thing. My DAL classes are designed to be transient. It's an essential feature to deal with some of Entity Framework Core's more interesting quirks.

I can see ways around this, say if instead of passing classes to the MainViewModel constructor, I pass types instead, but that seems unwieldy.

So, instead of using constructor injection, the ViewModels can get their dependent ViewModels from the Locator as needed:

C# code:
private void ManageAccountTypesClick()
{
    AccountTypeViewModel vm = Tools.Locator.AccountTypeViewModel;
    DialogSupport.ShowDialog<AccountTypeWindow>(vm, Application.Current.MainWindow);
}

adaz
Mar 7, 2009


I don''t know WPF well enough to give you a elegant solution. However, you can get around your problem - and adhere to more classic DI with a single composition root - with a couple different strategies. If they are worth the abstraction is up to you, and how hard it is to test your current setup.

* Expose an interface method - like GetViewModel<T> in your MainViewModel and from there instantiate from your injector. Basically turn it into a factory for your viewModels.
* Like you did above, inject into your MainViewModel each of your viewModels. However, all they take in is a single service - can call it something like ViewModelFactory. The ViewModelFactory<T> would then create the 'real' class when methods where
called. Basically a facade to enable lazy loading.
* Modify your ViewModels so they only take in a single dependency - call it ServiceProvider<T>. The ServiceProvider<T> you'd call in your properties to instantiate the class dependency. Take this dumb example class:

code:
public class SomeService : IService {
     
  private IServiceProvider provider { get; }

 private _someDatabase;
  private ISomeDatabase SomeDatabase {
     get {
              if(_someDatabase == null) {
                _someDatabase = provider.GetService<ISomeDatabase>();
               return _someDatabase;
            } else {
                return _someDatabase;
              }
      }
  }  
  
   public SomeService(IServiceProvider serviceProvider) {
             provider = serviceProvider;
   }
}

SirViver
Oct 22, 2008

LongSack posted:

My Locator class is simply a thin veneer over a bog standard Microsoft DI ServiceProvider. All it does differently is expose services as properties, so that instead of using _provider.GetRequiredService<Foo>(), you can use the exposed property: Locator.Foo. Since it does use standard MS DI, it can - and does - use constructor injection, in fact my Data Access Layer classes get their DbContext via constructor injection.

...

As for the rest, all of that could be said of ASP.NET Core and it's use of DI. The ServiceProvider is essentially a god object that gets you everything, and if you forget to register a service it will hand you a run-time error.

Yes, that... isn't the point. The ServiceProvider in this case is the IoC container. It's the thing that knows about all the registered services/types and about all their dependencies. It can be used to create an instance of any of the registered types. It is the god object. Every IoC container is.

Whether you fall victim to the locator pattern depends on how/where you use that IoC container. And the somewhat hyperbolic yet simple answer to that is: if you use the IoC container at all, you're doing it wrong.

To be exact, the only place your IoC container should be used in (and ideally be accessible from) is your application's composition root (bootstrapper, entry point, App_Start... whatever it's called) or the framework-specific equivalent thereof (e.g. public void ConfigureServices(IServiceCollection services) in ASP.NET Core). In that place you register all of the application's required services/dependencies. Whether that is done by hand or by automatic assembly scanning doesn't really matter. After you've done that, depending on the framework you're using, you either do nothing because the framework does everything for you already, or you use the IoC container once to instantiate your main startup/entry/root object (main form/window?) from which your dependency graph grows.

From this point on, the IoC container should not be used anywhere at all. Ideally you shouldn't even be able to access it from anywhere else in your code. If you have static access to it, or pass it around as parameter, you're using the locator pattern, because you're obfuscating the actual dependencies of whatever class that invokes the IoC container's GetService<T>() functionality. If you wrap your IoC container in a class that exposes the GetService<T>() calls via properties then all you've accomplished is "documenting" some of the services that are available via the IoC container. But the actual problem - that you obfuscate a type's dependencies - has not been solved in any way.

The reason it's so easy to fall for this is because DI introduces a rather obvious problem: "How do I new up stuff?", to which the answer is: you don't. But that answer doesn't solve the problem, leading to the easy/wrong solution of just using the IoC container directly or indirectly to GetService<T>()-it for you. The actual correct solution is to use the Factory pattern and to inject an IFactory<ISomeClass> dependency instead of an ISomeClass dependency. An important detail here is of course that the factory must be for a specific type; a factory that can give you anything is the same thing as an IoC container and as soon as you use that it's the locator pattern all over again.


Again, I'm sorry I can't really give you any concrete WPF-specific examples/answers for this due to my lack of experience with that framework. With ASP.NET it's quite straightforward, because you can just declare the dependencies in the controller's constructor and ASP.NET will use the IoC container to resolve those dependencies for you - you're never creating instances of the controllers yourself. This usage is of course OK, because ASP.NET's internal scaffolding using the IoC container doesn't obfuscate any dependencies - the framework and your user code aren't logically linked in any way.


adaz posted:

* Modify your ViewModels so they only take in a single dependency - call it ServiceProvider<T>. The ServiceProvider<T> you'd call in your properties to instantiate the class dependency. Take this dumb example class:

code:
public class SomeService : IService {
     
  private IServiceProvider provider { get; }

 private _someDatabase;
  private ISomeDatabase SomeDatabase {
     get {
              if(_someDatabase == null) {
                _someDatabase = provider.GetService<ISomeDatabase>();
               return _someDatabase;
            } else {
                return _someDatabase;
              }
      }
  }  
  
   public SomeService(IServiceProvider serviceProvider) {
             provider = serviceProvider;
   }
}

This is bad, because this is just the locator pattern all over again. You pass in a a single thing (service provider/locator) that can get you anything. Nowhere in the constructor or otherwise "obvious place" does the example above document that ISomeDatabase is a dependency of SomeService.

As mentioned above, every time you need to dynamically construct anything (where the various lifetime capabilities of the IoC container and used framework aren't enough, e.g. singleton, per-request, etc.) the DI answer is to use the Factory pattern and to inject the Factory that can create a dependency instance instead of the dependency itself.

adaz
Mar 7, 2009

SirViver posted:




This is bad, because this is just the locator pattern all over again. You pass in a a single thing (service provider/locator) that can get you anything. Nowhere in the constructor or otherwise "obvious place" does the example above document that ISomeDatabase is a dependency of SomeService.

As mentioned above, every time you need to dynamically construct anything (where the various lifetime capabilities of the IoC container and used framework aren't enough, e.g. singleton, per-request, etc.) the DI answer is to use the Factory pattern and to inject the Factory that can create a dependency instance instead of the dependency itself.

That's not the definition of locator.If I pass in a single thing - a factory - that can get me anything that isn't service locator as well. Dependency injection is supplying all the dependencies of your class when its constructed. I mean if you just rename ServiceProvider to ServiceFactory and it returned SomeService that had a SomeDatabase there would _also_ not be an 'obvious' way to tell that unless you looked @ the IoC container registrations of the class definitions. Service Locator pattern is explicitly referring to calling a single, usually, static class that returns a specific instance or service. This can be called, and usually is, anywhere in code. When we're talking about DI we're referring to supplying all the dependencies up front at your class construction. To your point never using new.

What we're both trying to suggest is he instead relies on an IoC framework to inject in something that will supply their dependencies for them. I think my second option is the one that aligns most closely with the preferred method of doing this to avoid instantiating the entire app @ startup. That somewhat psuedo code looks like this. The rough issue here is i'm not sure what would ever release the viewModel unless you explicitly called it. Not sure the lifecycle of viewModels in WPF but that might be a concern.


code:


public class ModelViewShim {
     
   private IViewFactory viewFactory { get; }

   public ModelViewFactory(IViewFactory _viewFactory) {
             viewFactory = _viewFactory;
   }

private void ManageAccountTypesClick()
{
    AccountTypeViewModel vm = viewFactory.Create<AccountTypeViewModel>();
    DialogSupport.ShowDialog<AccountTypeWindow>(vm, Application.Current.MainWindow);

}

}

SirViver
Oct 22, 2008

adaz posted:

That's not the definition of locator.If I pass in a single thing - a factory - that can get me anything that isn't service locator as well.

Hm, I disagree. Whether the locator is static or dependency-injected doesn't change the underlying problem at all (see Variation: Abstract Service Locator). The knowledge that your class is going to use a god object (service provider/locator) to get who-knows-what isn't useful. The difference between using locator pattern or not is whether you're upfront with your actual concrete dependencies - in practice meaning whether they are declared in the constructor. Sure, the sub-dependency graph for a given dependency may still be deep, but no matter how far you go, you'll only have to look at each dependency's constructor to figure out what it needs. If all you do is use a static locator, or you inject a locator service, then you'll have to scan each and every method inside that class for all usages of the locator instance to figure out what the actual dependencies are.

Your ServiceFactory argument makes no sense to me. Good factory pattern usage necessitates that the factory creates objects of exactly one concrete (base-)type or interface only. A factory that can return you anything, be it via unconstrained generics or even as ugly as returning a plain object, is a god-object/locator, and using it means you're using the locator pattern and all the drawbacks it has.

SirViver fucked around with this message at 22:25 on Nov 3, 2020

uXs
May 3, 2005

Mark it zero!
I have a service that receives JSON messages from a queue.

The format and content of this message can vary and change over time, so I can't deserialize the entire thing into an explicitly defined .NET object.

What's the best/easiest way to add a property to this message?

The standard libraries seem most concerned with deserializing and reserializing the entire JSON object, which is something I want to avoid if it all possible.

NihilCredo
Jun 6, 2011

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

uXs posted:

I have a service that receives JSON messages from a queue.

The format and content of this message can vary and change over time, so I can't deserialize the entire thing into an explicitly defined .NET object.

What's the best/easiest way to add a property to this message?

The standard libraries seem most concerned with deserializing and reserializing the entire JSON object, which is something I want to avoid if it all possible.

Do you mean a .NET property, or a JSON property?

In either case, you could deserialize it to a dynamic JObject. If you want an extra .NET property, make a subclass of JObject with the extra property. If you want a JSON property, just call the .Add(String, JToken) method.

mystes
May 31, 2006

If you're using .net core, if you parse the json as a JsonDocument it looks you might theoretically be able to get away without deserializing the whole thing to an Object or JObject but it would be a bit of a pain.

uXs
May 3, 2005

Mark it zero!

NihilCredo posted:

Do you mean a .NET property, or a JSON property?

In either case, you could deserialize it to a dynamic JObject. If you want an extra .NET property, make a subclass of JObject with the extra property. If you want a JSON property, just call the .Add(String, JToken) method.

A JSON property. I need to add it to the message and then send it back on another queue.

I think I've got it working with what you said + casting the JObject to a dynamic to add the property. Thanks!

mystes
May 31, 2006

Is that what he meant by dynamic? I don't think you need to do that for jsonobject since there are methods to get/set arbitrary json properties.

mystes fucked around with this message at 13:22 on Nov 4, 2020

Just-In-Timeberlake
Aug 18, 2003
Is there a way to get either the HttpContext or just the URL in the Startup.cs of a net core api? I'm trying to load a different configuration based on which environment it's deployed to (ie. dev.mysite.com vs. staging.mysite.com).

One wouldn't think this would be such a pain in the rear end, but gently caress me if I can't figure it out.

FrantzX
Jan 28, 2007

Just-In-Timeberlake posted:

Is there a way to get either the HttpContext or just the URL in the Startup.cs of a net core api? I'm trying to load a different configuration based on which environment it's deployed to (ie. dev.mysite.com vs. staging.mysite.com).

One wouldn't think this would be such a pain in the rear end, but gently caress me if I can't figure it out.

No. A HttpContext can only exist by means of an inbound request to the server.

What you want to do is to have different appsettings.json for each environment. That what they are there for.

NihilCredo
Jun 6, 2011

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

Note that using appsettings.json is the MS default approach but by no means required. If your configuration data lives elsewhere, you can just read the environment from an environment variable / CLI argument / whatever and move on from there.

As FrantzX pointed out, however, you're not going to know if you're running in the correct environment until you receive an HTTP request and can check the Host it's pointing at.

To enforce that you're only answering requests targeted at the correct requirement, there's a built-in middleware for host filtering: https://andrewlock.net/adding-host-filtering-to-kestrel-in-aspnetcore/

Rocko Bonaparte
Mar 12, 2002

Every day is Friday!

Bruegels Fuckbooks posted:

You can pass the window as a command parameter - give the window a name in the XAML, and pass it by name e.g. "CommandParameter = "{Binding ElementName=TopWindow}".
ElementName was the magic word. I couldn't figure out how to point it at that. My stuff is getting called now. I have to deal with the next steps but I will do that later.

I noticed with RelayCommand examples that the pattern is shown with and without a lambda. The way without a lambda puzzles me because I didn't realize that was legal C# grammar:
code:
private RelayCommand<MainWindow> _showConfirmationDialog;
public ICommand ShowConfirmationDialog
{
	get
	{
		if (_showConfirmationDialog == null)
		{
			_showConfirmationDialog = new RelayCommand<MainWindow>(handleConfirmationDialog);
		}
		return _showConfirmationDialog;
	}
}

private void handleConfirmationDialog(object item)
...
I'll treat methods like first-class objects in Python all the time but I didn't think C# could do this. Is this some shenanigans related to Actions?

raminasi
Jan 25, 2005

a last drink with no ice

Rocko Bonaparte posted:

I noticed with RelayCommand examples that the pattern is shown with and without a lambda. The way without a lambda puzzles me because I didn't realize that was legal C# grammar:
code:
private RelayCommand<MainWindow> _showConfirmationDialog;
public ICommand ShowConfirmationDialog
{
	get
	{
		if (_showConfirmationDialog == null)
		{
			_showConfirmationDialog = new RelayCommand<MainWindow>(handleConfirmationDialog);
		}
		return _showConfirmationDialog;
	}
}

private void handleConfirmationDialog(object item)
...
I'll treat methods like first-class objects in Python all the time but I didn't think C# could do this. Is this some shenanigans related to Actions?

Method groups automatically convert to delegates when possible. It's just the C# version of what you'd do in Python.

Bruegels Fuckbooks
Sep 14, 2004

Now, listen - I know the two of you are very different from each other in a lot of ways, but you have to understand that as far as Grandpa's concerned, you're both pieces of shit! Yeah. I can prove it mathematically.

Rocko Bonaparte posted:

I'll treat methods like first-class objects in Python all the time but I didn't think C# could do this. Is this some shenanigans related to Actions?
Action<T> is a delegate type. ref. https://docs.microsoft.com/en-us/dotnet/api/system.action-1?view=netcore-3.1

If you take a look at the remarks:

quote:

You can use the Action<T> delegate to pass a method as a parameter without explicitly declaring a custom delegate.

Just-In-Timeberlake
Aug 18, 2003
Anybody have any Serilog knowledge/experience?

I have Serilog working locally (sends logs to Elasticsearch), but when I deploy to AWS Lambda, I am getting the following error in the Cloudwatch Logs: System.InvalidOperationException: Unable to resolve service for type 'Serilog.Extensions.Hosting.DiagnosticContext' while attempting to activate 'Serilog.AspNetCore.RequestLoggingMiddleware

Any ideas what the issue could be? Kinda hard to debug this.

adaz
Mar 7, 2009

Just-In-Timeberlake posted:

Anybody have any Serilog knowledge/experience?

I have Serilog working locally (sends logs to Elasticsearch), but when I deploy to AWS Lambda, I am getting the following error in the Cloudwatch Logs: System.InvalidOperationException: Unable to resolve service for type 'Serilog.Extensions.Hosting.DiagnosticContext' while attempting to activate 'Serilog.AspNetCore.RequestLoggingMiddleware

Any ideas what the issue could be? Kinda hard to debug this.

I only use serilog for a bit before I swapped to NLog, but that looks like you didn't properly setup Serilog. In your app.startup for the 'cloud' config or whatever you use for your lambda are you UseSerilog in your host builder?. I took a look at that third party extension you're using and its just expecting that Diagnostic Context to already be there, and it's not

LongSack
Jan 17, 2003


Thank you for this. I think I finally understand the point(s) you’re making here.

Get rid of the Locator class. Load the IoC container at app startup (probably in App.xaml.cs), and “seed” the MainViewModel. All other dependencies are resolved via constructor injection, and the “turtles all the way down” problem is addressed by passing factory classes rather than concrete classes.

I hope I got that right.

Gonna have to bone up on my factory pattern. I’ve used it, primarily in an IConfigurationFactory class, but not extensively.

Thanks.

adaz
Mar 7, 2009

SirViver posted:

Hm, I disagree. Whether the locator is static or dependency-injected doesn't change the underlying problem at all (see Variation: Abstract Service Locator). The knowledge that your class is going to use a god object (service provider/locator) to get who-knows-what isn't useful. The difference between using locator pattern or not is whether you're upfront with your actual concrete dependencies - in practice meaning whether they are declared in the constructor. Sure, the sub-dependency graph for a given dependency may still be deep, but no matter how far you go, you'll only have to look at each dependency's constructor to figure out what it needs. If all you do is use a static locator, or you inject a locator service, then you'll have to scan each and every method inside that class for all usages of the locator instance to figure out what the actual dependencies are.

Your ServiceFactory argument makes no sense to me. Good factory pattern usage necessitates that the factory creates objects of exactly one concrete (base-)type or interface only. A factory that can return you anything, be it via unconstrained generics or even as ugly as returning a plain object, is a god-object/locator, and using it means you're using the locator pattern and all the drawbacks it has.

Ah, I should've included a tiny bit of additional code in the explanation of the factory, absolutely the factory should be constrained to a real type of _some_ kind. If you do like the code below, and let your IoC container handle the creation and wiring up of the actual ModelViews, would absolutely be my preferred solution.

The container being able to instantiate unconstrained anything isn't service locator, ideally your entire app has a single composition root where everything gets created -- Mark has a good point on this - although in weird instances like this where the framework doesnt give you that hook this is the best workaround imo.

code:

public interface IViewFactory<T> where T : ModelView {

   Create<T>();
}

public Class ViewFactory<ModelView> : IViewFactory {

   public Create<ModelView> () {
          return container.Resolve<T>(); // or whatever your container of choiceinstantiation method is
  }

}

public class ModelViewShim {
     
   private IViewFactory viewFactory { get; }

   public ModelViewFactory(IViewFactory _viewFactory) {
             viewFactory = _viewFactory;
   }

private void ManageAccountTypesClick()
{
    AccountTypeViewModel vm = viewFactory.Create<AccountTypeViewModel>();
    DialogSupport.ShowDialog<AccountTypeWindow>(vm, Application.Current.MainWindow);

}

}

Rocko Bonaparte
Mar 12, 2002

Every day is Friday!
I had to disable my save button list when the save overwrite dialog was being shown over it. Everything was in one grid beforehand; the dialog just took up all rows and columns on top of what was normally shown. To deny control to the other buttons, I ended up wrapping all of that with another grid that had no topology information. It really was just a <Grid></Grid> around everything I already had. I could then name my original save button grid something so I could set IsEnabled to false on it when the dialog had to be shown shown. This seems to work but I feel a little sour about using an outer Grid like that. I figured there was a kind of panel I need to use and Grid doesn't really do it right. Is there one that is more kosher for this kind of thing?

Basically, I want to draw my main save menu across the whole window normal, but then have something overlayed on top to show my special overwrite dialog. As a reminder, system dialogs and windows are a no-no thanks to moving this to Noesis afterwards.

Just-In-Timeberlake
Aug 18, 2003

adaz posted:

I only use serilog for a bit before I swapped to NLog, but that looks like you didn't properly setup Serilog. In your app.startup for the 'cloud' config or whatever you use for your lambda are you UseSerilog in your host builder?. I took a look at that third party extension you're using and its just expecting that Diagnostic Context to already be there, and it's not

I think I've got it all set up right looking at that, otherwise I don't think it would work at all, but it does work on my dev machine.

I decided to pare it down to the basics, I removed anything to do with Serilog except the base Serilog.AspNetCore package, and used the most bare bones configuration necessary to log something, and I'm still getting the same error.

LocalEntryPoint.cs:

code:
	public static void Main(string[] args)
        {
            Log.Logger = new LoggerConfiguration()
                .WriteTo.Console()
                .CreateLogger();

            try
            {
                Log.Information("Starting up");

                CreateHostBuilder(args).Build().Run();
            }
            catch (Exception ex)
            {
                Log.Fatal(ex, "Application start-up failed");
            }
            finally
            {
                Log.CloseAndFlush();
            }

            CreateHostBuilder(args).Build().Run();
        }

	public static IHostBuilder CreateHostBuilder(string[] args) =>
            Host.CreateDefaultBuilder(args)
                .UseSerilog()
		.ConfigureWebHostDefaults(webBuilder =>
                {
                    webBuilder.UseStartup<Startup>();
                });
and in Startup.cs

code:
	public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
        {
           if (env.IsDevelopment())
            {
                app.UseDeveloperExceptionPage();
            }
            
            app.UseHttpsRedirection();

            app.UseSerilogRequestLogging();

            app.UseRouting();

            app.UseAuthorization();

            app.UseEndpoints(endpoints =>
            {
                endpoints.MapControllers();
            });

        }

Dolemite
Jun 30, 2005
This isn't so much a programming question, but more of a question about the .NET world.

I'm a PHP dev that got laid off over the summer (thanks, covid!). As I apply for jobs, I'm noticing that there is faaarr less demand for PHP these days. But goddamn, .NET jobs are everywhere! So needless to say, I'm wanting to transition over to the .NET world.

In that last job, I was lucky to get a solid year of experience working with:

C# (naturally) and LINQ
.NET Core (including creating APIs)
Entity Framework Core
Team Foundation Server

I gotta say, I actually liked working with the .NET stack!

So, let's say you were a hiring manager looking to hire a new web dev. What would you be looking for in terms of skills and knowledge? Experience wise, I have like 9 years-ish of development under my belt. Perhaps that's a plus in a hiring manager's eyes?

Right now, I'm going through plural sight courses on some of the above. Just finished up a .NET Core course, now I'm moving on to plural sight's EF Core course. Are there any other things you all can suggest I study up on, so that I can actually land a .NET job?

Even though I'm at the senior level when it comes to PHP, I plan to apply to entry level .NET jobs to get my foot in the door. Luckily, I can take the pay hit.

Rocko Bonaparte
Mar 12, 2002

Every day is Friday!
I have a goofy little behavior with my listview of buttons. When I click the currently-bottom-most-visible button, it instead scrolls down one button and my command doesn't fire. I am guessing it's using my click to get the button completely visible instead of processing events on it. The issue is that the button itself is completely visible; it's just short of a little bit of its padding. This makes it pretty confusing. How might I get around this? I'm guessing I can do something to size the list view to prevent this. I am trying to make it so that at 640x480 that I would have 4 buttons still completely visible. I guess I'm technically a little short of that right now.

Edit: Even more confusing is how well-aligned it looks in the designer in Visual Studio, but then it's something like a teeny weeny more padding gets added when running it.

Rocko Bonaparte fucked around with this message at 20:01 on Nov 8, 2020

distortion park
Apr 25, 2011


Dolemite posted:

This isn't so much a programming question, but more of a question about the .NET world.

I'm a PHP dev that got laid off over the summer (thanks, covid!). As I apply for jobs, I'm noticing that there is faaarr less demand for PHP these days. But goddamn, .NET jobs are everywhere! So needless to say, I'm wanting to transition over to the .NET world.

In that last job, I was lucky to get a solid year of experience working with:

C# (naturally) and LINQ
.NET Core (including creating APIs)
Entity Framework Core
Team Foundation Server

I gotta say, I actually liked working with the .NET stack!

So, let's say you were a hiring manager looking to hire a new web dev. What would you be looking for in terms of skills and knowledge? Experience wise, I have like 9 years-ish of development under my belt. Perhaps that's a plus in a hiring manager's eyes?

Right now, I'm going through plural sight courses on some of the above. Just finished up a .NET Core course, now I'm moving on to plural sight's EF Core course. Are there any other things you all can suggest I study up on, so that I can actually land a .NET job?

Even though I'm at the senior level when it comes to PHP, I plan to apply to entry level .NET jobs to get my foot in the door. Luckily, I can take the pay hit.

If you've got 9 years of experience then you should be able to get more than an entry level role hopefully.

In any case make sure you know how the dotnet garbage collector works, the difference between the various dotnet flavours, and how the linq extension methods work (and how you can write your own). Interviewers love all of those when assessing basic dotnet knowledge.

insta
Jan 28, 2009
Seconding learn how LINQ works (specifically LINQ-to-objects). It is NOT just a database provider.

Be very comfortable with generics, async/await, and rudementary data structures (HashSet is usually what you want when using List, for instance). With that much background in PHP you'll probably be going full-stack/frontend, so brush up on Razor Pages and how they're different than MVC. Depending on how you wrote your PHP, Razor Pages may feel completely at home.

Dolemite
Jun 30, 2005

pointsofdata posted:

If you've got 9 years of experience then you should be able to get more than an entry level role hopefully.

In any case make sure you know how the dotnet garbage collector works, the difference between the various dotnet flavours, and how the linq extension methods work (and how you can write your own). Interviewers love all of those when assessing basic dotnet knowledge.

These suggestions are super helpful! Thanks! For starters, I'll be looking up more info on how garbage collection works.

insta posted:

Seconding learn how LINQ works (specifically LINQ-to-objects). It is NOT just a database provider.
Re: LINQ - in the past, I've used LINQ in such a way as
code:
foo.Where(stuff => logic goes here).ToList()
. Is this what is meant by LINQ extensions, or rather, this being an example of how LINQ lets you query collections, instead of just doing DB stuff?

quote:

Be very comfortable with generics, async/await, and rudementary data structures (HashSet is usually what you want when using List, for instance). With that much background in PHP you'll probably be going full-stack/frontend, so brush up on Razor Pages and how they're different than MVC. Depending on how you wrote your PHP, Razor Pages may feel completely at home.

Generics being things like, say List<T> where T acts as a sort of placeholder until you sub in your type? Like, List<T> can be written as List<string> for example?

I'm definitely targeting full-stack roles, since yeah, I've done a lot of JS stuff for front-end business requirements. I went through one course that made use of Razor pages. How commonplace is Razor? Because, yikes, I don't like it! Feels quite clunky. But, once I understood the whole PageModel thing, Razor Pages felt a lot like working with PHP templating frameworks.

I could very much be missing something when it comes to Razor Pages not being true MVC, but it seems like the page handles routing and its page model is sorta like a controller with its OnGet and OnPost methods. Like, rather than a true view -> request to server -> controller -> model flow, the Razor page handles everything except for DB interactions, which are kicked off by interacting with the model.

The way my last place used .NET, it wasn't in the MVC style. .Net Core was used to strictly have the code serve as an API (so I got used to async/await) and send JSON back to the client. The front end was all Angular, no Razor pages. Now that I think about it, I've seen other places with a .NET team also use it strictly as a backend service/API, with a JS app handling the front end, dispatching requests to the .NET side.

New Yorp New Yorp
Jul 18, 2003

Only in Kenya.
Pillbug

Dolemite posted:

Re: LINQ - in the past, I've used LINQ in such a way as
code:
foo.Where(stuff => logic goes here).ToList()
. Is this what is meant by LINQ extensions, or rather, this being an example of how LINQ lets you query collections, instead of just doing DB stuff?

Yes. More specifically, understand what lambda expressions are and how to use Actions and Funcs. LINQ is a very common use case for lambdas, but you'll see them pop up all over the place.

Dolemite posted:

Generics being things like, say List<T> where T acts as a sort of placeholder until you sub in your type? Like, List<T> can be written as List<string> for example?

Yes. If you want to start hurting your brain a little bit, start reading up on covariant and contravariant generics. It doesn't come up a lot day-to-day but it's one of those things that can impress interviewers if you can talk about it.

You may also want to look into Blazor for front-end work; it's relatively new and immature but it's catching on in the .NET world. It's client-side C#, so you write your front end using Razor syntax and C#, which all runs on the client-side. No writing JavaScript needed.

I'd also say it's critical to understand how to isolate dependencies behind interfaces and implement inversion of control, as well as how to write good tests, and when to do unit testing vs integration testing. Not being familiar with unit testing and common practices around it is a huge red flag.

Rocko Bonaparte
Mar 12, 2002

Every day is Friday!

Rocko Bonaparte posted:

I have a goofy little behavior with my listview of buttons. When I click the currently-bottom-most-visible button, it instead scrolls down one button and my command doesn't fire.

If anybody was curious, I figured it out from a posting somebody had about a ListBox. I had to suppress RequestBringIntoView on my buttons; it just maps to a callback that marks that the event was handled without doing anything. I already had a ListView style so it went in there. There haven't been any secondary effects so far. However, I'll have to see what happens eventually in Unity3d when I try to scroll through the list using a gamepad or something.

brap
Aug 23, 2004

Grimey Drawer
IMO covariant and contravariant are lousy terms, hard to remember unless you’ve drilled yourself on type theory or have just encountered the concept many times.

Covariant should be called output-variant, e.g. IEnumerable<out T> and refers to when a conversion from more specific to less specific type is safe because the type is only used as an output, such as for a return value or out parameter. e.g. IEnumerable<string> safely converts to IEnumerable<object>.

Contravariant should be called input-variant, e.g. IEquatable<in T>. Converting from a less specific to a more specific type is allowed because the type is only used for inputs, i.e. by-value parameters. So IEquatable<object> safely converts to IEquatable<string>.

Only interfaces get to do this in C#. Non interface types with type parameters are invariant for complex reasons I believe Eric Lippert explained on Stack Overflow. The in/out modifiers can be used on type parameters in interfaces and the compiler enforces the type parameter is used only in the allowed positions.

Anyway, this is how I mentally keep track of co/contravariance and has helped me. Maybe it will help someone here.

raminasi
Jan 25, 2005

a last drink with no ice

brap posted:

Only interfaces get to do this in C#.

And delegates!

power crystals
Jun 6, 2007

Who wants a belly rub??

Alright, I have my own WPF question:

I have an ItemsControl (actually several different instances across different controls; a TreeView is the most prominent) bound to an ICollectionView which in turn has as its source an ObservableCollection<T>. I want to filter the ICollectionView. But calling Refresh() when the filter changes seems to force the ItemsControl to drop and rebuild its entire child control collection instead of just looking for differences and e.g. removing the now-filtered rows, which is a performance hit. Is there a way to do that? Do I need to implement my own filtering with a second ObservableCollection and bind to that instead of the ICollectionView? Should I be setting the children to invisible based on the filter and just leave them in the ItemsControl?

brap
Aug 23, 2004

Grimey Drawer

raminasi posted:

And delegates!

Thanks :) Func and Action IIRC are also a good way to demonstrate these rules

Adbot
ADBOT LOVES YOU

Dolemite
Jun 30, 2005

New Yorp New Yorp posted:

Yes. More specifically, understand what lambda expressions are and how to use Actions and Funcs. LINQ is a very common use case for lambdas, but you'll see them pop up all over the place.
Yeah, I definitely saw lambdas all over the place within the codebase at the last company. Oddly, I never once saw any lines of code containing Action<T1, T2> or Func<T, TResult>. Took a look at the C# reference docs and these are interesting. They're referred to as delegates, which makes sense. The way Action and Func are used in the code samples, it's almost like you use the Action or Func to describe a method signature. Then, attach your concrete function - with matching parameter list - like a callback. Am I in the ballpark?

quote:

I'd also say it's critical to understand how to isolate dependencies behind interfaces and implement inversion of control, as well as how to write good tests, and when to do unit testing vs integration testing. Not being familiar with unit testing and common practices around it is a huge red flag.
The first line here - is this pretty similar to, if not exactly, dependency injection? I've briefly read about IoC before, but every explanation is written for Comp Sci people. I'm self taught, so chunks of it fly over my head. Even poo poo from the MS docs (I keep having to google Big O notation when I see it in the docs).

Unit/integration testing is a VERY huge hole in my skills. Being a PHP dev, writing in PHP which lets anyone become a dev in five minutes, I have yet to work in a shop that actually, well, unit tests! Hell, even in the last place, our PHP code was not testable! (the code was already like this when I arrived)

The .NET team's code base did have testing all set up, but I barely ever had to modify those. Unit testing courses are on my list to study. Luckily, Pluralsight has such courses.

quote:

Yes. If you want to start hurting your brain a little bit, start reading up on covariant and contravariant generics. It doesn't come up a lot day-to-day but it's one of those things that can impress interviewers if you can talk about it.

brap posted:

IMO covariant and contravariant are lousy terms, hard to remember unless you’ve drilled yourself on type theory or have just encountered the concept many times.
[...Helpful stuff!]

Co-, and Contravariant make a little more sense now. Seems to boil down to ways to introduce type safety?


raminasi posted:

And delegates!

brap posted:

Thanks :) Func and Action IIRC are also a good way to demonstrate these rules

Ha, funny that delegates and all are mentioned. Like brap, I also found Action and Func using delegates.

---

Real talk: Thread, thank you so much for writing all of these :words:. If anyone has more to add, I'm definitely all ears! This is all super helpful! :)

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