Realm не работает с xUnite и .net core

#.net-core #realm #xunit

#.net-core #realm #xunit

Вопрос:

У меня возникли проблемы с запуском realm с xUnite и Net core. Вот очень простой тест, который я хочу запустить

 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());
        }
    }
  

Я получаю разные исключения на разных компьютерах (Windows и Mac) в строке, где я пытаюсь создать область instace с помощью InMemoryConfiguration.
На Mac я получаю следующее исключение

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

В Windows я получаю следующее исключение при запуске

 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
  

Я использую Realm 3.3.0 и xUnit 2.4.1
Я пробовал понизить версию до Realm 2.2.0, и это тоже не сработало.

Ответ №1:

Решение этой проблемы было найдено в этом посте на Github

Фрагмент кода из этого помог мне решить проблему

 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;
}
  

Хотя мне потребовалось некоторое время, чтобы применить это к моему решению.
Прежде всего, вместо того, чтобы просто устанавливать контекст на null , я использую Nito.AsyncEx.AsyncContext. Потому что в противном случае автоматические изменения не будут распространяться через потоки, поскольку для работы этой функции realm требуется значение, отличное от null SynchronizationContext . Итак, в моем случае метод выглядит примерно так

 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;
        }
    }
  

Кроме того, это исправило множество неудачных модульных тестов. Но я все еще получал то же самое исключение — область, доступ к которой осуществлялся из неправильного потока. И я понятия не имел, почему, потому что все было настроено правильно. Затем я обнаружил, что неудавшиеся тесты были связаны, в частности, с методами, в которых я использовал async realm api realm.WriteAsync . После еще нескольких поисков я нашел следующие строки в документации realm.

Это не проблема, если вы установили SynchronisationContext.Current но это приведет к повторной отправке WriteAsync в пул потоков, что может создать другой рабочий поток. Итак, если вы используете Current в своих потоках, рассмотрите возможность вызова просто Write вместо WriteAsync .

В моем коде не было прямой необходимости использовать async API. Я удалил и заменил на sync Write , и все тесты снова стали зелеными! Я думаю, что если я окажусь в ситуации, когда мне действительно нужно использовать async API из-за каких-то массовых вставок, я бы либо смоделировал этот конкретный API, либо заменил своим собственным фоновым потоком, используя Task.Run вместо версии Realm.