UI gets stuck while loading lot of data from realm - xamarin

I am trying to show 10,000 contacts on listview in xamarin forms using realm. But whenever user traverse to contact listing screen
it gets freezed and loads after a while.
Moreover , i have provided an option to search from list as well and that too gets stuck as search if performing on UI thread.
Following is the code to load data from realm
public override async Task Initialize(Object data )
{
private Realm _realmInstance = getRealm();
if (contactList != null)
{
contactList.Clear();
}
contactList = _realmInstance.All<Contact>().OrderByDescending(d => d.contactId).ToList();
// here the binding happens with realm data
contacts = new ObservableCollectionFast<Contact>(contactList);
}
public ObservableCollectionFast<Contact> contacts
{
get { return items; }
set
{
items = value;
OnPropertyChanged("contacts");
}
}
as it was taking time in loading i thought to fetch realm data in background and bind it on UI thread as follows
but that is throwing error
realm accessed from incorrect thread
await Task.Run(() => {
contactList = _realmInstance.All<Contact>().OrderByDescending(d => d.contactId).ToList();
});
if (contactList.Count() > 0)
{
ContactListView = true;
AddContactMsg = false;
}
else
{
AddContactMsg = true;
}
Device.BeginInvokeOnMainThread(() =>
{
contacts = new ObservableCollectionFast<Contact>(contactList);
});
i wanted to try limiting the results by using TAKE function of LINQ but unfortunately its not supported by realm yet. not sure how i can smoothly load records from realm to listview.
EDIT
as per the SushiHangover i have changed things from IList to IQueryable
public IQueryable<Contact> contacts
{
get { return items; }
set
{
items = value;
OnPropertyChanged("contacts");
}
}
public override async Task Initialize(Object data )
{
_realmInstance = getRealm();
contacts = dbContactList= _realmInstance.All<Contact>();
}
so far search is working pretty smoothly but IQueryable change leads to another issue. on repeatedly performing following steps results in app crash
tap on list item
detail page gets open then press back
scroll down to few records
perform step 1 and repeat
this results into stackoverflow error
04-19 06:05:13.980 F/art ( 3943): art/runtime/runtime.cc:289]
Pending exception java.lang.StackOverflowError thrown by 'void
md5b60ffeb829f638581ab2bb9b1a7f4f3f.CellAdapter.n_onItemClick(android.widget.AdapterView,
android.view.View, int, long):-2' 04-19 06:05:13.980 F/art (
3943): art/runtime/runtime.cc:289] java.lang.StackOverflowError: stack
size 8MB
Link to entire log file
code to fire item click command is
public ICommand OnContactSelectCommand => new Command<Contact>(OnContactSelect);
following code will open ups a detail page
private async void OnContactSelect(Contact contact)
{
if (contact != null)
{
await NavigationService.NavigateToAsync<ContactDetailViewModel>(contact.mContactId);
}
}
Note:
when i replace IQueryable with List i do not face any error
somehow my issue is related to realm github thread where user is getting exception on listView.SelectedItem = null while using IQueryable
here is my code of list view item tap
private static void ListViewOnItemTapped(object sender, ItemTappedEventArgs e)
{
var listView = sender as ListView;
if (listView != null && listView.IsEnabled && !listView.IsRefreshing)
{
// have commented this line because it was crashing the app
//listView.SelectedItem = null;
var command = GetItemTappedCommand(listView);
if (command != null && command.CanExecute(e.Item))
{
command.Execute(e.Item);
}
}
}

Related

Load Image Async from Api

