Sunday, September 1, 2019

Interfaces in C# 8 are a Bit of a Mess

Interfaces are getting a significant overhaul in C# 8. I've been looking at some of the features, and I'm frankly stumped at some of the decisions that have been made. I'm also a bit disappointed at the messaging from Microsoft regarding these new features.

I'll start out by saying that I may be completely missing the point. So feel free to leave your comments. I'm looking for any help I can get at this point.

Code for this article (and further experimentation) is available on GitHub: jeremybytes/interfaces-in-csharp-8. Note: at the time of this writing, the code uses .NET Core 3.0 Preview 8.

Some of the new features include the following:
  • Default implementation for interface members
  • Access modifiers - public, private, protected, internal
  • abstract members
  • static members
Let's start by looking at default implementation.

Default Implementation of Interface Members
Default implementation has been the most widely-touted enhancement to interfaces in C# 8. Mads Torgersen wrote an article on the topic: This article has the example that I've seen repeated when this topic is brought up.

The assumption is that we have an interface with a single member, and a class that implements that interface.

Then another member is added to the interface. To keep the existing implementations from breaking, we add default behavior to the new interface member.

Here is an ILogger interface that has 2 members. The second has a default implementation (this is in the ILogger.cs file in the GitHub project):

The original class only implements the first member (this is in the InitialLogger.cs file in the GitHub project):

Since the 2nd member (the Log method that takes an exception parameter) has a default implementation, this code builds just fine.

We can put together a little console application that shows that both methods work (this is in the Program.cs file in the GitHub project);

The output shows that calls to both of the Log methods work:

Of course, we are also free to provide an implementation of the 2nd member in the class. This would hide the default implementation.

My Thoughts
When I first heard of this feature, I thought it was a bad idea. This blurs the line between an abstract class (that contains implementation) and an interface (no implementation).

My recommendation would be to avoid default implementation for a few reasons.

First, I have only seen trivial examples where a good default exists. Most of our code is more complicated than that. If I want to add "Save" functionality to my API, there is probably no good default for that. If I end up throwing a NotImplementedException as a default, then that doesn't accomplish anything other than moving compile-time errors to run-time errors.

Second, we already have a way to extend functionality of an interface -- through interface inheritance. By creating a new "extended" interface that inherits from the original, the existing interface implementations continue to work. And if a client needs the extended functionality, then the client can use the new extended interface (this also helps us follow the interface segregation principle).

Third, interfaces are a difficult enough concept as it is. When I started programming professionally, it took me a good 2 years before I really understood interfaces and what they were good for. This was such a frustration to me that I've spent the last 7-1/2 years helping developers understand interfaces (this includes 33 live presentations, various blog articles, and a Pluralsight course that has over 100,000 unique viewers). If nothing else, this points out that the topic can be a challenge for developers to grasp.

Messaging from Microsoft
Most of the messaging from Microsoft has been around this -- adding functionality to an existing API. The idea that if you have an existing interface, you can add to it without breaking the initial implementations.

But it turns out that this is not the only reason this feature was added to the language.

For this, we can turn to C# 8 Language Design Proposals. Here is the article for default implementation:

There are 3 reasons stated for this change: (1) extending APIs, (2) interoperability with Android (Java) and iOS (Swift), (3) supporting the "traits" language feature.

"Traits" are really a way of doing mix-ins with classes. The idea is that small groupings of code can be combined to form classes.

I think that traits can be a good idea; however, I think that using interfaces to implement that feature is a mistake. Another construct in C# would be less confusing to developers (even if it compiles down to the same IL).

To show the confusion that's on its way, let's take a look at some of the other changes to interfaces that facilitate these features.

Access Modifiers
Previously, access modifiers were not allowed on interface members. Here's an example from a .NET Framework 4.7.2 project:

Previously, all interface members were automatically visible. We weren't even allowed to mark something as "public". If we think of interfaces as a contract, then it makes sense that we are not allowed to use access modifiers. The terms of the contract need to be visible to everyone -- both the clients and the implementing classes.

