Send / Receive -> Could not load assembly 'System.Private.CoreLib'
See original GitHub issueHi,
I use EasyNetQ 6.3.1.
When sending string value from asp .net core to .net console
Sender
var sender = RabbitHutch.CreateBus(_connectionString);
await Sender.SendReceive.SendAsync("ConsoleChat", message );
Receiver
receiver = RabbitHutch.CreateBus("host=localhost");
receiver.SendReceive.Receive("ConsoleChat", x => x.Add<string>(e => { Console.WriteLine($"Message: {e}"); }));
I got error on EasyNetQ_Default_Error_Queue:
“Exchange”:“”,“Queue”:“ConsoleChat”,“Exception”:"EasyNetQ.EasyNetQException: Could not load assembly ‘System.Private.CoreLib’\r\n w EasyNetQ.DefaultTypeNameSerializer.GetTypeFromTypeNameKey(TypeNameKey typeNameKey)
it looks like serialization issue…
Issue Analytics
- State:
- Created 2 years ago
- Comments:20 (3 by maintainers)
Top Results From Across the Web
'Could not load file or assembly 'System.Private.CoreLib ...
1 using .NET framework. I ran my code today after it works fine yesterday, but today I received this exception: System.IO.FileNotFoundException ...
Read more >Could not load file or assembly 'System.Private.CoreLib ...
An unhandled exception of type 'System.IO.FileNotFoundException' occurred in Unknown Module. Could not load file or assembly 'System.Private.
Read more >Could not load file or assembly 'System.Private.CoreLib
The system cannot find the file specified. This error occurs because Microsoft Store no longer accepts packages with mixed UWP and Desktop .NET ......
Read more >How to solve "Could not load System.Private.CoreLib"
NET Core app, send it over to another system which uses .NET Framework 4.x and then I realize that objects can't be deserialized...
Read more >Could not load type 'System.Object' from assembly ...
dll version 6.0.0.0 inside the gac a different error is coming, saying that Could not load type 'System.Object' from assembly 'System.Private.
Read more >Top Related Medium Post
No results found
Top Related StackOverflow Question
No results found
Troubleshoot Live Code
Lightrun enables developers to add logs, metrics and snapshots to live code - no restarts or redeploys required.
Start FreeTop Related Reddit Thread
No results found
Top Related Hackernoon Post
No results found
Top Related Tweet
No results found
Top Related Dev.to Post
No results found
Top Related Hashnode Post
No results found
Top GitHub Comments
EasyNetQ is pretty clear that it is an ‘Opinionated Wrapper’ over RabbitMq’s client libraries. Part of this ‘opinionated’ aspect is that the publisher and consumer would share the same opinions of how the messages in RabbitMq - both data and headers - should be formatted. Do not forget that this opinionated nature includes the error queue message and headers, queue naming, and more (though you are overriding the queue naming in your example).
The implication of the above is that you need to fully share the assembly, and types, of what is being published between publisher and consumer, and that these types are, in fact, able to be properly serialized and deserialized. Using this library in a way where the messages are either produced or consumed by a non EasyNetQ component is not one of the core scenarios EasyNetQ is intended to enable. It IS possible, but not easy. I mention this because of your mentioning ‘cross platform’.
There is a side benefit to publishing your own type (rather than the string) in that you can evolve the type being published to add additional fields if you are careful that on the deserialization/consumer side you can handle messages of the older format. This feature is impossible if you simply publish a serialized string.
If you absolutely MUST publish a string like your sample, or cannot share assemblies/types of the messages being published you are outside of the parts that EasyNetQ makes ‘Easy’. The issue you raised is one of a leaky abstraction in that the type you are publishing is a type that cannot be created using the default components of the library. This is one that is difficult, if not impossible, for EasyNetQ to fix. The underlying issue is that as part of the ‘opinionated’ nature, the type being published is serialized into the RabbitMQ message header
Type
- in your case it is serializingtypeof(string)
asSystem.String, System.Private.CoreLib
which cannot be created on the consumer side using the defaultDefaultTypeNameSerializer
implementation. I’d suggest that you replaceITypeNameSerializer
in your consumer, by forking the EasyNetQ source forDefaultTypeNameSerializer
with your own changes. There are two possible changes:DeSerialize
using a solution similar to what you can see here. To be honest, I intended to create a PR long time ago to make some of the methods in the Default and Legacy TypeNameSerializer classes virtual, so one could override the method, replace the type name, and call to base for situations like this.DefaultTypeNameSerializer
code to pre-populate thedeserializedTypes
collection, and in the ctor (or elsewhere) callRegisterReplacementType("System.String, System.Private.CoreLib", typeof(string));
Other Thoughts
If it is important for you to have the capability of cross-framework (in other words a publisher or consumer that is NOT EasyNetQ), I would suggest you abandon EasyNetQ. Sure, you can replace the default components, such as
IMessageSerializationStrategy
andITypeNameSerializer
, but at what point is it that you cross a threshold where replacing the bits of EasyNetQ (and suffering through breaking changes in EasyNetQ as time progresses) where the costs of customization outweigh the benefits of using an opinionated wrapper where the opinions are so divergent?I use EasyNetQ, replacing some bits, such as
Conventions
andITypeNameSerializer
that enabled me to retain the name of the queue and process messages where the assembly and class name had changed across versions (where queued messages of the ‘old’ would still need to be processed using the ‘new’). On the consumer side, I replace theIConsumerErrorStrategy
. And I rely on the mechanism of the error queue with an inbuilt hosepipe like mechanism to retry processing of failed messages. For the most part, the opinions of EasyNetQ match mine.In other cases, I skip EasyNetQ altogether and use the RabbitMq library directly. In my case, I have an RPC scenario, combined with Priority Queues, where I need to handle timeouts on caller and callee in specific manners, as well as making sure in the event of a connection burp the caller’s initially randomly named return message queue is properly recreated with the same name so if a reply is being worked on after the burp it can still be delivered. Sure, I could replace some of the default EasyNetQ bits, but then it becomes a constant battle between the (forgive the overuse of the word) opinionated nature of both EasyNetQ and RabbitMq APIs against my own business requirements and the API that I wish to expose to the internal consumers where I could replace RabbitMq with other options in the future if needed/desired.
I could create (and have created) pull requests for both libraries to make them better equipped for my use cases, or to resolve blocking bugs. In these cases you are also subservient to the release cycles of both libraries, and it is rather difficult to find and fix bugs going through so many layers.
At a certain point you need to realize when the opinions of you and your libraries diverge enough that you need to abandon one of these opinions in order to develop an easy to use, understand, and maintain piece of software.
Thanks for this @StevenBonePgh 🔥
@robertok77 Feel free to raise a PR for changing
DefaultTypeNameSerializer
in any mentioned way.PS I hope we also fine to have hardcoded system types translation inside
DefaultTypeNameSerializer
.