#.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.