But things are different with C# 8. We can have access modifiers. Here's an example of an interface that has a "protected" member (this is in the IReader.cs file in the GitHub project):

This causes a bit of confusion when implementing the interface. Mainly because the tooling isn't there yet. (I'm hoping it will be there soon; .NET Core 3.0 releases in 3 weeks.)

If we create a new class and mark it with the interface, we get the standard errors that there are members that are not implemented:

Note: since the Save method has a default implementation, it does not show up with the missing members.

I use the Ctrl+. shortcut to implement interfaces (or use the light bulb helper):

This, however, only implements the non-protected member. So if we use this "fix" and try to build, we still get errors:

The protected member is still not implemented.

To implement the protected member, we need to do an explicit implementation. This associates the method explicitly with the interface. This is another option on the Ctrl+. menu:

But if we do this, it implements *both* of the methods explicitly:

Note: Regardless of whether we use "Implement interface" or "Implement interface explicitly". The Save method (which has a default implementation) is not included. If we want to override that behavior, we need to type that in ourselves.

Now we have 2 implementations for "GetItem": one associated with the class, and one associated with the interface. This is probably not what we want.

You can take a look at the IntReader.cs file in the GitHub project to see the implementation that is probably more inline with what we want in this case:

This is a tooling problem. It can be fixed in Visual Studio, but it opens the door to some of the other things that come up when we have access modifiers on interface members.

Private Members
Since we can add access modifiers to interface members, we can have "private" members.

In this code, the "Save" method is marked as "private". The compiler will only let you mark a member as "private" if it has an implementation. So that's good.

But what would we do with a private interface member? The answer is not much. It is not accessible outside of the interface, so it is really only good if we want to create a method that will be used in other default implementations in the interface.

At this point, we're really creating a class, and an abstract class would be a much better solution.

Abstract Members
The thing that make me go  O_o  is the fact that we can have "abstract" interface members. The following code is valid and builds (this is the final IReader.cs file from GitHub):

In this code, the "GetItem" method is marked as "abstract".

What does this even mean? When we're dealing with abstract classes, abstract members have a declaration but not implementation. That describes *everything* in an interface (okay, it described everything in an interface until we had default implementation).

I'm a bit confused by this. The code behaves the same way whether the "abstract" is here or not.

I'll need to do a bit more research to find out why this is necessary.

Static Members
Interfaces can also have static members. This is something that is brand new. Statics were not allowed at all in interfaces previously.

If you're curious about using static members in interfaces, take a look at the tutorial: Update interfaces with default interface members in C# 8.0. This example shows how static members can be used to parameterize default implementations.

My Thoughts
Static members break the spirit of interfaces. Interfaces have to do with describing a contract -- something that a client can rely upon being there and an implementer has to include. The tutorial shows using static fields to hold data. Those are now implementation details.

My Thoughts
In my view, interfaces in C# 8 are a bit of a mess. We can no longer think of them as a contract. They are no longer the "shape" of a set of members in a class or struct. Instead, they can contain implementation. The members are no longer automatically visible, they can be private, protected, or internal.

I may be completely missing the point. But I can no longer answer the question: "What is an interface?"

An interface is no longer one thing. It has multiple aspects depending on how we want to use it. It will take a while to make sense of the new world. If you want to share your views, please leave a comment. I'm looking for clarification and to gain a better understanding of the changes.

Stay tuned to the GitHub project (jeremybytes/interfaces-in-csharp-8). There will be more samples added and more articles. Together we can figure this out.

Happy Coding!

Saturday, August 24, 2019

Converting .NET Framework to .NET Core - UseShellExecute Has a Different Default Value

I came across a difference between .NET Framework and .NET Core that caused a bit of frustration when I was converting a project.
Process.StartInfo.UseShellExecute defaults to "true" in .NET Framework, but it defaults to "false" in .NET Core.
If you have code using Process, this may come up when converting your projects over to .NET Core.

Discovery and Pain
.NET Core is the path forward. And I have been slowly converting my code from .NET Framework to .NET Core. This is especially interesting because I can now move my WPF projects over to .NET Core 3 (which will be released in about a month).

