realm-dotnet
realm-dotnet copied to clipboard
UWP: The view is not in sync with the database.
Goals
Hello. I'm getting started with Realm platform to make an application in sync.
The problem is that the data in my view does not become synced with db and I need to fetch it again.
Expected Results
I want to add a data to database and I want the data list to be in sync with DB.
I bonded the ListView to an IRealmCollection
.
Actual Results
When I add the data in the db using
var todo = new Todo()
{
Id = DateTime.UtcNow.Ticks.ToString(),
Subject = DateTime.Now.ToString(),
Detail = Details.Text,
Status = 2
};
RealmContext.Instance.Write(() =>
{
RealmContext.Instance.Add(todo);
});
it saves successfully but the point is that UI does not update itself with newly added data; so I need to fetch the data again with RealmContext.Instance.All<Todo>().AsRealmCollection();
which is not what I want. When I subscribe to changes via
TodayList.CollectionChanged += (s, e) =>
{
foreach (var item in TodayList)
{
Debug.WriteLine(item.Subject);
}
};
the fun fact is that it is not triggered when I insert my first data into the db. it triggers second time. Even more fun is this, the VS debug output shows all items that I've added except the one that it didn't trigger this event !!! Please help.
Code Sample
You can take a look at the project that I'm working on at HERE
In the core
project there are everything related to the Database.
Also TimelineViewModel.cs
file is related to the issue.
Views>Add>Task.xaml is for adding an item to DB.
Version of Realm and Tooling
- Realm Object Server Version: Last version. On the cloud
- Client SDK Version: Windows 10 SDK 17134
- Client OS & Version: 3.1.0
To implify things, I have made another test project that you can use to duplicate the issue in your environment: MainPage.xaml
<Page
x:Class="ReactiveUI.MainPage"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="using:ReactiveUI"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
mc:Ignorable="d"
Background="{ThemeResource ApplicationPageBackgroundThemeBrush}">
<StackPanel>
<Button Content="Login" Width="400" Height="40" Click="Button_Click_1" />
<Button Content="Initialize" Width="400" Height="40" Click="Button_Click_2" />
<Button Content="Add" Width="400" Height="40" Click="Button_Click" />
<ListView x:Name="itemListView" ItemsSource="{x:Bind TodayList}"/>
</StackPanel>
</Page>
MainPage.xaml.cs
using Realms;
using Realms.Sync;
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.IO;
using System.Linq;
using System.Runtime.InteropServices.WindowsRuntime;
using Windows.Foundation;
using Windows.Foundation.Collections;
using Windows.UI.Xaml;
using Windows.UI.Xaml.Controls;
using Windows.UI.Xaml.Controls.Primitives;
using Windows.UI.Xaml.Data;
using Windows.UI.Xaml.Input;
using Windows.UI.Xaml.Media;
using Windows.UI.Xaml.Navigation;
// The Blank Page item template is documented at https://go.microsoft.com/fwlink/?LinkId=402352&clcid=0x409
namespace ReactiveUI
{
/// <summary>
/// An empty page that can be used on its own or navigated to within a Frame.
/// </summary>
public sealed partial class MainPage : Page
{
public Realm Instance;
public MainPage()
{
this.InitializeComponent();
}
public IRealmCollection<Todo> TodayList { get; set; }
protected override void OnNavigatedTo(NavigationEventArgs e)
{
base.OnNavigatedTo(e);
var configuration = new FullSyncConfiguration(new Uri("~/myRealm", UriKind.Relative));
Instance = Realm.GetInstance(configuration);
TodayList = Instance.All<Todo>().AsRealmCollection();
}
private void Button_Click(object sender, RoutedEventArgs e)
{
var todo = new Todo()
{
Id = DateTime.UtcNow.Ticks.ToString(),
Subject = DateTime.Now.ToString(),
Status = 2
};
Instance.Write(() =>
{
Instance.Add(todo);
});
}
private async void Button_Click_1(object sender, RoutedEventArgs e)
{
var credentials = Credentials.UsernamePassword("mohsens22", "test", createUser: false);
var user = await User.LoginAsync(credentials, new Uri("https://denna.de1a.cloud.realm.io/"));
User.ConfigurePersistence(UserPersistenceMode.Encrypted);
}
private void Button_Click_2(object sender, RoutedEventArgs e)
{
var configuration = new FullSyncConfiguration(new Uri("~/myRealm", UriKind.Relative));
Instance = Realm.GetInstance(configuration);
}
}
public class Todo : RealmObject
{
[PrimaryKey]
public string Id { get; set; }
public string Subject { get; set; }
public string Detail { get; set; }
public int Notify { get; set; }
public int Status { get; set; }
public DateTimeOffset StartTime { get; set; }
public DateTimeOffset EndTime { get; set; }
public int Imprtance { get; set; }
}
}
Note that the UI is not reactive and it's not in sync with the database.
This is somewhat unfortunate, but it's a decision that we made - the reason it doesn't automatically update listviews is that UWP needs the runtime representation of the collection to implement IList
. This is not a problem in of itself, but Xamarin.Android has a bug where if a collection implements IList
, the app freezes when deselecting items - https://github.com/realm/realm-dotnet/issues/1575.
So we had to choose to either support databinding in collections for UWP or Xamarin.Android and we chose the latter. Still, because the change was made with Realm 3.1.0, you can downgrade to 3.0.0 which should still work with UWP. Hope this helps.
Nicely works on realm 3.0 But my advice is that to fix this UWP thing. I can help you with that if you want.
Also tag this with bug.
It's an either-or situation - either we fix it and it works on UWP or we don't and it works on Xamarin.Android. Currently, we have many more users on Android which is why we decided to take that path.
I used to contribute on couchbase. They had similar issue. They decided to make support packages for each platform and inject platform-specific implementations to their code via "Dependency Injection" You could go that way or make a separate .UWP package inducing a hotfix.
Also there is another bug in 3.0 When I delete something, it just changes the order (but deletes in database) So I need to fetch database again to have them in correct order Fix em PLEASE
The order of items in the database is not fixed and deletes can rearrange that. For example, if you delete the item in position X, the last item in the table moves to its place to avoid fragmenting the storage. If you want your view to display them in particular order, you need to sort the table, e.g. TodayList = Instance.All<Todo>().OrderBy(o => o.Name)
.
And while compiling platform-specific packages is something we've considered in the past, the maturity of the tooling at the time prevented us from pursuing this avenue.
What's your plan for fixing these platform issues?
It's one issue with a simple workaround, that affects a small number of users, and is not part of the core functionality. That's why, it's not very high on our priority list and unless a paying customer requests a fix for it, I don't expect we'll look into it before Q1/2019.
I'm going to fix and make a Realm.UWP
package for now. But I await your fix for all platforms.
UPDATE: https://www.nuget.org/packages/Realm.UWP/3.1.0 https://github.com/Mohsens22/realm-dotnet
It's one issue with a simple workaround, that affects a small number of users, and is not part of the core functionality. That's why, it's not very high on our priority list and unless a paying customer requests a fix for it, I don't expect we'll look into it before Q1/2019.
Has it been completly abondoned as Idea?
Sorry for the late response - I think I have a workaround that doesn't crash the latest Xamarin.Forms.
Sorry for the late response - I think I have a workaround that doesn't crash the latest Xamarin.Forms.
So Items added to the list through Flex Sync, do not get automatically added to the List. If it's done locally, it updates. Removing does not update locally nor through flex sync. Editing works, locally and through flex sync.
This is my RealmObject
public sealed class PasswordGroup : RealmObject
{
[PrimaryKey]
[MapTo("_id")]
public ObjectId Id { get; set; } = ObjectId.GenerateNewId();
[MapTo("passwords")]
public IList<PasswordData> Passwords { get; }
[MapTo("ownerId")]
[Required]
public string OwnerId { get; set; }
}
And I currently applying it to the ListView like so
var binding = new Binding();
binding.Source = Database.PasswordGroup.Passwords.OrderBy(p => p.Website);
binding.Mode = BindingMode.OneWay;
PasswordListView.SetBinding(ListView.ItemsSourceProperty, binding);
I believe the issue is that you're applying an ordering to the Passwords
list, which snapshots the collection (i.e. it is no longer live). You can confirm that by trying to cast Database.PasswordGroup.Passwords.OrderBy(p => p.Website) as INotifyCollectionChanged
- I'm fairly certain that'll give you null
.
If you want to preserve the collection as a live collection emitting notifications, you need to construct a query and apply the ordering on top of that (note that you'll be limited by the sorting capabilities of the database, though a simple sort like that will work just fine). I haven't tested it, but I think something like this would work:
binding.Source = Database.PasswordGroup.Passwords.AsRealmQueryable().OrderBy(p => p.Website);
This will convert the list to a query and execute the sort at the database level, thus preserving change notification capabilities.
I believe the issue is that you're applying an ordering to the
Passwords
list, which snapshots the collection (i.e. it is no longer live). You can confirm that by trying to castDatabase.PasswordGroup.Passwords.OrderBy(p => p.Website) as INotifyCollectionChanged
- I'm fairly certain that'll give younull
.If you want to preserve the collection as a live collection emitting notifications, you need to construct a query and apply the ordering on top of that (note that you'll be limited by the sorting capabilities of the database, though a simple sort like that will work just fine). I haven't tested it, but I think something like this would work:
binding.Source = Database.PasswordGroup.Passwords.AsRealmQueryable().OrderBy(p => p.Website);
This did indeed work, thanks a lot !