You know WPF, but do you know .Net Core?



  • I never understood why the actual technical stack is so important when the differences are very minor.

    I mean, just how many years of experience you want in .Net Framework AND .Net Core?

    Would you mind telling me how they're using Core, that's so much more important to whether I have the experience.

    Thanks.


  • I survived the hour long Uno hand

    @xaade said in You know WPF, but do you know .Net Core?:

    I never understood why the actual technical stack is so important when the differences are very minor.

    I mean, just how many years of experience you want in .Net Framework AND .Net Core?

    Would you mind telling me how they're using Core, that's so much more important to whether I have the experience.

    Thanks.

    I could see some concern if a developer was exclusively .NET Framework (which didn't have Dependency Injection as a built-in feature and is much more likely to have old legacy apps that didn't really do much unit testing / TDD patterns), but you were hiring for a code base that was .NET Core and leveraging the improved handling for DI/IoC and such that is built in now.

    Likewise, if they're using EF Core, the behavior is somewhat different than EF Framework, and less forgiving of some of the lazy LINQ that EF Framework let you get away with (and just switched into "performance suck" mode automatically).

    But mostly, you know it's just HR drone filtering, which uses ==== matching.


  • kills Dumbledore

    I've had recruiters ask if I know .net as well as C# before


  • Considered Harmful

    @Jaloopa I fear that distinction might start to become somewhat relevant (even if the question itself ist nicht einmal falsch) now that C# language support is getting so fragmented among .NET versions.



  • @izzion said in You know WPF, but do you know .Net Core?:

    I could see some concern if a developer was exclusively .NET Framework (which didn't have Dependency Injection as a built-in feature and is much more likely to have old legacy apps that didn't really do much unit testing / TDD patterns)

    One of our recent hires was converting our old .Net 4 stuff that had good architecture and plenty of unit tests to .Net Core... without either. He argued that simply upgrading to .Net Core was the most important thing and we would add in unit testing later.


  • 🚽 Regular

    @Jaloopa said in You know WPF, but do you know .Net Core?:

    I've had recruiters ask if I know .net as well as C# before

    Gotta filter out those VB.Net programmers.


  • Considered Harmful

    @izzion said in You know WPF, but do you know .Net Core?:

    Dependency Injection as a built-in feature

    I've always used Framework and this is the first I'm hearing about this.

    I thought DI was more of a design pattern. How is it built-in?


  • I survived the hour long Uno hand

    @error said in You know WPF, but do you know .Net Core?:

    @izzion said in You know WPF, but do you know .Net Core?:

    Dependency Injection as a built-in feature

    I've always used Framework and this is the first I'm hearing about this.

    I thought DI was more of a design pattern. How is it built-in?

    .NET Core has direct support for a DI container built-in, as compared to .NET Framework where you had to use a 3rd party library (Autofac being the most common one I've seen) in order to get DI.


  • Considered Harmful

    @izzion said in You know WPF, but do you know .Net Core?:

    @error said in You know WPF, but do you know .Net Core?:

    @izzion said in You know WPF, but do you know .Net Core?:

    Dependency Injection as a built-in feature

    I've always used Framework and this is the first I'm hearing about this.

    I thought DI was more of a design pattern. How is it built-in?

    .NET Core has direct support for a DI container built-in, as compared to .NET Framework where you had to use a 3rd party library (Autofac being the most common one I've seen) in order to get DI.

    Ah, OK. You mean an IoC framework. It's possible to use DI without one, if painful.


  • I survived the hour long Uno hand

    @error said in You know WPF, but do you know .Net Core?:

    @izzion said in You know WPF, but do you know .Net Core?:

    @error said in You know WPF, but do you know .Net Core?:

    @izzion said in You know WPF, but do you know .Net Core?:

    Dependency Injection as a built-in feature

    I've always used Framework and this is the first I'm hearing about this.

    I thought DI was more of a design pattern. How is it built-in?

    .NET Core has direct support for a DI container built-in, as compared to .NET Framework where you had to use a 3rd party library (Autofac being the most common one I've seen) in order to get DI.

    Ah, OK. You mean an IoC framework. It's possible to use DI without one, if painful.

    I'm just a converted sysadmin, ok. Keep your :technically-correct: in your back pocket :mlp_smug:


  • 🚽 Regular

    @izzion said in You know WPF, but do you know .Net Core?:

    .NET Core has direct support for a DI container built-in, as compared to .NET Framework where you had to use a 3rd party library

    You mean :airquotes: direct support :airquotes: via using Microsoft.Extensions.Hosting?

    I guess you can say Microsoft is not your typical 3rd party.


  • Discourse touched me in a no-no place

    @Zecc said in You know WPF, but do you know .Net Core?:

    I guess you can say Microsoft is not your typical 3rd party.

    Yes, not enough glasses of cheap wine and mysterious cheese cubes on cocktail sticks.


  • Considered Harmful

    @dkf said in You know WPF, but do you know .Net Core?:

    @Zecc said in You know WPF, but do you know .Net Core?:

    I guess you can say Microsoft is not your typical 3rd party.

    Yes, not enough glasses of cheap wine and mysterious cheese cubes on cocktail sticks.

    That sounds more like a second party. 3rd parties is cheap beer and obvious cubes of government cheese, no stick



  • I really don't get the point of dependency injection, though the disadvantages are awfully familiar.

    Somebody said in A quick intro to Dependency Injection: what it is, and when to use it

    Disadvantages of DI

    • It’s a bit complex to learn, and if overused can lead to management issues and other problems.
    • Many compile time errors are pushed to run-time.
    • Dependency injection frameworks are implemented with reflection or dynamic programming. This can hinder use of IDE automation, such as “find references”, “show call hierarchy” and safe refactoring.

    Couldn't you achieve exactly the same "replace objects" functionality if your code said

    function BlahBlah ()
    {
      IObject something = Zenith.Factory.GetIObject();
      IObject somethingElse = Zenith.Factory.GetIObject(Reflection.GetCaller());
    }
    

    instead of

    function BlahBlah ()
    {
      ObjectA something = new ObjectA();
    }
    

    ?

    Edit: I also don't see any proof of "less boilerplate" that listed as an advantage. My experience with modern fads like DI/IoC has been orders of magnitude more boilerplate.


  • I survived the hour long Uno hand

    @Zenith said in You know WPF, but do you know .Net Core?:

    I really don't get the point of dependency injection, though the disadvantages are awfully familiar.

    Somebody said in A quick intro to Dependency Injection: what it is, and when to use it

    Disadvantages of DI

    • It’s a bit complex to learn, and if overused can lead to management issues and other problems.
    • Many compile time errors are pushed to run-time.
    • Dependency injection frameworks are implemented with reflection or dynamic programming. This can hinder use of IDE automation, such as “find references”, “show call hierarchy” and safe refactoring.

    Couldn't you achieve exactly the same "replace objects" functionality if your code said

    function BlahBlah ()
    {
      IObject something = Zenith.Factory.GetIObject();
      IObject somethingElse = Zenith.Factory.GetIObject(Reflection.GetCaller());
    }
    

    instead of

    function BlahBlah ()
    {
      ObjectA something = new ObjectA();
    }
    

    ?

    Edit: I also don't see any proof of "less boilerplate" that listed as an advantage. My experience with modern fads like DI/IoC has been orders of magnitude more boilerplate.

    The case where I've used it is in defining "class level" dependencies so that they get auto-discovered, but provide a mock point for unit testing. It works really well in a Web Controller environment that's making connections to a database via a shared ORM context, e.g.:

    public class FrobnicatorMaker
    {
        private readonly DbContext _context;
    
        public FrobnicatorMaker(DbContext context)
        {
            _context = context;
        }
        
        public void CreateFrobnicator(Frobnicator newFrobnicator)
        {
            _context.Add(newFrobnicator);
            _context.SaveChanges();
        }
    }
    
    public class FrobnicatorMakerTests
    {
        public void WhenCreatingFrobnicator_AddsFrobnicatorToTheDb_AndSavesChanges()
        {
            var context = new Mock<DbContext>();
            var maker = new FrobnicatorMaker(context.Object);
        
            maker.CreateFrobnicator(new Frobnicator { Id = 1 });
    
            context.Verify(c => c.Add(It.IsAny<Frobnicator>()), Times.Once);
            context.Verify(c => c.SaveChanges(), Times.Once);
        }
    }
    

    And similarly whatever controller needs to create frobnicators takes a dependency on the FrobnicatorMaker, so when the application starts and builds the DI container, the controller will be able to find a FrobnicatorMaker that knows how to talk to the database context that was built, without you having to pass all that down from app startup by hand.

    But you can mock that out in the FrobnicatorMaker very easily, since the dbcontext is a parameter in the class constructor, and not something that was manually configured with a property somewhere or some static method that'll be a complete pain in the ass (or impossibility) to mock out.


  • Notification Spam Recipient

    @Zenith said in You know WPF, but do you know .Net Core?:

    I really don't get the point of dependency injection

    You don't need to know how to construct every dependency you need.

    though the disadvantages are awfully familiar.

    Such as?

    Somebody said in [A quick intro to Dependency Injection: what it is, and when to use

    • Many compile time errors are pushed to run-time.

    "Many" as in

    1. You created dependency tree that is impossible to construct.
      or
    2. You forgot to register something in the container.

    Couldn't you achieve exactly the same "replace objects" functionality if your code said

    That's Factory - you need to write instantiation code and worry about instances' lifecycle/state yourself.

    Edit: I also don't see any proof of "less boilerplate" that listed as an advantage. My experience with modern fads like DI/IoC has been orders of magnitude more boilerplate.

    For short examples, probably yes. For bigger systems, surely no.



  • @MrL said in You know WPF, but do you know .Net Core?:

    That's Factory - you need to write instantiation code and worry about instances' lifecycle/state yourself.

    namespace Zenith
    {
      static class Factory
      {
        IObject GetIObject()
        {
          return new SomethingThatImplmentsIObject();
        }
      }
    )
    

    Oh no, I have to call a constructor. 😐

    I started with C++ and call Win32 in my C#, so I know all about pointers and references and marshaling and so on, and I never see any of that in modern development. Even the using keyword throws the "developers" on these jobs for a loop. My own patterns lean heavily towards static functions where by design I don't throw many objects with lifecycles around at all.

    "Many" as in
    You created dependency tree that is impossible to construct.
    or
    You forgot to register something in the container.

    Another way of reading that is that you have the same two errors, and side effects of them, repeatedly. You know, like when JavaScript just stops executing because it has typos because it never ran through a compiler before deployment.



  • @Zenith Quick question. How would you support different databases? I.e. your application only needs one actual DB in production but it could be either MongoDB, PostgreSQL, SQLite or ... Oracle ;)


  • Banned

    @Rhywden you don't need DI for that, just plain old interfaces.



  • @Zenith said in You know WPF, but do you know .Net Core?:

    @MrL said in You know WPF, but do you know .Net Core?:

    That's Factory - you need to write instantiation code and worry about instances' lifecycle/state yourself.

    namespace Zenith
    {
      static class Factory
      {
        IObject GetIObject()
        {
          return new SomethingThatImplmentsIObject();
        }
      }
    )
    

    Oh no, I have to call a constructor. 😐

    I started with C++ and call Win32 in my C#, so I know all about pointers and references and marshaling and so on, and I never see any of that in modern development. Even the using keyword throws the "developers" on these jobs for a loop. My own patterns lean heavily towards static functions where by design I don't throw many objects with lifecycles around at all.

    "Many" as in
    You created dependency tree that is impossible to construct.
    or
    You forgot to register something in the container.

    Another way of reading that is that you have the same two errors, and side effects of them, repeatedly. You know, like when JavaScript just stops executing because it has typos because it never ran through a compiler before deployment.

    The big advantages I've seen with IoC is in creating services/controllers/whatever that have several dependencies that have several dependencies that have several dependencies.

    Cut-and-pasted-and-obfuscated example from code I occasionally work in:

        public SpecificWorkItemTypeController(
            IWorkItemService workItemService, 
            ICacheFactory cacheFactory,
            IWebRequestService webRequestService,
            IWorkItemMirrorService workItemMirrorService,
            ITelemetryClient telemetryClient,
            ISpecificWorkItemTypeService specificWorkItemTypeServiceParam)
        {
            this.workItemService = workItemService;
            this.cacheFactory = cacheFactory;
            this.webRequestService = webRequestService;
            this.workItemMirrorService = workItemMirrorService;
            this.telemetryClient = telemetryClient;
            this.specificWorkItemTypeService = specificWorkItemTypeServiceParam;
        }
    

    Some of our services/controllers have a ~dozen such dependencies, depending on how far we broke the services into sub-services and how over-sized the top-level service/controller is. And every single one of those dependencies has further dependencies:

    • WorkItemService has 2 dependences, one of which is a IWebRequestService (used above in SpecificWorkItemTypeController, but not necessarily available in other places that use WorkItemService)
    • CacheFactory is from some Nuget package that I'm not going to bother to track down, but I believe it uses Redis and web requests at the very least
    • WebRequestService has 8 dependencies, including CacheFactory used in WorkItemService
    • WorkItemMirrorService has 5 dependencies, and only one of them is the same as from WorkItemService. (WorkItemService fetches/edits stuff in an external data store, and WorkItemMirrorService copies the stuff we actually use to/from that external data store so that we can do clever things. It's... a bit complicated.)
    • TelemetryClient has only 1 dependency, an IHttpContextAccessor
    • SpecificWorkItemTypeService is the actual work that SpecificWorkItemTypeController does. It has 6 dependencies, 3 of which are not present in other dependencies above.

    Add all that up, and SpecificWorkItemTypeController would have to have about 18 dependencies it needs to know about before it can construct the 6 higher-level things it uses. And that's without drilling further into those 18 dependencies to see which ones have sub-dependencies that I haven't mentioned yet. Some of those dependencies are singletons that would need to be passed down the call stack, and others are things that SpecificWorkItemTypeController could instantiate in its constructor.

    And then, your next class has three of those things, plus two others, and now you need to figure out where else to pass some of those dependencies through.

    The upshot: yes, Whatsit-as-the-only-implementor-of-IWhatsit is an annoying pattern, but it would be an unmitigated pain in the ass to have to pass around these complicated dependencies otherwise. Sooner or later, I'd end up sticking these things into globals, and later, creating a global store of these dependencies to make it easier to get at them from awkward parts of code, and then... I've just reimplemented IoC, badly.



  • @Rhywden said in You know WPF, but do you know .Net Core?:

    @Zenith Quick question. How would you support different databases? I.e. your application only needs one actual DB in production but it could be either MongoDB, PostgreSQL, SQLite or ... Oracle ;)

    More or less what @Gąska said. Specifically, I had designed a class that abstracted away even the interfaces. I still use an evolution of that class in projects now. It's just acquired a better version of the old Windows ODBC dialogs for building connections.

    I did the same with Office automation to support different versions of Office and some of its competitors.


  • Banned

    @PotatoEngineer said in You know WPF, but do you know .Net Core?:

    Some of our services/controllers have a ~dozen such dependencies, depending on how far we broke the services into sub-services and how over-sized the top-level service/controller is.

    You didn't break anything, it's still a monolithic piece of code, just spread over multiple classes. In properly designed system with properly encapsulated modules, at least 2-3 of those I's could be removed by doing the needful where it belongs (cache is the prime candidate, followed by mirror).



  • @Gąska said in You know WPF, but do you know .Net Core?:

    @PotatoEngineer said in You know WPF, but do you know .Net Core?:

    Some of our services/controllers have a ~dozen such dependencies, depending on how far we broke the services into sub-services and how over-sized the top-level service/controller is.

    You didn't break anything, it's still a monolithic piece of code, just spread over multiple classes. In properly designed system with properly encapsulated modules, at least 2-3 of those I's could be removed by doing the needful where it belongs (cache is the prime candidate, followed by mirror).

    The various services are getting reused in other classes, with a different (or not) subset of functions in the service being used by each class. The "cache" here is a generic thing that you specialize by giving it a cache "name", and we tend to use each name only within a single class. So it's acting like a cacheForThisWorkItemType -- sure, we could have made a specialist class for this, but the generic-class-with-specific-param works about the same.

    And you still get cross-cutting concerns like logging (in TelemetryClient) and networking (in WebRequestService). So while it's possible to get this specific class to have fewer dependencies by grouping a bit better, it would likely come at the cost of making other classes less convenient. It's a fairly sizable codebase.


  • Banned

    @PotatoEngineer said in You know WPF, but do you know .Net Core?:

    @Gąska said in You know WPF, but do you know .Net Core?:

    @PotatoEngineer said in You know WPF, but do you know .Net Core?:

    Some of our services/controllers have a ~dozen such dependencies, depending on how far we broke the services into sub-services and how over-sized the top-level service/controller is.

    You didn't break anything, it's still a monolithic piece of code, just spread over multiple classes. In properly designed system with properly encapsulated modules, at least 2-3 of those I's could be removed by doing the needful where it belongs (cache is the prime candidate, followed by mirror).

    The various services are getting reused in other classes, with a different (or not) subset of functions in the service being used by each class. The "cache" here is a generic thing that you specialize by giving it a cache "name", and we tend to use each name only within a single class. So it's acting like a cacheForThisWorkItemType -- sure, we could have made a specialist class for this, but the generic-class-with-specific-param works about the same.

    Sounds like it should be instance member rather than externally provided service, then. Dependency injection is for dependencies.

    And you still get cross-cutting concerns like logging (in TelemetryClient) and networking (in WebRequestService).

    Logging - yes. Networking - hell no. Anything with "network" or "web" in its name shouldn't ever appear in business logic and should only be used in the lower layers where actual network communication happens. And if your network communication happens everywhere - there's your problem.


  • Notification Spam Recipient

    @Zenith said in You know WPF, but do you know .Net Core?:

    @MrL said in You know WPF, but do you know .Net Core?:

    That's Factory - you need to write instantiation code and worry about instances' lifecycle/state yourself.

    namespace Zenith
    {
      static class Factory
      {
        IObject GetIObject()
        {
          return new SomethingThatImplmentsIObject();
        }
      }
    )
    

    Oh no, I have to call a constructor. 😐

    If you have no dependencies, then DI is rather pointless. Same as your factory, just new everything on the spot.

    pointers and references and marshaling and so on, and I never see any of that in modern development.

    That's a good thing.

    My own patterns lean heavily towards static functions where by design I don't throw many objects with lifecycles around at all.

    Well, that's horrible, what can I say.

    "Many" as in
    You created dependency tree that is impossible to construct.
    or
    You forgot to register something in the container.

    Another way of reading that is that you have the same two errors, and side effects of them, repeatedly. You know, like when JavaScript just stops executing because it has typos because it never ran through a compiler before deployment.

    It can happen only when you create/change dependencies and it blows the container up, so not repeatedly.
    Call container verification on startup if you're worried about it.



  • @Gąska said in You know WPF, but do you know .Net Core?:

    @PotatoEngineer said in You know WPF, but do you know .Net Core?:

    @Gąska said in You know WPF, but do you know .Net Core?:

    @PotatoEngineer said in You know WPF, but do you know .Net Core?:

    Some of our services/controllers have a ~dozen such dependencies, depending on how far we broke the services into sub-services and how over-sized the top-level service/controller is.

    You didn't break anything, it's still a monolithic piece of code, just spread over multiple classes. In properly designed system with properly encapsulated modules, at least 2-3 of those I's could be removed by doing the needful where it belongs (cache is the prime candidate, followed by mirror).

    The various services are getting reused in other classes, with a different (or not) subset of functions in the service being used by each class. The "cache" here is a generic thing that you specialize by giving it a cache "name", and we tend to use each name only within a single class. So it's acting like a cacheForThisWorkItemType -- sure, we could have made a specialist class for this, but the generic-class-with-specific-param works about the same.

    Sounds like it should be instance member rather than externally provided service, then. Dependency injection is for dependencies.

    I'm honestly not sure what the quibble is here -- is this just about finding the right moment to call it? SpecificWorkItemController needs a named cache, and CacheFactory supplies that named cache on demand. I'll admit that the CacheFactory is a bit weird (and, frankly, smells slightly of globals), and I may have explained it badly, but it's a dependency.

    At the risk of getting further into the weeds, calls to CacheFactory look like this (repeated each time it's used; I'm not sure why there isn't a class instance of the name, but given how janky this is, there must be some implementation reason -- the folks who put this together weren't complete idiots):

    var itemCache = this.cacheFactory.GetCache(nameof(some_data_class));
    await itemCache.RemoveAsync("some_key_name");
    

    So the CacheFactory is definitely a dependency, though the cache-reference itself possibly should have been initialized in the SpecificWorkItemConstructor rather than on access.

    And you still get cross-cutting concerns like logging (in TelemetryClient) and networking (in WebRequestService).

    Logging - yes. Networking - hell no. Anything with "network" or "web" in its name shouldn't ever appear in business logic and should only be used in the lower layers where actual network communication happens. And if your network communication happens everywhere - there's your problem.

    Funny you should mention that -- I just noticed that the SpecificWorkItemController doesn't actually use the WebRequestService in its constructor. I'm guessing it got subsumed into SpecificWorkItemService, and then never cleaned up.

    Networking is still pretty cross-cutting, though: I agree that it shouldn't be up in the controllers, but there are potentially dozens of services that are the "last stop" for a particular workflow, and which need to interact with low-level concerns like storage and networking. Both WorkItemService and WorkItemMirrorService, for instance, interact with that external data store, so of course they're doing network requests.


  • Banned

    @PotatoEngineer said in You know WPF, but do you know .Net Core?:

    @Gąska said in You know WPF, but do you know .Net Core?:

    @PotatoEngineer said in You know WPF, but do you know .Net Core?:

    @Gąska said in You know WPF, but do you know .Net Core?:

    @PotatoEngineer said in You know WPF, but do you know .Net Core?:

    Some of our services/controllers have a ~dozen such dependencies, depending on how far we broke the services into sub-services and how over-sized the top-level service/controller is.

    You didn't break anything, it's still a monolithic piece of code, just spread over multiple classes. In properly designed system with properly encapsulated modules, at least 2-3 of those I's could be removed by doing the needful where it belongs (cache is the prime candidate, followed by mirror).

    The various services are getting reused in other classes, with a different (or not) subset of functions in the service being used by each class. The "cache" here is a generic thing that you specialize by giving it a cache "name", and we tend to use each name only within a single class. So it's acting like a cacheForThisWorkItemType -- sure, we could have made a specialist class for this, but the generic-class-with-specific-param works about the same.

    Sounds like it should be instance member rather than externally provided service, then. Dependency injection is for dependencies.

    I'm honestly not sure what the quibble is here -- is this just about finding the right moment to call it? SpecificWorkItemController needs a named cache, and CacheFactory supplies that named cache on demand. I'll admit that the CacheFactory is a bit weird (and, frankly, smells slightly of globals), and I may have explained it badly, but it's a dependency.

    That answers the wrong question. The right one is: are the same caches shared between different modules? If yes, then it makes sense as a service. But your earlier post sounds like they aren't - in which case, why the hell isn't the cache part of the module itself?

    And you still get cross-cutting concerns like logging (in TelemetryClient) and networking (in WebRequestService).

    Logging - yes. Networking - hell no. Anything with "network" or "web" in its name shouldn't ever appear in business logic and should only be used in the lower layers where actual network communication happens. And if your network communication happens everywhere - there's your problem.

    Funny you should mention that -- I just noticed that the SpecificWorkItemController doesn't actually use the WebRequestService in its constructor. I'm guessing it got subsumed into SpecificWorkItemService, and then never cleaned up.

    LOL. That was unexpected.

    Networking is still pretty cross-cutting, though: I agree that it shouldn't be up in the controllers, but there are potentially dozens of services that are the "last stop" for a particular workflow, and which need to interact with low-level concerns like storage and networking.

    So these "last stop" services should have that dependency, and everything else shouldn't. It all comes down to Single Responsibility Principle. If you have many dependencies, you likely do a lot of things in one place. If you've had those things properly separated so that every module is responsible for just one thing, each of those modules wouldn't have so many dependencies to start with.



  • @Gąska said in You know WPF, but do you know .Net Core?:

    @PotatoEngineer said in You know WPF, but do you know .Net Core?:

    @Gąska said in You know WPF, but do you know .Net Core?:

    @PotatoEngineer said in You know WPF, but do you know .Net Core?:

    @Gąska said in You know WPF, but do you know .Net Core?:

    @PotatoEngineer said in You know WPF, but do you know .Net Core?:

    Some of our services/controllers have a ~dozen such dependencies, depending on how far we broke the services into sub-services and how over-sized the top-level service/controller is.

    You didn't break anything, it's still a monolithic piece of code, just spread over multiple classes. In properly designed system with properly encapsulated modules, at least 2-3 of those I's could be removed by doing the needful where it belongs (cache is the prime candidate, followed by mirror).

    The various services are getting reused in other classes, with a different (or not) subset of functions in the service being used by each class. The "cache" here is a generic thing that you specialize by giving it a cache "name", and we tend to use each name only within a single class. So it's acting like a cacheForThisWorkItemType -- sure, we could have made a specialist class for this, but the generic-class-with-specific-param works about the same.

    Sounds like it should be instance member rather than externally provided service, then. Dependency injection is for dependencies.

    I'm honestly not sure what the quibble is here -- is this just about finding the right moment to call it? SpecificWorkItemController needs a named cache, and CacheFactory supplies that named cache on demand. I'll admit that the CacheFactory is a bit weird (and, frankly, smells slightly of globals), and I may have explained it badly, but it's a dependency.

    That answers the wrong question. The right one is: are the same caches shared between different modules? If yes, then it makes sense as a service. But your earlier post sounds like they aren't - in which case, why the hell isn't the cache part of the module itself?

    No, the caches aren't shared, but the implementation of "what is this cache thing" and "how does it store" and "how persistent is this anyway" is handled by CacheFactory. As I mentioned above, I think it's backed by Redis, so the data all ends up in the "same" bucket, but as a consumer who just says "I need a cache to stick DooHickey structs into", I don't care. The cache lives for however long CacheFactory says (and the class will evict things from the cache as it does something to make them stale), and I don't need to worry about the lifecycle of controller classes.

    Yes, this is prone to eventual collision, as we're using a struct-like-class's name as the cache name, but the implementation is that, so far, no two classes have ever used this kind of cache for the same object type. Or maybe they did, and it didn't matter much because each class has a similar cache strategy and it didn't end up being a problem. Because we've done a not-completely-awful job of separating concerns so that the relatively-low-level structs that we want to cache aren't interesting outside of the particular class that wants to cache them.


  • Discourse touched me in a no-no place

    @Rhywden said in You know WPF, but do you know .Net Core?:

    Quick question. How would you support different databases? I.e. your application only needs one actual DB in production but it could be either MongoDB, PostgreSQL, SQLite or ... Oracle

    You wouldn't unless you enjoy punishing yourself 🍹



  • @loopback0 said in You know WPF, but do you know .Net Core?:

    @Rhywden said in You know WPF, but do you know .Net Core?:

    Quick question. How would you support different databases? I.e. your application only needs one actual DB in production but it could be either MongoDB, PostgreSQL, SQLite or ... Oracle

    You wouldn't unless you enjoy punishing yourself 🍹

    I have heard vague anecdata that you could set up your storage to use any kind of DB, but in practice, it's a lot of extra effort and it's never* used. (Granted, if you didn't prepare for a change, and you're suddenly forced, it's a much larger effort.)

    *rounded to whichever precision makes this true



  • @PotatoEngineer said in You know WPF, but do you know .Net Core?:

    (Granted, if you didn't prepare for a change, and you're suddenly forced, it's a much larger effort.)

    If you did prepare, but didn't test every supported DB engine, then the effort is still large. If you did test every DB engine regularly, then you simply paid for the effort long before you needed it.

    The only time I see multiple back-ends as useful is when you sell your product and a choice of back-ends will win more sales. However, it's always a good idea for the storage layer to be abstracted enough that the effort to support a new back-end is at least straightforward, even if significant.


  • Banned

    @PotatoEngineer said in You know WPF, but do you know .Net Core?:

    @Gąska said in You know WPF, but do you know .Net Core?:

    @PotatoEngineer said in You know WPF, but do you know .Net Core?:

    @Gąska said in You know WPF, but do you know .Net Core?:

    @PotatoEngineer said in You know WPF, but do you know .Net Core?:

    @Gąska said in You know WPF, but do you know .Net Core?:

    @PotatoEngineer said in You know WPF, but do you know .Net Core?:

    Some of our services/controllers have a ~dozen such dependencies, depending on how far we broke the services into sub-services and how over-sized the top-level service/controller is.

    You didn't break anything, it's still a monolithic piece of code, just spread over multiple classes. In properly designed system with properly encapsulated modules, at least 2-3 of those I's could be removed by doing the needful where it belongs (cache is the prime candidate, followed by mirror).

    The various services are getting reused in other classes, with a different (or not) subset of functions in the service being used by each class. The "cache" here is a generic thing that you specialize by giving it a cache "name", and we tend to use each name only within a single class. So it's acting like a cacheForThisWorkItemType -- sure, we could have made a specialist class for this, but the generic-class-with-specific-param works about the same.

    Sounds like it should be instance member rather than externally provided service, then. Dependency injection is for dependencies.

    I'm honestly not sure what the quibble is here -- is this just about finding the right moment to call it? SpecificWorkItemController needs a named cache, and CacheFactory supplies that named cache on demand. I'll admit that the CacheFactory is a bit weird (and, frankly, smells slightly of globals), and I may have explained it badly, but it's a dependency.

    That answers the wrong question. The right one is: are the same caches shared between different modules? If yes, then it makes sense as a service. But your earlier post sounds like they aren't - in which case, why the hell isn't the cache part of the module itself?

    No, the caches aren't shared, but the implementation of "what is this cache thing" and "how does it store" and "how persistent is this anyway" is handled by CacheFactory. As I mentioned above, I think it's backed by Redis, so the data all ends up in the "same" bucket, but as a consumer who just says "I need a cache to stick DooHickey structs into", I don't care.

    Sounds like bad design all around. Those definitely should be instance members.

    The cache lives for however long CacheFactory says (and the class will evict things from the cache as it does something to make them stale), and I don't need to worry about the lifecycle of controller classes.

    That sounds even more like bad design. But not because of lifetime of anything. It's bad design because why is your controller operating directly on the cache anyway? Yes, I can see that making sense sometimes. But my experience tells me that 99 times out of 100, the cache read/store should happen in the object factory/data loading module, not in the action controller, or maybe it shouldn't even be used at all because these objects are so easy to recreate.



  • @Gąska said in You know WPF, but do you know .Net Core?:

    The cache lives for however long CacheFactory says (and the class will evict things from the cache as it does something to make them stale), and I don't need to worry about the lifecycle of controller classes.

    That sounds even more like bad design. But not because of lifetime of anything. It's bad design because why is your controller operating directly on the cache anyway? Yes, I can see that making sense sometimes. But my experience tells me that 99 times out of 100, the cache read/store should happen in the object factory/data loading module, not in the action controller, or maybe it shouldn't even be used at all because these objects are so easy to recreate.

    Okay, now I finally get what you're complaining about: that the controller is doing this. Would it change your answer, at all, if the controller was a giant pile of stubs that passed all of their logic to ServiceNamedAfterTheController, as opposed to having the controller do work? That's the more recent style in this codebase, anyway.


  • Banned

    @PotatoEngineer yes, it would. The new one is "why the fuck is the controller passing those things around, couldn't the parts that actually need them have them from the start?"



  • @Gąska said in You know WPF, but do you know .Net Core?:

    @PotatoEngineer yes, it would. The new one is "why the fuck is the controller passing those things around, couldn't the parts that actually need them have them from the start?"

    And now I've lost track of which bits you're complaining about. My earlier posts were about how "dependency injection = good, because there's piles of dependencies down there somewhere, and callers shouldn't have to worry about the details quite so much." So I've been mildly confused by your complaints, because the dependency stack ends up pretty-much-the-same (and one trivial level deeper) if the controller outsources everything to a service that uses the same dependencies that the controller otherwise would have been using. Yes, putting everything in the controller is inconvenient when someone else wants to call that particular function, so we've stopped doing that because we keep finding reasons to use logic that's in a controller.

    Sure, the cache is weird, but all I can say is that it's an implementation detail that I'm not familiar with enough to defend it to the level of detail that you're demanding over a point that I'm not trying to make. I'm unclear on why it must be an instance member instead of a dependency, if the logic that powers the cache is best encapsulated in a separate service rather than re-implemented in this class that's using the cache.


  • Banned

    @PotatoEngineer said in You know WPF, but do you know .Net Core?:

    @Gąska said in You know WPF, but do you know .Net Core?:

    @PotatoEngineer yes, it would. The new one is "why the fuck is the controller passing those things around, couldn't the parts that actually need them have them from the start?"

    And now I've lost track of which bits you're complaining about. My earlier posts were about how "dependency injection = good, because there's piles of dependencies down there somewhere, and callers shouldn't have to worry about the details quite so much."

    And my main point is "there are no details to worry about if you design your application right in the first place, since you'll have very few external dependencies in any given module and all of them will be immediately relevant".



  • @Gąska said in You know WPF, but do you know .Net Core?:

    @PotatoEngineer said in You know WPF, but do you know .Net Core?:

    @Gąska said in You know WPF, but do you know .Net Core?:

    @PotatoEngineer yes, it would. The new one is "why the fuck is the controller passing those things around, couldn't the parts that actually need them have them from the start?"

    And now I've lost track of which bits you're complaining about. My earlier posts were about how "dependency injection = good, because there's piles of dependencies down there somewhere, and callers shouldn't have to worry about the details quite so much."

    And my main point is "there are no details to worry about if you design your application right in the first place, since you'll have very few external dependencies in any given module and all of them will be immediately relevant".

    Our business logic is... tangled, because our requirements are tangled. Things are connected to stuff, stuff is organized into bunches, bunches are corralled into herds, and some herds know some things about stuff. The application design could be better (it always can), but I can't imagine shaving off more than, say, about 20% of the dependencies of any given service.

    It ain't pretty, but it's home! Which is why it's called Stockholm, of course. Our local villain is Syndrome, he's a real peach.



  • @MrL said in You know WPF, but do you know .Net Core?:

    Well, that's horrible, what can I say.

    It's also easier to debug, easier to test, easier to maintain, and faster to execute. So take your metric ton of dependency injection framework boilerplate spaghetti and bring somebody else's server farm to its knees. 🍹


  • BINNED

    @PotatoEngineer said in You know WPF, but do you know .Net Core?:

    Sooner or later, I'd end up sticking these things into globals, and later, creating a global store of these dependencies to make it easier to get at them from awkward parts of code, and then... I've just reimplemented IoC, badly.

    So basically the IoC implementation is just a container of global variables?



  • @MrL said in You know WPF, but do you know .Net Core?:

    That's a good thing.

    Not when it's replaced by dumb web service APIs that transmit an entire recordset just to get the length written by developers that don't understand computing concepts because it's all hidden under a game of buzzword bingo. And I'm posting this separately because the magic JavaScript dependency injection framework powering this site can't scroll a fucking textbox.



  • @topspin said in You know WPF, but do you know .Net Core?:

    @PotatoEngineer said in You know WPF, but do you know .Net Core?:

    Sooner or later, I'd end up sticking these things into globals, and later, creating a global store of these dependencies to make it easier to get at them from awkward parts of code, and then... I've just reimplemented IoC, badly.

    So basically the IoC implementation is just a container of global variables?

    That's how I'd implement it if I was in a rush to get something else done.

    I'm sure that IoC is really implemented with a graceful globally-accessible API to allow the developer to register a set of instances/constructors to a variety of interfaces, so that consumer classes can seamlessly get the correct instances instantiated/inserted when they need it. Totally not global variables masquerading as singletons. Nope nope nope.

    (In my experience, it's a real treat to use: you register the interface with the IoC, tell it which class to use, and all I have to do as a consumer is to say "this class takes an IThingy in its constructor", and the IoC does everything for me for Controllers' constructor arguments – and for everything else, I might have to make an explicit call to serviceProvider.GetRequiredService<IInterfaceName>(). Internally, it's an ugly mess of reflection, but I try real hard not to think about it.)


  • BINNED

    @PotatoEngineer so what’s the advantage over globals if it’s just pretend-not-to-be-globals?


  • Banned

    @PotatoEngineer said in You know WPF, but do you know .Net Core?:

    @Gąska said in You know WPF, but do you know .Net Core?:

    @PotatoEngineer said in You know WPF, but do you know .Net Core?:

    @Gąska said in You know WPF, but do you know .Net Core?:

    @PotatoEngineer yes, it would. The new one is "why the fuck is the controller passing those things around, couldn't the parts that actually need them have them from the start?"

    And now I've lost track of which bits you're complaining about. My earlier posts were about how "dependency injection = good, because there's piles of dependencies down there somewhere, and callers shouldn't have to worry about the details quite so much."

    And my main point is "there are no details to worry about if you design your application right in the first place, since you'll have very few external dependencies in any given module and all of them will be immediately relevant".

    Our business logic is... tangled, because our requirements are tangled.

    Excuses, excuses. It's not just business logic that is tangled - non-business is tangled too, and you have business tangled with non-business. The reason I'm picking on cache specifically is because caches are supposed to be transparent to business logic. If your project didn't get that right, I'm pretty sure it got some other things wrong too.

    I'm not saying you should drop everything and start refactoring. I'm just pointing out that, in my experience, 99 out of 100 "we have so many dependencies that DI framework saves us a lot of boilerplate" is just a symptom of a more fundamental problem with the overall design of the program, that could've been avoided if the decisions made many years ago were made differently, without any loss to ergonomics or quality or anything. And it could probably be fixed even now, in a fantasy world where you are allowed to dump 200-300 dev hours into refactoring.


  • Banned

    @topspin said in You know WPF, but do you know .Net Core?:

    @PotatoEngineer so what’s the advantage over globals if it’s just pretend-not-to-be-globals?

    You know where they are. And that's about it.


  • BINNED

    @Gąska said in You know WPF, but do you know .Net Core?:

    @topspin said in You know WPF, but do you know .Net Core?:

    @PotatoEngineer so what’s the advantage over globals if it’s just pretend-not-to-be-globals?

    You know where they are. And that's about it.

    That’s a good point actually, but I don’t need a framework for that.



  • @Jaime said in You know WPF, but do you know .Net Core?:

    @izzion said in You know WPF, but do you know .Net Core?:

    I could see some concern if a developer was exclusively .NET Framework (which didn't have Dependency Injection as a built-in feature and is much more likely to have old legacy apps that didn't really do much unit testing / TDD patterns)

    One of our recent hires was converting our old .Net 4 stuff that had good architecture and plenty of unit tests to .Net Core... without either. He argued that simply upgrading to .Net Core was the most important thing and we would add in unit testing later.

    Conversion to .NET Core from .NET v4 is refactoring. Trying to do refactoring without unit test to ensure there's full compatibility between the codebase versions is 💩



  • @Zenith Or in case let's assume there's no satisfactory DI libraries to be found, you can always use good old #if...#endif directive to replace parts with mock objects on testing.

    Of course, that also mean you're not testing the "live" code in your testing. And you need to define multiple test symbols (say, for a lot of functions you'll want to replace all your data access classes with mocked one. However you won't want to test on mock data access classes if the test is on those data access functions.



  • Status: Probably staring at a dependency injection error right now. Clicked an "availability status" link in an e-mail from a major retailer. Just a "page" (MVC controller) and an item number, what could go wrong? Oops, an error occurred, but we totally logged it for reals real LOL.



  • @topspin said in You know WPF, but do you know .Net Core?:

    @PotatoEngineer so what’s the advantage over globals if it’s just pretend-not-to-be-globals?

    1. You don't need to build up each service-with-injected-dependencies with its dependencies individually. So if Service1 takes A, B, and C, and Service2 takes B, C, and D, you just register Service1, Service2, A, B, C, and D in any order, and the IoC library will take care of matching up params and consumers. If you go the I-build-my-own-globals route, then you'd have to be more explicit about order of operations.
    2. Some programmatic injection of runtime stuff. Some services need the current security principal injected, or some other thing, and the IoC library makes it easier to do. There's some currentPrincipalProvider.getScopedService() calls that are required for getting to some servers/services I use.
    3. Syntactic sugar. It's pretty nice the way that the controllers get their params filled automatically and I don't have to think about it. (Heaven help me if this breaks, but if it does, it's a 99% chance that I just didn't register something correctly.)
    4. Probably something else. I didn't set this up in the first place, so I'm not sure what it did/didn't solve. I mainly just consume this.


  • @topspin said in You know WPF, but do you know .Net Core?:

    @PotatoEngineer said in You know WPF, but do you know .Net Core?:

    Sooner or later, I'd end up sticking these things into globals, and later, creating a global store of these dependencies to make it easier to get at them from awkward parts of code, and then... I've just reimplemented IoC, badly.

    So basically the IoC implementation is just a container of global variables?

    The bad one, yes.

    @topspin said in You know WPF, but do you know .Net Core?:

    @PotatoEngineer so what’s the advantage over globals if it’s just pretend-not-to-be-globals?

    You can have several different set of globals at once. Which is actually native feature of some languages, but not the mainstream ones (for a good reason).

    Also, I would my two cents: the point of IoC DI is that you don't actually need to use any framework; you can get the code and just write everything directly by calling constructors/methods and passing arguments. If the code needs correctly set up DI context, it's basically "J2EE Application Server". And that is, like all IBM creations, inspired by Necronomicon.


Log in to reply