Yesterday, I was converting my maze generation program (GitHub: jeremybytes/mazes-for-programmers) and ran into an issue. This program generates a maze using a selected algorithm and outputs it as a text maze (using ASCII art) as well as a graphical maze (using a bitmap). The bitmap gets saved to the file system as a .png file.

The application itself is a console application, and I want to show the graphical maze after it is saved to the file system. So, I do a Process.Start to have Windows open up the file using the default bitmap viewer. Here is the code for that (code can be found in Program.cs of the DrawMaze project):

This works fine with .NET Framework. But when I ran it in .NET Core, I got a runtime exception.

"System.ComponentModel.Win32Exception; 'The specified executable is not a valid application for this OS platform.'"

This left me scratching my head.

There was something wrong with the Process.Start. This is not something I do regularly, but I do know that "StartInfo" has a lot of properties. So I started fumbling around looking for answers.

Google & StackOverflow were not very helpful -- probably because I didn't know what to search for.

One thing I tried was changing the file name to ".\maze.png". You need to use a ".\" in PowerShell to execute something, so I thought maybe I needed it here. That didn't work.

Another thing I tried was changing the "FileName" property to "cmd.exe" and adding an "Argument" property that had "maze.png". That didn't work.

Then I started looking through the docs for Process and more specifically StartInfo.

Eventually, I ended up at the "UseShellExecute" property (docs) and found this little gem:

"The default is true on .NET Framework apps and false on .NET Core apps."


As soon as I saw this, I knew it was my problem. This property determines whether to use the operating system shell to start a process.

When it is "true", it's basically the same as typing "maze.png" from a command prompt. If you do this from a Windows command prompt, it will will try to open the file using the default image viewer.

But if it is "false", then we're saying "execute this file", and a bitmap is not an executable.

Fixing the Code
The code fix was easy after that. I just had to set the UseShellExecute property to "true":

Then the program worked. On my machine it opens the "Photos" app, which is my default viewer.

(And in case you're curious, here's the full image of the maze that was generated -- be sure to zoom in to see all the interesting detail)

Cross-Platform Solution?
I find the cross-platform features of .NET Core to be particularly interesting. So I've been trying a bunch of my .NET Core projects on MacOS and on Linux.

I have not figured out how to do something like this in a cross-platform-friendly way. The text maze generation in a console/terminal window works just fine. The bitmap is created and saved to the file system just fine. But I'm not sure how to display the bitmap file automatically. I'm really relying on the default Windows behavior in the current application.

One potential solution is to put in some OS-specific code. If it is Windows, do a shell execute. If it is MacOS, open up the "Preview" application with the bitmap file. But there's really no equivalent in the Linux world. What I'd like to do is "open this bitmap with the default bitmap viewer". That doesn't exist on Linux.

Another idea is to open the file in a browser. But there's no guarantee that a particular browser is available on a particular platform.

If you have any ideas, be sure to leave a comment on this article.

Wrap Up
I really like .NET Core, and I'm excited for the Core 3 release so I can start using it with my WPF application on Windows.

.NET Core is the path forward. So far, moving code over to new projects has been fairly painless. But I have run into a few issues like this one that are a bit frustrating. This is a breaking change between platforms.

I did not run this application through the .NET Portability Analyzer. This application is small and straight forward -- a console application and a couple of libraries that aren't doing anything exciting; they don't even use any external NuGet packages. I should probably run it through the analyzer to see if it finds this change between the platforms.

I'll be sure to post any other strangeness I find when doing my conversions. And I'm hoping to do a walkthrough of an entire application conversion sometime soon.

Happy Coding!

Monday, July 1, 2019

Linking Files in Visual Studio

Linking files in Visual Studio lets us use the same physical file in different projects. I always forget how to do this, so I'm making notes here.

The Short Version
If you already know about linking files and why you want to do it, here's the short version that has the mechanics (details are further down).

To link files, use the "Add Existing Item" dialog. One way of getting to this is to right-click on the project and choose "Add", then "Existing Item...".

When the dialog opens, locate the file you want to link to.

