Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Cannot use "new" library in .net 4.8 #4735

Closed
Danielku15 opened this issue Oct 7, 2021 · 4 comments
Closed

Cannot use "new" library in .net 4.8 #4735

Danielku15 opened this issue Oct 7, 2021 · 4 comments
Assignees
Labels
customer assistance Help customers with questions regarding usage of WCF features.

Comments

@Danielku15
Copy link

Describe the bug
Currently the NuGet libraries provided by this project for .net 4.8 are forwarding the implementation to the System.ServiceModel coming with .net 4.8 itself. This makes migration really hard because there is no good way to already start using dotnet/wcf + CoreWCF already.

To Reproduce
Steps to reproduce the behavior:

  1. Setup a .net 4.8 solution with a server and client project inside.
  2. Use CoreWCF for the server and the NuGet System.ServiceModel.* for the client
  3. Notice that the NuGet Packages are actually causing a reference to the .net 4.8 built-in System.ServiceModel
  4. Struggle migrating your code to the next-gen libraries to upgrade to .net core as a next step.

Expected behavior
We need a way to enforce usage of the new client-side-only libraries through NuGet without pulling the full-framework WCF including the server parts.

Screenshots

Additional context
We cannot yet change our project to actually be .net5.0 or .net6.0 due to a bigger scale rework ongoing which we need to do gradually across all our company wide products and libraries. We would like to first migrate to the new CoreWCF for Server and System.ServiceModel NuGets for client but stay on .net4.8.

Even if we make a .netstandard2.0 library project and consume the nugets and then use it in our main .net 4.8 application, the main application still pulls/references the .net 4.8 WCF libs.

NuGet does not provide a proper way to force usage of .netstandard2.0 libs for individual packages. There are only hacks where you need to spoil your whole solution tree with additional custom references. Therefore my hope lies here on the package authors side to provide a way to consume the new packages and provide a migration path away from the full framework WCF.

@HongGit HongGit added the customer assistance Help customers with questions regarding usage of WCF features. label Oct 13, 2021
@mconnew
Copy link
Member

mconnew commented Oct 15, 2021

This is by design. When you are writing code against .NET Core/.NET, you get more API's available (we have some async Task based api's only available on Core), when targetting netstandard, you get the api's which are available on all supported platforms, so .NET Core/.NET and .NET Framework. On .NET Framework you get the api's that are available there, which is all of them.
It looks like you've got 2 issues. One is limiting the set of api's the compiler will compile against so you don't accidentally add usage for missing api's. And the second is you'd like to actually consume the Core version of the implementation on .NET Framework.
For the first problem, I have a couple of ideas. The first one is semi-hacky though. You can detect in msbuild that you are running in the context of Visual Studio. In that case you can modify the set of reference assemblies to remove System.ServiceModel.dll and add the ones in the ref folder from the package. This way command line/automated builds aren't using any hacks, but in VS you will get build errors if you are using any API's unavailable in Core.
My second idea is using the API porting tools to scan your code and flag any api's you are using which aren't available in Core. I believe you can pass parameters to restrict target platforms and namespaces etc to only get the list of WCF ones. Once you have your codebase only using supported API's, you could make it a build error if new unsupported usages get added in at a later date. I'm sorry I don't have a better answer for you for the first problem.

The second issue is a bit more interesting. I believe it would work, but you would need to pull apart the packages and either directly reference the various pieces or build your own. I think simply removing the net461 (might be net462, I forget which version it is, but it will be obvious) in all the folders should be sufficient.

As for creating a netstandard2.0 separate library, the way classic .NET Framework projects handle transitive dependencies is different than dotnet SDK projects. Can you try experimenting with using an SDK style project targeting .NET Framework (e.g. TargetFramework is net472) with a project reference to the netstandard2.0 SDK style project? I would expect the netstandard2.0 library to correctly reference the nuget reference assembly. It will still use .NET Framework at runtime though, but compilation should reference the correct reference assemblies in this case.

@Danielku15
Copy link
Author

This is by design. When you are writing code against .NET Core/.NET, you get more API's available (we have some async Task based api's only available on Core), when targetting netstandard, you get the api's which are available on all supported platforms, so .NET Core/.NET and .NET Framework. On .NET Framework you get the api's that are available there, which is all of them.

While I can understand the mentality behind this statement, it somehow feels still strange to me. I don't expect that my request will likely change much there but I still would like to share my thoughts on this topic. Maybe it triggers some interesting discussion or I might learn some stuff 😁 😉

The .net framework in this list does somehow not fit into the "line" of the other two ones (.net core and .net standard): The .net framework version of WCF is not maintained within this repository while the other two are. If all 3 versions of the library would be built from this codebase and different sets of features would be exposed this statement makes more sense. But the fact that the NuGet packages of this new library are only redirects to a "different" library, the packages do not add much value and in parallel prevent anyone targeting still .net framework from really using the code developed here. 🤔

I might be a bit distant to this approach, but I also don't see much value in this practice: Why reference a default .net library (available through <Reference />) through the overhead of NuGet (through <PackageReference />) just to again result in a reference to the .net library? Especially as the System.ServiceModel.* NuGets (and this repo) are aiming for a client only library. Having the NuGet System.ServiceModel packages suddenly providing "Server" components feels weird and even in conflict with the goals of this repository to not provide Server Side components. There might be a bit a benefit for other package authors multi-targeting the classical WCF and new client only WCF because they only need to maintain one dependency entry. Beside that I'm skeptical.

