Search code examples
.net-corerealmxunit

Realm doesn’t work with xUnite and .net core


I’m having issues running realm with xUnite and Net core. Here is a very simple test that I want to run

public class UnitTest1
    {
        [Scenario]
        public void Test1()
        {
            var realm = Realm.GetInstance(new InMemoryConfiguration("Test123"));
            realm.Write(() =>
                        {
                            realm.Add(new Product());
                        });
            var test = realm.All<Product>().First();
            realm.Write(() => realm.RemoveAll());
        }
    }

I get different exceptions on different machines (Windows & Mac) on line where I try to create a Realm instace with InMemoryConfiguration. On Mac I get the following exception

libc++abi.dylib: terminating with uncaught exception of type realm::IncorrectThreadException: Realm accessed from incorrect thread.

On Windows I get the following exception when running

ERROR Unable to read data from the transport connection: An existing connection was forcibly closed by the remote host. at 
System.Net.Sockets.NetworkStream.Read(Span1 destination) at 
System.Net.Sockets.NetworkStream.ReadByte() at 
System.IO.BinaryReader.ReadByte() at 
System.IO.BinaryReader.Read7BitEncodedInt() at 
System.IO.BinaryReader.ReadString() at 
Microsoft.VisualStudio.TestPlatform.CommunicationUtilities.LengthPrefixCommunicationChannel.NotifyDataAvailable() at 
Microsoft.VisualStudio.TestPlatform.CommunicationUtilities.TcpClientExtensions.MessageLoopAsync(TcpClient client, ICommunicationChannel channel, Action1 errorHandler, CancellationToken cancellationToken) Source: System.Net.Sockets HResult: -2146232800 Inner Exception: An existing connection was forcibly closed by the remote host HResult: -2147467259

I’m using Realm 3.3.0 and xUnit 2.4.1 I’ve tried downgrading to Realm 2.2.0, and it didn’t work either.


Solution

  • The solution to this problem was found in this Github post

    The piece of code from that helped me to solve the issue

    Realm GetInstanceWithoutCapturingContext(RealmConfiguration config)
    {
        var context = SynchronizationContext.Current;
        SynchronizationContext.SetSynchronizationContext(null);
    
        Realm realm = null;
        try
        {
            realm = Realm.GetInstance(config);
        }
        finally
        {
            SynchronizationContext.SetSynchronizationContext(context);
        }
    
        return realm;
    }
    

    Though it took a while for me to apply this to my solution. First and foremost, instead of just setting the context to null I am using Nito.AsyncEx.AsyncContext. Because otherwise automatic changes will not be propagated through threads, as realm needs a non-null SynchronizationContext for that feature to work. So, in my case the method looks something like this

    public class MockRealmFactory : IRealmFactory
        {
            private readonly SynchronizationContext _synchronizationContext;
            private readonly string _defaultDatabaseId;
    
            public MockRealmFactory()
            {
                _synchronizationContext = new AsyncContext().SynchronizationContext;
                _defaultDatabaseId = Guid.NewGuid().ToString();
            }
    
            public Realm GetRealmWithPath(string realmDbPath)
            {
                var context = SynchronizationContext.Current;
                SynchronizationContext.SetSynchronizationContext(_synchronizationContext);
    
                Realm realm;
                try
                {
                    realm = Realm.GetInstance(new InMemoryConfiguration(realmDbPath));
                }
                finally
                {
                    SynchronizationContext.SetSynchronizationContext(context);
                }
    
                return realm;
            }
        }
    

    Further, this fixed a lot of failing unit tests. But I was still receiving that same exception - Realm accessed from incorrect thread. And I had no clue why, cause everything was set correctly. Then I found that the tests that were failing were related to methods where I was using async realm api, in particular realm.WriteAsync. After some more digging I found the following lines in the realm documentation.

    It is not a problem if you have set SynchronisationContext.Current but it will cause WriteAsync to dispatch again on the thread pool, which may create another worker thread. So, if you are using Current in your threads, consider calling just Write instead of WriteAsync.

    In my code there was no direct need of using the async API. I removed and replaced with sync Write and all the tests became green again! I guess if I find myself in a situation that I do need to use the async API because of some kind of bulk insertions, I'd either mock that specific API, or replace with my own background thread using Task.Run instead of using Realm's version.