Realm 不适用于 xUnite 和 .net core

Realm doesn’t work with xUnite and .net core

我在使用 xUnite 和 Net Core 时遇到 运行ning 领域的问题。这是我想要的一个非常简单的测试 运行

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)上尝试使用 InMemoryConfiguration 创建 Realm 实例时遇到不同的异常。 在 Mac 我得到以下异常

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

在 Windows 运行ning

时出现以下异常
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,也没用。

this Github post

中找到了此问题的解决方案

那段代码帮助我解决了问题

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。因为否则自动更改将不会通过线程传播,因为领域需要非空 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;
        }
    }

此外,这修复了许多失败的单元测试。但我仍然收到同样的异常 - 从不正确的线程访问的领域。而且我不知道为什么,因为一切都设置正确。然后我发现失败的测试与我使用异步领域 api,特别是 realm.WriteAsync 的方法有关。经过更多挖掘后,我在领域文档中发现了以下几行。

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.

在我的代码中,没有直接需要使用异步 API。我删除并替换为 sync Write ,所有测试再次变为绿色!我想如果我发现自己因为某种批量插入而确实需要使用异步 API,我要么模拟那个特定的 API,要么用我自己的后台线程替换使用 Task.Run 而不是使用 Realm 的版本。