I have a Blazor Server .Net 6 app. It has a Synfusion grid which has an ImageViewer componen that I have built. When the grid is loaded it passes a DocumentID to the ImageViewer for each row. The ImageViwer takes the DocumenID and loads an image via a web API service from a database. Teh problem I have is that the image does not fully load, it works if I use the OnInitializedAsync method but thsi does not work if I filter the data. Any ideads the best method to load such images
<SfGrid>
<MyImageViewer AuthCookieValue="#AuthCookieValue" DocumentID="#data.DocumentID" />
<SfGrid>
//THIS IS MY IMAGE CONTROL
#inject HttpClient httpClient
#if (DocumentFileData != null)
{
<img src="data:image;base64,#(Convert.ToBase64String(DocumentFileData))" />
}
#code {
public int _DocumentID { get; set; }
[Parameter] public string AuthCookieValue { get; set; }
[Parameter] public int DocumentID
{
get { return _DocumentID; }
set
{
_DocumentID = value;
//I know this is naughty to call a method via a property and does not work but thought I would try to trigger the change of the image when I refresh the grid
Task.Run(() => GetDocument());
}
}
private byte[] DocumentFileData { get; set; }
protected async override Task OnInitializedAsync()
{
//THIS METHOD WORKS BUT DOES NOT WORK WHEN I CHANGE THE GRID
if (DocumentID != 0)
{
await GetDocument();
}
}
private async Task GetDocument()
{
httpClient.DefaultRequestHeaders.Clear();
httpClient.DefaultRequestHeaders.Add("Authorization", "Bearer " + AuthCookieValue);
MyServices.DocumentService documentService;documentService = new(httpClient);
documentService = new(httpClient);
DocumentModel doc = await documentService.GetDocumentAsync(_DocumentID);
DocumentFileData = doc.FileData;
}
}
Many thanks in advance
Make two small changes:
// //I know this is naughty to call a method via a property and does not work but thought I would try to trigger the change of the image when I refresh the grid
// Task.Run(() => GetDocument());
and
//protected async override Task OnInitializedAsync()
protected async override Task OnParametersSetAsync()
{
See the Lifecycle events documentation.
OnInitializedAsync() is only called once in the lifetime of a component. OnParametersSetAsync() is called each time DocumentID changes, and the side benefit is that you don't need that Task.Run() anymore.
The fact that Task.Run() was not awaited here made your UI fall out of sync and not show the image. It was being loaded but not rendered.

Stop validation error item from being published

We are using Sitecore 7.2 and have implemented the 'Required' field validator for number of fields.
However, the user can still save or create an item with validation error.
I know that we can stop this validation errored items from being published using Work Flow.
We do not want to implement any workflow, therefore can someone please suggest how to stop validation errored item from being able to published?
You can create your own validation class like this (the one below only check for validation bar errors):
public void ValidateItem(object sender, EventArgs args)
{
ItemProcessingEventArgs theArgs = (ItemProcessingEventArgs) args;
Item currentItem = theArgs.Context.PublishHelper.GetSourceItem(theArgs.Context.ItemId);
if ((currentItem != null) && (currentItem.Paths.IsContentItem))
{
if (!IsItemValid(currentItem))
{
theArgs.Cancel = true;
}
}
}
private static bool IsItemValid(Item item)
{
item.Fields.ReadAll();
ValidatorCollection validators = ValidatorManager.GetFieldsValidators(
ValidatorsMode.ValidatorBar, item.Fields.Select(f => new FieldDescriptor(item, f.Name)), item.Database);
var options = new ValidatorOptions(true);
ValidatorManager.Validate(validators, options);
foreach (BaseValidator validator in validators)
{
if (validator.Result != ValidatorResult.Valid)
{
return false;
}
}
return true;
}
and add event handler to publish:itemProcessing event:
<event name="publish:itemProcessing" help="Receives an argument of type ItemProcessingEventArgs (namespace: Sitecore.Publishing.Pipelines.PublishItem)">
<handler type="My.Assembly.Namespace.ValidateBeforePublish, My.Assembly" method="ValidateItem"/>
</event>
You could set the parameters field on the validation to "Result=FatalError" to stop the user from saving an item before the issue is resolved. This way the user has to fix the issue before they are allowed to save.

ReactiveCollection and invalid cross-thread access

I'm developing my first app for Windows Phone 7.1 using Reactive UI, and I'm facing difficulties working with ReactiveCollection class.
My app is roughly about accessing the WP7 SQL CE engine (LINQ to SQL). I would like to perform data operations in background, using ReactiveAsyncCommand. Data from database should "automagically" appear in UI, therefore I decided to use ReactiveCollection as a proxy between database and UI.
Here's my model, so you could have a better idea:
public class EncounteredModel
{
public ReactiveCollection<Fact> FactsCollection;
public ReactiveCollection<FactEntry> FactEntriesCollection;
private EncounteredModel()
{
using (EncounteredDataContext db = new EncounteredDataContext())
{
FactsCollection = new ReactiveCollection<Fact>(from fact in db.Facts select fact);
FactEntriesCollection = new ReactiveCollection<FactEntry>(from factEntry in db.FactEntries select factEntry);
}
FactsCollection.ItemsAdded.Subscribe(fact => { using (var db = new EncounteredDataContext()) { db.Facts.InsertOnSubmit(fact); db.SubmitChanges(); } });
FactsCollection.ItemsRemoved.Subscribe(fact => { using (var db = new EncounteredDataContext()) { db.Facts.DeleteOnSubmit(fact); db.SubmitChanges(); } });
FactEntriesCollection.ItemsAdded.Subscribe(factEntry => { using (var db = new EncounteredDataContext()) { db.FactEntries.InsertOnSubmit(factEntry); db.SubmitChanges(); } });
FactEntriesCollection.ItemsRemoved.Subscribe(factEntry => { using (var db = new EncounteredDataContext()) { db.FactEntries.DeleteOnSubmit(factEntry); db.SubmitChanges(); } });
}
private static EncounteredModel instance;
public static EncounteredModel Instance
{
get
{
if (instance == null)
instance = new EncounteredModel();
return instance;
}
}
}
In my view model I've tried using 2 different variants:
Create a derived reactive collection and bind UI to it (With ReactiveCollection.CreateDerivedCollection() method. It's derived from the EncounteredModel.FactsCollection, for example.
Use ObservableAsPropertyHelper<IEnumerable<Fact>>, then subscribe to Model's ReactiveCollection Changed IObservable to populate the OAPH.
Both variants, unfortunately, give me "Invalid cross-thread access." Stack trace is generally the same for both variants, here's one for the variant 1 (shortened to the significant part):
at MS.Internal.XcpImports.CheckThread()
at System.Windows.DependencyObject.GetValueInternal(DependencyProperty dp)
at System.Windows.FrameworkElement.GetValueInternal(DependencyProperty dp)
at System.Windows.DependencyObject.GetValue(DependencyProperty dp)
at System.Windows.Controls.ItemsControl.get_ItemsHost()
at System.Windows.Controls.ItemsControl.OnItemsChangedHandler(Object sender, ItemsChangedEventArgs args)
at System.Windows.Controls.ItemContainerGenerator.OnItemAdded(Object item, Int32 index, Boolean suppressEvent)
at System.Windows.Controls.ItemContainerGenerator.System.Windows.Controls.ICollectionChangedListener.OnCollectionChanged(Object sender, NotifyCollectionChangedEventArgs args)
at System.Windows.Controls.WeakCollectionChangedListener.SourceCollectionChanged(Object sender, NotifyCollectionChangedEventArgs e)
at System.Windows.Controls.ItemCollection.NotifyCollectionChanged(NotifyCollectionChangedEventArgs e)
When I change to ReactiveCommand (not async one), everything's fine. Any ideas how to overcome this? Much thanks in advance.
ReactiveCollection doesn't proxy everything to the UI thread, if you add items to it from a worker thread, you will signal the UI on that same thread and get the crash.
However, one thing that ReactiveAsyncCommand does for you is give you back the results back on the UI thread, so you can do something like this:
var cmd = new ReactiveAsyncCommand();
cmd.RegisterAsyncFunc(() => getAllTheItems())
.Subscribe(theItems => theItems.ForEach(item => collection.Add(item)));
The Subscribe is guaranteed to be on the UI thread (if it isn't, that's a bug)

ASP.Net MVC 3 System.Data.SqlClient.SqlException Timeout expired

I am developing an ASP.Net MVC 3 web application using Entity Framework 4.1. I recently uploaded the application to my test server and I have noticed an error email delivered by ELMAH stating
System.Data.SqlClient.SqlException Timeout expired. The timeout period
elapsed prior to completion of the operation or the server is not
responding.
Below shows some of my code.
Controller
public ActionResult VerifyEmail(int uid, string vid)
{
var userList = _userService.VerifyEmail(uid,vid).ToList();
}
Service
public IList<User> VerifyEmail(int uid, string emailvcode)
{
return _uow.User.Get(u => u.userID == uid && u.emailVerificationCode == emailvcode).ToList();
}
Unit of Work
public class UnitOfWork : IUnitOfWork, IDisposable
{
readonly LocumEntities _context = new LocumEntities();
private GenericRepository<User> _user = null;
public IGenericRepository<User> User
{
get
{
if (_user == null)
{
_user = new GenericRepository<User>(_context);
}
return _user;
}
}
}
Generic Repository
public IList<TEntity> Get(Expression<Func<TEntity, bool>> filter = null,Func<IQueryable<TEntity>, IOrderedQueryable<TEntity>> orderBy = null,string includeProperties = "")
{
IQueryable<TEntity> query = dbSet;
if (filter != null)
{
query = query.Where(filter);
}
foreach (var includeProperty in includeProperties.Split
(new char[] { ',' }, StringSplitOptions.RemoveEmptyEntries))
{
query = query.Include(includeProperty);
}
if (orderBy != null)
{
return orderBy(query).ToList();
}
else
{
return query.ToList();
}
}
The Timeout error is sometimes happening when the line within the Service method is trying to execute
return _uow.User.Get(u => u.userID == uid && u.emailVerificationCode == emailvcode).ToList();
This error is not happening every time, only occasionally, however, I don't understand why as this query will either return a list of Users, or, a NULL list.
Can anyone spot from my code why this may be happening?
Any feedback would be appreciated as I have no idea why this is happening.
Thanks.
Try increasing the timeout property in the connection string. Also run the SQL Server Profiler to see how much SQL is being generated for your queries, as the query may be returning a large volume of data causing the timeout.

Accessing User in Entity partial class via OnContextCreated()?

All of my tables have common audit fields: modifiedBy,modifiedDateTime, etc.
I would like to have these automatically set, and can set most of them with the following code:
partial class myEntities
{
partial void OnContextCreated()
{
this.SavingChanges += new EventHandler(Entities_SavingChanges);
}
private void Entities_SavingChanges(object sender, EventArgs e)
{
IEnumerable<ObjectStateEntry> objectStateEntries =
from ose
in this.ObjectStateManager.GetObjectStateEntries(EntityState.Added | EntityState.Modified)
where ose.Entity != null
select ose;
var auditDate = DateTime.Now;
var auditUser = System.Web.HttpContext.Current.User.Identity.Name;//I wish
foreach (ObjectStateEntry entry in objectStateEntries)
{
ReadOnlyCollection<FieldMetadata> fieldsMetaData = entry.CurrentValues.DataRecordInfo.FieldMetadata;
FieldMetadata modifiedField = fieldsMetaData.Where(f => f.FieldType.Name == "ModifiedBy").FirstOrDefault();
if (modifiedField.FieldType != null)
{
string fieldTypeName = modifiedField.FieldType.TypeUsage.EdmType.Name;
if (fieldTypeName == PrimitiveTypeKind.String.ToString())
{
entry.CurrentValues.SetString(modifiedField.Ordinal, auditUser);
}
}
}
}
}
The problem is that there doesn't appear to be any way to get access to the current user. The app is intranet only, using Windows auth.
Is there a way to either pass in a parameter, or get access to the HttpContext (which doesn't seem like it would be a good idea, but I'm stuck)? Is there a way to populate the EventArgs with information?
Check out the section where the poster has overridden the SaveChanges method (6th code box down on the page). This way you can pass in the UserID and perform your audit and not have to use an event handler.

Resources