This library shares a common conceptual root with the client part of the classical WCF but from what I've seen. the differences are actually quite significant. A change in your target framework actually introduces breaking changes even though you reference the same version of the System.ServiceModel packages. While this can happen often if language features are not available on the other target framework it is even a larger scale API difference here. There are APIs in this library, which don't exist in the classical WCF, and vice versa. It's not like that if you are targeting newer frameworks, you are getting more features, but it's the case that you are using "something else" in the older frameworks. And even net48, providing the APIs of netstandard2.0, is consuming a completely different library. I guess it is even quite risky to accidentally introduce breaking changes within the netstandard version by exposing APIs which do not exist in net4.8. This would then cause runtime issues on if a netstandard library would call these APIs. I bet you have this under control in this project, but it just feels risky and could even be an innovation blocker on this repo by requiring you to stay fully backwards compatible to the old API.

Bringing also CoreWCF onto the stage for discussion, it gets even more complicated because CoreWCF does not forward to the classical WCF. It follows a different mentality of providing a migration path to the future. "Drop the old default .net framework libs and integrate the new ones and then you're set for a change in your target framework" - so to say 😁 I would have loved to see there a consistent approach.

For the first problem, I have a couple of ideas. The first one is semi-hacky though. You can detect in msbuild that you are running in the context of Visual Studio. In that case you can modify the set of reference assemblies to remove System.ServiceModel.dll and add the ones in the ref folder from the package. ...

I gave this one a try using the GeneratePathProperty (as linked above) but as you mention: it is really hacky and prone to errors. It is nothing we can establish well in our corporate environment. I fiddled a bit with Directory.Build.targets to do the reference rewrite on global scale but due to it's hacky nature and risky approach I didn't want to follow this path any further. In our case this problem spans across many repositories covering multiple products.

The second issue is a bit more interesting. I believe it would work,...

This is more what I have thought about as temporary workaround for our migration path. We are operating company internal NuGet repositories and it might be easier to re-package the assemblies from NuGet.org only including .net core (and above) and .net standard libs. With a regular CI/CD trigger checking for changes on NuGet it should be fairly easy to stay up to date.

Nevertheless I preferred the path of reaching out to the package authors to address this issue at the root instead of establishing custom workarounds. Still, this path seems to be a possible workaround for us.

As for creating a netstandard2.0 separate library, the way classic .NET Framework projects handle transitive dependencies is different than dotnet SDK projects. Can you try experimenting with using an SDK style project targeting .NET Framework (e.g. TargetFramework is net472) with a project reference to the netstandard2.0 SDK style project?..

We are already using SDK style csprojs for all our projects and we are only using <PackageReference /> tags. In this environment I referenced from a netstandard2.0 project the System.ServiceModel NuGet packages. I guess the library might have used the netstandard reference libs during compilation, but within the net48 main executable, we ended up with the System.ServiceModel assemblies from the .net Framework and things like ServiceHost where visible. But actually we want to get rid of all the "old" WCF components and migrate to CoreWCF (for server side) and the dotnet/wcf (for client side) without yet changing from net48 to net5/6.

This is bit a cascading issue in our case. We're aiming for a stepwise migration replacing our legacy components in an iterative approach. Some components hold us back to net48 until we migrated away from all, but to migrate away, components like dotnet/wcf expect us already to change to .net 5/6 to really migrate. "chicken-egg-problem" 😅

@mconnew
Copy link
Member

mconnew commented Apr 20, 2022

Could you split the client code into a separate project which targets .NET Standard? It might be possible to run integration tests using it on .NET to validate the features you need are there.
The only real concern as far as I can see is if there's a different behavior in the .NET Core implementation than in the .NET Framework implementation. These fall into 2 camps. First, a feature being absent and throwing. This can be mitigated with the separate project approach I mentioned earlier. The second is behavior differences which aren't immediately obvious. Being able to run the .NET Core version of the WCF client implementation on .NET Framework wouldn't completely solve this though. For example, we switched to using HttpClient in .NET Core. Running our packages on .NET Framework would mean we're using the .NET Framework version of HttpClient which wraps HttpWebRequest. Some of the bigger issues we've seen people hit are differences in implementation between HttpWebRequest on .NET Framework and SocketsHttpHandler on .NET Core. You still wouldn't be exposed to those differences if you ran the WCF Core client on .NET Framework. There are other subtle differences I've discovered, such as closing a Socket abandons pending writes on .NET Core, but on .NET Framework it waits for those writes before closing the socket. I have done everything I can to work around the behavior differences between the two platforms so generally you won't be exposed to those differences.

One piece of advice I will give which you might not hit until too late is to go 100% async with WCF. We removed the sync implementation in the WCF Core client internally so the sync api's are all sync over async and can cause you scalability problems with extra threads. This isn't a problem with something like a desktop UI app, but for a middleware scenario, it can have an impact. Also explicitly open your channels before using them, and if using anything other than BasicHttpBinding or NetHttpBinding, open it async.

As you can see, even if we made it so you could use the .NET Core implementation on .NET Framework, you still won't see the same behavior as you would on .NET Core as our dependencies would have different behaviors. It's not an ideal situation, but it's the best of a set of possible solutions each with their own consequences. I try everything I can to get consistent behavior as far as possible though.

@HongGit
Copy link
Contributor

HongGit commented Jul 12, 2023

Close this as no new response. Feel free to re-activate if that is not the case.

@HongGit HongGit closed this as completed Jul 12, 2023
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
customer assistance Help customers with questions regarding usage of WCF features.
Projects
None yet
Development

No branches or pull requests

3 participants