Then DO NOT CLICK THE "Add" BUTTON. Instead, notice that the "Add" button is a drop-down (this is really easy to overlook).

Use the drop-down and select "Add As Link".

That's all there is to it!

If you want details on what linking files really means and why you might want to do it, keep reading.

The Long Version
To show the differences between copying files and linking files, I've set up a solution with 3 class libraries.

The first library, "A-SourceLibrary", is a .NET Core 3.0 class library that contains the file we want to use in the other projects. The file is called "Person.cs".

The file system shows that we have a .csproj file and one .cs file:

The second library, "B-CopiedFiles", is a .NET Standard 2.0 class library that does not currently have any files.

The file system shows that we have a .csproj file only:

The third library, "C-LinkedFiles", is a .NET Framework 4.7.2 class library that does not currently have any files.

The file shows that we have only a .csproj file here as well:

Let's look at the difference between copying and linking.

Copying Files
Copying files from one project to another is out in the open (for the most part).

As one option, we can right-click on the source file and choose "Copy" and then right-click on the destination project and choose "Paste".

The keyboard shortcuts Ctrl+C and Ctrl+V also work as long as you have the correct items selected in the Solution Explorer.

Add Existing Item
Another way to copy a file is to Add an Existing Item. We will do this to copy the "Person.cs" file from the "A-SourceLibrary" project to the "B-CopiedFiles" project.

Just like above, you right-click on the project, and select "Add", then "Existing Item..."

When the dialog pops up, just locate the file.

And then click the "Add" button.

This will create a *copy* of the file in the destination project. We see the new file pop up in the Solution Explorer.

But we also see a new file pop up in the File Explorer for the project.

Now we have 2 separate copies of the "Person.cs" file. If we make changes to one file, it will not be reflected in the other.

If we only want 1 physical file that appears to multiple projects, we can use a linked file instead.

Linking Files
The problem I always have with linking files is that I forget how to do it. When I copy files from one project to another, I usually do the "Copy"/"Paste" options that show up in the right-click menu (or with the keyboard shortcuts).

When I want to link a file, I copy the file to my clipboard and the look for a "Paste Special" or something similar on the right-click menu. That doesn't exist. Then I have to try to remember how to do it. So here it is.

This time we will link to the "Person.cs" file in the "A-SourceLibrary" project in the "C-LinkedFiles" project.

As we saw above, we just need to right-click on the destination project ("C-LinkedFiled") and choose "Add" then "Existing Item..."

Then in the dialog, locate the source file in the "A-SourceLibrary" folder.

Then use the drop-down on the "Add" button to choose "Add As Link".

This adds a link to the file in the destination project. We can see that the "Person.cs" file shows up as part of the "C-LinkedFiles" project in Visual Studio.

But if we look at the file system, the "C-LinkedFiles" folder does *not* have a "Person.cs" file.

When we look at the Solution Explorer, we can see that there is a different icon for the linked file compared to the copied file (and the original file):

What we have done is create a reference in one project to a file in a different project. So if we make changes in one location, it is reflected in both.

Why Would We Want to Link Files?
So why would we want to do this? Usually if we want to use an existing class, we would reference that assembly and use it directly. This avoids duplication. And this is what I do when I can.

But in this scenario, we have 3 different class libraries all with different targets. In this case, we may not be able to reference one assembly from another. This example is a bit contrived, so let's explore something a little more realistic.

Transitioning to .NET Core
We are in a bit of a transition period with .NET. As of today, .NET Core 3.0 is still coming (it will be released on September). With .NET Core 3.0, we also get .NET Standard 2.1.

But most people have a lot of applications that are .NET Framework. If I have a .NET Framework 4.7.2 project, I will not be able to use a .NET Standard 2.1 library (only .NET Standard 2.0). While I'm moving my projects from .NET Framework 4.7.2 to .NET Core 3.0, I may want to set up some interim projects to help with the transition.

So I could create a .NET Standard 2.1 class library with the intention that this will be the library referenced by the .NET Core 3.0 project. And then I can create a .NET Standard 2.0 project (that links to the files in the .NET Standard 2.1 project) that I can use today with the .NET Framework project.

For anything that doesn't match up (for example, I might be using additional APIs in the .NET Standard 2.1 project -- like Task.IsCompletedSuccessfully), I can copy files and make custom shivs that will get me through the transition period.

Shared Libraries
Another place where linked files can come in handy is if we create shared libraries (or NuGet packages) that will be used in different environments. We may need code that is a bit different for each environment, but we can use linked files to share what we can.

WPF on .NET Core 3.0
Another place this is useful is when transitioning WPF from .NET Framework to .NET Core. Right now, the WPF designers in Visual Studio are not quite complete for .NET Core. (I'm hoping things will be awesome when it comes to release time.)

To help with the transition, we can have *both* a WPF .NET Framework project and a WPF .NET Core project. We can put our production forms (XAML) in the .NET Core project and link the files to the .NET Framework project.

Then we can use the XAML designer in the .NET Framework project and build/run the .NET Core project.

We only have one set of files to worry about.

Linking Files is Useful
Linking files is really useful. I used to do this quite a bit back in the Silverlight days (boy, I feel old now). Each class library would link to the same source files (sometimes with #if/#endif directives for conditional compilation).

When I got started experimenting with WPF on .NET Core 3.0, someone gave me the advice to link files to get the designers like mentioned above. That was great except that I completely forgot how to link files. So I dropped this here mainly for me to remember later.

Keep learning, and find somewhere to log the things you forget. Then you will have them available to you the next time.

Happy Coding!

Tuesday, April 2, 2019

Task.IsCompletedSuccessfully - YAY! (and hmmm)

So, apparently I missed something along the way. Task in C# has an "IsCompletedSuccessfully" property (in some environments). This has been around since .NET Core 2.0, and it is in the current 2.2 release (and the 3.0 preview).
The reason I love the idea of "IsCompletedSuccessfully" is that "IsCompleted" does not mean what you think it means.
But there are some caveats about which types of projects can use this property. More on that below.

The Problem of "IsCompleted"
I've been writing and speaking about Task and Await since 2015. It's one of those things that took me a while to understand and then became really useful to me.

One of the hurdles I had to deal with has to do with the "Is" properties on Task. These include "IsCanceled", "IsFaulted", and "IsCompleted".

The main problem with these properties is that they sound like they are mutually exclusive, meaning something will be *either* Canceled *or* Faulted *or* Completed. And that is not the case.

If we look at the documentation for "IsCompleted", we find the following remarks:

IsCompleted will return true when the task is in one of the three final states: RanToCompletion, Faulted, or Canceled.

So "IsCompleted" does not mean "completed successfully", it means "no longer running".

The usefulness of this property in Task continuations is limited. And because of the problems I had when I first came across it, I have talked about it in articles (Task Continuations), videos (Task and Await in C# - Part 3), and also in my live presentations (I'll Get Back to You: Task, Await, and Asynchronous Methods).

Note: the short version is that if we want to see if a Task completed successfully, we can use the Task.Status property (an enumeration) and look for "RanToCompletion".

With .NET Core 2.0 and later, we finally get the option that I've been waiting for.

The "IsCompletedSuccessfully" property has been around since .NET Core 2.0. Somehow I missed it. It wasn't really a surprise that I missed it because I was mainly using Task in WPF projects (.NET Framework) and .NET Standard 2.0 class libraries. This property is not available in those environments (more on that in a bit).

First, let's look at the documentation (Task.IsCompletedSuccessfully Property):

The first thing to note is that the documentation is currently incomplete. It just tells us that the property exists, but it doesn't say what it is for. Based on previous experience (and a little experimentation), we can deduce that this tells us whether the task ended up in the "RanToCompletion" status.

Also note that this applies to .NET Core only. It has been available since .NET Core 2.0, and it is available in 2.1, 2.2, and the 3.0 Preview.

So, Yay! that this is available, and hmmm... let's figure out where we can really use this.

.NET Core vs. .NET Standard
The first thing to consider is that there is a difference between .NET Core and .NET Standard. With .NET Standard, we can create libraries that will compile in a variety of environments. I have created lots of .NET Standard libraries that can be used with WPF applications (using .NET Framework) and WebAPI applications (using .NET Core).

This is great because .NET Standard gives us a standard set of APIs that can be used by any project that supports that version of .NET Standard.

I've been using .NET Standard 2.0. This means that my libraries will work with .NET Core 2.0 (and above) and .NET Framework 4.6.1 (but more realistically, they work with .NET Framework 4.7.2 -- but that's another story).

The bad news is that "IsCompletedSuccessfully" is *not* available in .NET Standard 2.0.

This means that for my current projects using .NET Standard 2.0 and WPF with .NET Framework 4.7.2, this property is not available.

If we do want to use "IsCompletedSuccessfully" in a class library, we also have the option of creating a .NET Core class library. This has the restriction that we can only call it from a .NET Core project, so it will not work with a .NET Framework project like our .NET Standard libraries do.

The good news is that "IsCompletedSuccessfully" *is* available in .NET Standard 2.1.

It's important to understand that .NET Standard 2.1 is not really available yet. This will come along with .NET Core 3.0 which is still in preview.

To make things more "interesting", .NET Standard 2.1 will not be supported by .NET Framework 4.8. Here's an excerpt from Microsoft: "Announcing .NET Standard 2.1".
"Given many of the API additions in .NET Standard 2.1 require runtime changes in order to be meaningful, .NET Framework 4.8 will remain on .NET Standard 2.0 rather than implement .NET Standard 2.1. .NET Core 3.0 as well as upcoming versions of Xamarin, Mono, and Unity will be updated to implement .NET Standard 2.1."
So, we will not be able to use this property with .NET Framework 4.8.

Can I Use "IsCompletedSuccessfully"?
  • .NET Core 3.0: YES
  • .NET Standard 2.1: YES
  • .NET Core 2.0+ : YES
  • .NET Standard 2.0: No
  • .NET Framework 4.7.2: No
  • .NET Framework 4.8: No

Moving to .NET Core 3.0
With all of the changes that are happening in the .NET ecosystem, it is becoming more and more clear that we should be moving to .NET Core. For 2 examples, (1) .NET Framework 4.8 will not support .NET Standard 2.1, and (2) several C# 8 features will not be implemented in .NET Framework 4.8.

.NET Core 3.0 supports WinForms and WPF applications. I've done a few experiments with the Preview of .NET Core 3.0, and it works pretty well for the WPF applications that I've converted.

If we start to move to .NET Core, then things get easier in a lot of areas. The project systems are more compatible, we get runtime enhancements, and we can use .NET Standard 2.1.

We are in bit of a transitional period here. .NET Core 3.0 is still to be released. And even though Visual Studio 2019 works well with the .NET Core 3.0 Preview, the tooling for WinForms and WPF does not exist yet. There are some ways that we can manage WPF projects today, and I'll show that in an upcoming article.

If you haven't done so already, take a look at .NET Core. This is the future of .NET.

Happy Coding!

Sunday, March 31, 2019

Getting .NET Core 3 Preview to Work in Visual Studio 2019

The steps listed below work for Visual Studio 2019 prior to 16.2.

In 16.2, the setting to "Use previews of the .NET Core SDK" has moved to the "Enviroment > Preview Features" section of the Options dialog:


Visual Studio 2019 just released (okay, it releases on Tuesday), and you're probably anxious to try out .NET Core 3.0 -- but that has *not* been released yet, and release date is TBD. But you can still download the .NET Core 3.0 Preview and use it in Visual Studio 2019.

But it is not available by default. Here's the short version: you have to enable it explicitly in Visual Studio.

From the "Tools" menu, select "Options", then navigate to "Projects and Solutions". Under there, you'll find a ".NET Core" node, and that has the setting we need.

Just check "Use previews of the .NET Core SDK", and you're good to go.

A big thanks to Brian Lagunas' video that showed me how to do this: [SOLVED] .NET Core 3 is not working with Visual Studio 2019.

Longer Version
So a couple weeks ago, I was excited to try out some .NET Core 3.0 (Preview) stuff to see how it works. Since we are really close to launch for Visual Studio 2019, I grabbed the latest version (which was the RC (release candidate)).

Visual Studio 2019 is available here:

I also installed the latest preview of .NET Core 3.0 SDK. That is available here:

Then I created a new .NET Core console application. The new project dialog does not have a way to select the .NET Core version, so I figured that I would just go to the Project Options and change it myself.

I was a bit dismayed to see that .NET Core 3.0 was not available in my target framework dropdown:

A quick search led me to Brian Lagunas' video (mentioned above).

So then all I needed to do was go to Tools -> Options -> Projects and Solutions -> .NET Core.

Then I just checked the option for "Use previews of the .NET Core SDK".

When I restarted Visual Studio 2019, I saw the option pop up in the project options:

And then I was working with .NET Core 3.0 (Preview).

Initial Impressions
My initial impressions of .NET Core 3.0 are good. This is especially important when we look at what's happening with the .NET ecosystem (more on that in later articles).

I was most curious about the WPF support. I've converted most of my sample applications to use .NET Standard for the libraries, and .NET Core for the services. But I still have some WPF projects which are .NET Framework.

I was able to convert several of my WPF projects from .NET Framework to .NET Core 3.0 (Preview), and they ran just fine. (In some ways, they run better because it solves the NuGet issue that I brought up previously.)

So I've got some more exploration to do. There is also a bit of untangling because some features are cross platform, some are not. Also, since .NET Standard 2.1 does not support .NET Framework, there are some things that we need to be aware of -- particularly since there is one specific feature that is in .NET Standard 2.1 that I'd like to start using as quickly as possible.

Happy Coding!

Monday, January 21, 2019

Weirdness with EF Core 2 (SQLite), .NET Standard, and .NET Framework

I've run into some issues referencing .NET Standard projects from a .NET Framework project -- specifically in regard to Entity Framework Core 2 and SQLite. I encountered this when moving some of my demo code to newer projects.

Here's the error that I get:

This is a missing DLL. I've managed to come up with 2 workarounds -- neither of which I like -- but they get the projects working.

Let's take a look at the project.

This article is technically *not* part of the More DI series of articles. However, the issue is present in the code described in that series. The code is available on GitHub:

The Projects
The solution consists of 12 projects which are a mix of project types. Here are the full-framework projects:

The PeopleViewer project is a WPF application using .NET Framework 4.7.2.

The test projects are also .NET Framework due to current requirements of NUnit 3.x.

All of the "library" projects are .NET Standard 2.0:

The .NET Standard projects include the Presentation (which includes the view model), the Readers (including the CSV data reader, the SQL data reader, the Service data reader, and all of the decorators), and the Shared projects.

There is one .NET Core project:

This is the WebAPI service that provides data for the Service data reader.

Project References
Project references can get a bit weird when using .NET Standard with .NET Framework. When I was first having issues, I came across Scott Hanselman's article: Referencing .NET Standard Assemblies from both .NET Core and .NET Framework, and this helped. Let's see how.

Here are the dependencies from the "PersonReader.SQL" project (listed in the PersonReader.SQL.csproj file):

We can see the references to Entity Framework Core and SQLite.

The "PeopleViewer" project does not reference either of these NuGet packages directly:

Based on the advice provided in Scott Hanselman's article, I manually edited the "PeopleViewer.csproj" file to add a "RestoreProjectStyle" value of "PackageReference":

This took care of some of my issues: the ServiceReader works. But the SQLReader is still broken.

The Service Data Reader Works
The Service data reader project has a reference to NewtonSoft.Json (which is probably not a surprise):

By using the "PackageReference" setting, the correct version of the Newtonsoft.Json DLLs made it to the output folder of the "PeopleViewer" project.

You can look at the article "Adding Retry with the Decorator Pattern" for more information on running this code.

Be sure to read Scott Hanselman's article for the details on this setting and why it's needed.

The SQL Data Reader is Broken
But even with this setting, the SQL Data Reader is still broken. Let's take a closer look.

First, we'll update the "ComposeObjects" method to use the SQLReader object. This is in the "PeopleViewer" project, App.xaml.cs file -- although the code in the GitHub project is more complex than what we have here since it uses all of the decorators.

This builds a MainWindow that uses a PeopleReaderViewModel that uses a SQLReader.

When we run the application and click the "Refresh People" button, we get the error:

This indicates that we're missing a DLL that's needed for SQLite functionality.

If we check the output folder, we see that we have many Entity Framework DLLs, including one that references SQLite:

But "e_sqlite3.dll" is nowhere to be found.

As a side note, I tried this with the most recent version of EF Core/SQLite (version 2.2.1 at the time of writing) and had the same results.

I managed to work around this in 2 different ways. If there's a better solution, *PLEASE* let me know about it in the comments. I do not like either of these options.

Workaround #1: Include the NuGet Package in the .NET Framework Project
The first workaround I found was to include the Entity Framework Core / SQLite NuGet packages in the "PeopleViewer" project (the WPF application).

This works because the application now has all of the DLLs that it needs:

The "e_sqlite3.dll" is in the "x64" folder (and the "x86" folder as well).

Why I Don't Like This
I don't like this solution because it creates a dependency on a NuGet package that the .NET Framework project (the WPF application) does not explicitly need. The .NET Standard library (the SQL data reader) is the project that needs the package in order to work. This solution seems to mess up the dependency graph.

It can get a bit worse. If the version of the NuGet package referenced by the .NET Framework project is different from the version referenced in the .NET Standard project, there will be runtime errors. (I've run into this with Newtonsoft.Json references.)

Workaround #2: Copying the Needed Files to the Output Folder
The second workaround I came up with is to copy the missing files into the output folder. Since the bulk of the DLLs are making it to the output folder, I figured that just including the "e_sqlite3.dll" that's missing might be the best way to go.

To do this, I grabbed the "x64" and "x86" folders above, and put them into an "AdditionalFiles" folder at the root of the solution.

This folder already exists: it has the text file used by the CSV data reader (People.txt) as well as the SQLite database used by the SQL data reader (People.db). So adding a couple more files doesn't seem like too much of a problem.

The files are copied to the output folder using a post-build event on the "PeopleViewer" project:

This copies the contents of the "AdditionalFiles" folder to the output folder. For more information on Visual Studio build events, refer to "Using Build Events in Visual Studio to Make Life Easier".

The other thing I had to do was explicitly add these files to the Git repository. The .gitignore file doesn't usually include DLLs, so I had to add these two files manually. You can see them in the GitHub repository:

This has the same effect as Workaround #1: the files are added to the output folder:

Why I Don't Like This
The reason I don't like this workaround is the same as above: There's a good chance that the versions will get out of sync in the future. In addition, I don't like the idea of adding executable files to the Git repository.

There is a bug somewhere. I'm not sure if it's a bug in the Entity Framework Core packages (specifically the SQLite package). I'm not sure if it's a conflict between the package management schemes in the .NET Standard project vs. the .NET Framework project. And I have to admit that this is a pretty big frustration for me.

The frustration is 2-fold -- first for myself. I've been coding in the .NET framework for close to 15 years. I really would not expect to have these types of difficulties, even with things that are relatively new. But as a developer, I deal with this type of issue all the time.

The bigger frustration is when I think about other developers. Many developers in the .NET space do not have the type of experience that I have. If I am having difficulty, then that means that there are a large number of other developers who will also have difficulty.

This is even a bigger concern since the bulk of my effort is spent helping developers learn more about C# and .NET -- in particular, I work with the "dark matter developers" described by Scott Hanselman . I have a fear that if we make things too complex in the environment, we will have difficulty getting new developers on board.

This is just one example of what is causing me concerns. The other items are a topic for another day.

Wrap Up
If you have any advice on handling this issue, please let me know. I would file a bug report, but as noted, I'm not exactly sure where the bug is.

If you have run into this same issue, understand that I do not like either of these workarounds. They add concerns about versioning. But they do get the code working, and that's always the first step.

Happy Coding!