ReactiveUI ObservableAsProperty not notifyin UI - c#

I am learning to use ReactiveUI in WPF.
I have run into problem, as this ObesrvableProperty NavigationItems doesnt update UI, but WhenAnyValue fires if I set breakpoint and check the .Select
My DI:
Locator.CurrentMutable.RegisterConstant(new WindowsAuthenticationService(), typeof(IWindowsAuthenticationService));
Locator.CurrentMutable.RegisterConstant<INavigatorService>(new NavigatorService(Locator.Current.GetService<IWindowsAuthenticationService>()));
Locator.CurrentMutable.RegisterConstant(new MainWindowViewModel(Locator.Current.GetService<IWindowsAuthenticationService>(),
Locator.Current.GetService<INavigatorService>()));
My binding:
this.OneWayBind(ViewModel, vm => vm.NavigatorService.NavigationItems, v => v.SideNavigator.Items, s => s.ToList());
My MainViewModel
public MainWindowViewModel(IWindowsAuthenticationService windowsAuthenticationService, INavigatorService navigatorService)
{
WindowsAuthenticationService = windowsAuthenticationService;
NavigatorService = navigatorService;
try
{
WindowsAuthenticationService.AuthenticateUser();
}
catch (UserNotRegisteredException ex)
{
MessageBox.Show(ex.Message);
Environment.Exit(0);
}
}
Navigator service
public class NavigatorService : ReactiveObject, INavigatorService
{
public NavigatorService(IWindowsAuthenticationService windowsAuthenticationService)
{
WindowsAuthenticationService = windowsAuthenticationService;
this.WhenAnyValue(x => x.WindowsAuthenticationService.AccessibleComponents)
.SelectMany(s => s ?? new List<TPermissionComponents>())
.Select(s => new FirstLevelNavigationItemWithValue() { Icon = s.Icon, Label = s.Name, Value = s.Component })
.ToList()
.ToPropertyEx(this, x => x.NavigationItems, new List<FirstLevelNavigationItemWithValue>());
}
public IWindowsAuthenticationService WindowsAuthenticationService { get; }
[ObservableAsProperty]
public IEnumerable<BaseNavigationItem> NavigationItems { get; }
}
And finnaly auth service:
public class WindowsAuthenticationService : ReactiveObject, IWindowsAuthenticationService
{
public TUsers CurrentUser { get; set; }
[Reactive]
public IEnumerable<TPermissionComponents> AccessibleComponents { get; set; }
public void AuthenticateUser()
{
var user = GetCurrentUser();
if (user is null)
throw new UserNotRegisteredException(Environment.UserName);
CurrentUser = user;
AccessibleComponents = GetAccessibleComponents(user);
return;
}
public IEnumerable<TPermissionComponents> GetAccessibleComponents(TUsers user)
{
using AuthenticationContext ctx = new AuthenticationContext();
var components = from assigment in ctx.TPermissionAssignment
join permission in ctx.TPermissions
on assigment.RoleId equals permission.RoleId
join component in ctx.TPermissionComponents
on permission.ComponentId equals component.Id
where assigment.UserId == user.Id
select component;
return components.ToList();
}
public TUsers GetCurrentUser()
{
if (CurrentUser is null)
{
using AuthenticationContext ctx = new AuthenticationContext();
return ctx.TUsers.FirstOrDefault(f => f.Username.ToLower() == Environment.UserName.ToLower());
}
else
{
return CurrentUser;
}
}
I hope someone can help me, I am clueless for past 4 hours.
I will be glad for any ideas.
Thanks.

If someone comes to the same issue. This solved it to me:
public NavigatorService(IWindowsAuthenticationService windowsAuthenticationService)
{
WindowsAuthenticationService = windowsAuthenticationService;
this.WhenAnyValue(x => x.WindowsAuthenticationService.AccessibleComponents)
.Select(s => new ObservableCollection<FirstLevelNavigationItemWithValue>((s ?? new List<TPermissionComponents>()).Select(s => new FirstLevelNavigationItemWithValue() { Icon = Enum.Parse(typeof(PackIconKind), s.Icon, true), Label = s.Name, Value = s.Component })))
.ToPropertyEx(this, x => x.NavigationItems, new ObservableCollection<FirstLevelNavigationItemWithValue>());
}
public IWindowsAuthenticationService WindowsAuthenticationService { get; }
[ObservableAsProperty]
public IEnumerable<BaseNavigationItem> NavigationItems { get; }
Be noted, that if ObservableAsProperty is setup like this:
[ObservableAsProperty]
public ObservableCollection<BaseNavigationItem> NavigationItems { get; }
Then the ToPropertyEx will freak out and wont compile.
My initial thought was the .WhenAnyValue().ToPropertyEx() would spit out every time new collection and that would trigger the binding.
Now I understand it like the .WhenAnyValue().ToPropertyEx() creates sort of pipeline throught which each object (entity) goes only once.
I my assumption is correct, then I would love to know, if it is more profitable performance wise, than spitting new collection each time.
Anyway, I hope this will help someone.

Related

LiteDB return List of hierarchical data

I am new to LiteDb and maybe, I think to much in SQL way, instead of NoSQL way.
I try to receive a list of all child documents, which are defined within a project class.
Therefore, I have two classes generated:
public class Project : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
public long Id { get; set; }
public string ProjectName { get; set; }
public List<Title> Titles { get; set; } = new List<Title>();
}
And:
public class Title : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
public long Id { get; set; }
public string Title { get; set; }
}
What I try to achieve is, to get all Titles, which are stored in a specific project document.
I try the following code in C#
public async Task<IList<NamespaceTitle>> GetAllNamespacesAsync(string projectTitle)
{
var locReturnValue = await Task.Run(
() =>
{
using (var locDataBase = GetLiteDatabase())
{
var locCollection = locDataBase.GetCollection<Project>("projects");
var locGetAllTitles = locCollection.
Query().
Include(locTitle => locTitle.Titles).
Where(locProject => locProject.ProjectName == projectTitle).
Select<Title>(locTitles => locTitles.Titles).ToList();
return locGetAllTitles;
}
});
return locReturnValue;
}
The compiler error says, that a list can not be converted to Title
But when I change the
"Select(locTitles => locTitles.Titles)" in the query to
"Select<List>(locTitles => locTitles.Titles)",
the compiler error disapears, but then the return type is: "list<list>" !?
Can someone please help me, doing this in a correct way.
THX a lot in advance
I think, I found a solution for my problem.
After querying the data, I found out, that only the Id's of the child elements get saved in the master document.
So, when I do the following. Not sure, if it the common way, but it works.
var locProjectCollection = locDataBase.GetCollection<Project>("Project");
var locGetProjects = locProjectCollection.
Query().
Include(locTitles => locTitles.Titles).
Where(locProject => locProject.ProjectName == projectTitle).
ToList();
var locGetTitles = locGetProjects.
SingleOrDefault()?.
Titles.Select(locTitle => locTitle.Id);
if (locGetTitles.Count() > 0)
{
var locTitleCollection = locDataBase.GetCollection<Title>("Title");
return locTitleCollection.
Query().
Where(locTitle => locGetTitles.Contains(locTitle.Id)).
ToList();
}
else
{
return null;
}
Hope this will help other users as well - or please post an other aproach.

How to properly implement backend ReactiveList in RxUI to avoid thread affinity issue?

I'm new to Rx.Net & RxUI. During my learning of these two libraries, I tried to build a demo application which extracts images from websites. I used WPF combined with Rx.Net and RxUI to build the Views and ViewModels, and HtmlAgilityPack to handle html documents. My code looks like below
ViewModel:
public class MainViewModel : ReactiveObject
{
public MainViewModel()
{
var canSearch =
this.WhenAnyValue(x => x.TargetUrl, targetWebSite => !string.IsNullOrEmpty(targetWebSite));
_searchCommand = ReactiveCommand.CreateFromTask(GetHtmlDocument, canSearch);
_imageSequence = _searchCommand
.SelectMany(ImageExtractService.ExtractAllImageAddress).Distinct().Publish().RefCount();
_imageSequence.Subscribe(
url => ImageList.Add(new ScrappedWebImageViewModel { ImageUrl = url }),
ex => this.Log().Error(ex)); //Causing problem, need better solution
}
private readonly IObservable<string> _imageSequence;
private IHtmlDownloadService _htmlDownloadService;
private IHtmlDownloadService HtmlDownloadService =>
_htmlDownloadService ?? (_htmlDownloadService = Locator.Current.GetService<IHtmlDownloadService>());
private IImageExtractService _imageExtractService;
private IImageExtractService ImageExtractService =>
_imageExtractService ?? (_imageExtractService = Locator.Current.GetService<IImageExtractService>());
public ReactiveList<ScrappedWebImageViewModel> ImageList =
new ReactiveList<ScrappedWebImageViewModel>();
private readonly ReactiveCommand<Unit, HtmlDocument> _searchCommand;
public ICommand SearchCommand => _searchCommand;
private async Task<HtmlDocument> GetHtmlDocument()
{
return await HtmlDownloadService.GetHtmlDocument(TargetUrl);
}
}
View:
public partial class MainWindow : IViewFor<MainViewModel>
{
public MainWindow()
{
InitializeComponent();
ViewModel = new MainViewModel();
this.WhenActivated(d =>
{
d(this.Bind(ViewModel, x => x.Status, x => x.TblStatus.Text));
d(this.Bind(ViewModel, x => x.Progress, x => x.TblProgress.Text));
d(this.Bind(ViewModel, x => x.TargetUrl, x => x.TbxTargetWebSite.Text));
d(this.OneWayBind(ViewModel, x => x.ImageList, x => x.LbxImageList.ItemsSource));
d(this.BindCommand(ViewModel, x => x.SearchCommand, x => x.BtnBeginSearch));
});
}
public static readonly DependencyProperty ViewModelProperty =
DependencyProperty.Register("ViewModel", typeof(MainViewModel), typeof(MainWindow));
public MainViewModel ViewModel
{
get => (MainViewModel) GetValue(ViewModelProperty);
set => SetValue(ViewModelProperty, value);
}
object IViewFor.ViewModel
{
get => ViewModel;
set => ViewModel = (MainViewModel)value;
}
}
HtmlDownloadService:
internal class HtmlDownloadService : IHtmlDownloadService
{
private readonly HtmlWeb _webClient = new HtmlWeb();
public async Task<HtmlDocument> GetHtmlDocument(string url)
{
return await Task.Run(() => _webClient.Load(url));
}
}
ImageExtractService:
internal class ImageExtractService : IImageExtractService
{
public IEnumerable<string> ExtractAllImageAddress(HtmlDocument doc)
{
const string mstring = #".+\.(jpg|png|ico|jpeg|bmp|tif)$";
var hrefList = doc.DocumentNode.SelectNodes(#".//*[#href]");
var srcList = doc.DocumentNode.SelectNodes(#".//*[#src]");
if (hrefList != null)
{
foreach (var href in hrefList)
{
var attr = href.Attributes["href"];
if (Regex.IsMatch(attr.Value, mstring))
{
yield return attr.Value;
}
}
}
if (srcList == null) yield break;
foreach (var src in srcList)
{
var attr = src.Attributes["src"];
if (Regex.IsMatch(attr.Value, mstring))
{
yield return attr.Value;
}
}
}
}
The problem is, right after the command is executed, the application will halt. At this moment the main thread is running in
System.Reactive.dll!System.Reactive.Concurrency.AsyncLock.Wait
However no exception is thrown, and the application will exit if allowed to proceed. I tried to quote/unquote several lines, and it seems this is just another instance of the 'thread affinity' issue. But I don't know how fix this problem. My questions in short is:
How to update the VM in the most appropriate way?
How to catch the exception that shut down the application?
Update:
I tried with some other methods without the observables
public MainViewModel()
{
var canSearch =
this.WhenAnyValue(x => x.TargetUrl, targetWebSite => !string.IsNullOrEmpty(targetWebSite));
SearchCommand = ReactiveCommand.CreateFromTask(SearchImageAsync, canSearch, ThreadPoolScheduler.Instance);
}
and
private async Task SearchImageAsync()
{
var doc = await HtmlDownloadService.GetHtmlDocument(TargetUrl);
var imgs = ImageExtractService.ExtractAllImageAddress(doc);
foreach (var url in imgs)
{
ImageList.Add(new ScrappedWebImageViewModel {ImageUrl = url});
}
}
But still can't solve it. I'm using the latest unstable(alpha/preview) version of Rx.Net & RxUI, and there is little sample code for me to get started. So if anyone could provide some it would be a huge help, thanks!
Change
_imageSequence.Subscribe(
to
_imageSequence.ObserveOn(RxApp.MainThreadScheduler).Subscribe(

Entity framework error as"New transaction is not allowed because there are other threads running in the session

We are using entity framework codefirst approach
I am new to entity framework and I am facing error while trying to do "New transaction is not allowed because there are other threads running in the session.
public class DatabaseBackup : IDataBackup
{
private readonly IMonarchDbContext m_db;
public DatabaseBackup(IMonarchDbContext podb)
{
if (podb == null)
throw new ArgumentNullException("podb");
m_db = podb;
}
public DBBackupHistory GetLatestBackupHistory(DBBackupFrequency backupFrequency = DBBackupFrequency.Periodic)
{
DBBackupHistory result = null;
// get the backup history of the given backuptype and populate the objects
var configId = m_db.DBBackupConfigurations.Where(c => c.ScheduleType == (int)backupFrequency && c.BackupStatus == 1).Distinct().Select(c => c.ConfigurationId).DefaultIfEmpty(-1).First();
if (configId > 0)
{
result = m_db.DBBackupHistorys.Where(b => b.Status == 1 && b.ConfigurationId == configId).OrderByDescending(lb => lb.BackupDatetime).FirstOrDefault();
}
return result;
}
public IEnumerable<DBBackupConfiguration> GetAllConfiguration()
{
var result = m_db.DBBackupConfigurations.Where(c => c.BackupStatus == 1).OrderByDescending(c => c.ConfigurationId);
return result;
}
public void Backup(DBBackupConfiguration config, int fileIndex)
{
Console.WriteLine("Running DB Backup type {0} to device {1}", (DBBackupType)config.BackupType, fileIndex);
m_db.StoredProc.SPBackup(config, fileIndex);
}
I am calling the below methods in another class as follows
private readonly IDataBackup m_dataBackup;
public int PerformBackup(int defaultPollIntervalInMinutes = 15)
{
// polling interval in Minutes
int pollInterval = defaultPollIntervalInMinutes;
int fileIndex = getCurrentDumpFileIndex();
// check for the backup configuration
var configurations = m_dataBackup.GetAllConfiguration();
foreach (var config in configurations)
{
var lastBackup = m_dataBackup.GetLatestBackupHistory(DBBackupFrequency.Weekly);
if (lastBackup == null)
{
m_dataBackup.Backup(config, fileIndex + 1);
break;
}
Here is the Db Context class is as below
public class MonarchDbContext:DbContext,IMonarchDbContext
{
private IStoredProcedure m_storedProc;
private static object m_dbIntializerSet;
public MonarchDbContext(string nameOrConnectionString)
: base( nameOrConnectionString )
{
//-- Set the DB initializer only once.
System.Threading.LazyInitializer.EnsureInitialized( ref m_dbIntializerSet,()=>{
Database.SetInitializer<MonarchDbContext>(null);
//-- Give debug builds a chance to overwrite the above.
_SetInitializerForDebugBuilds();
return new object();
});
Configuration.LazyLoadingEnabled = false;
Configuration.ProxyCreationEnabled = false;
var csb = new SqlConnectionStringBuilder( this.Database.Connection.ConnectionString );
csb.MultipleActiveResultSets = true;
this.Database.Connection.ConnectionString = csb.ToString();
var objectContext = ( this as IObjectContextAdapter ).ObjectContext;
objectContext.CommandTimeout = 3600;
}
#region Public "Tables"
public IDbSet<DBBackupConfiguration> DBBackupConfigurations { get; set; }
public IDbSet<DBBackupHistory> DBBackupHistorys { get; set; }
public IStoredProcedure StoredProc
{
get
{
return System.Threading.LazyInitializer.EnsureInitialized(ref m_storedProc, () => new BackupStoredProc(this.Database));
}
}
#endregion
please let me know how can i solve the issue.
I found the issue
I need to add toList() at the end of the Linq code and it just worked for me.
public IEnumerable<DBBackupConfiguration> GetAllConfiguration()
{
var result = m_db.DBBackupConfigurations.Where(c => c.BackupStatus == 1).OrderByDescending(c => c.ConfigurationId).ToList();
return result;
}
Just add the List to Ienumerbale types

ServiceStack Redis how to implement paging

I am trying to find out how to do paging in SS.Redis, I use:
var todos = RedisManager.ExecAs<Todo>(r => r.GetLatestFromRecentsList(skip,take));
it returns 0, but i am sure the database is not empty, because r.GetAll() returns a list of things. What is the correct way to do this?
EDIT: Here is the code:
public class ToDoRepository : IToDoRepository
{
public IRedisClientsManager RedisManager { get; set; } //Injected by IOC
public Todo GetById(long id) {
return RedisManager.ExecAs<Todo>(r => r.GetById(id));
}
public IList<Todo> GetAll() {
return RedisManager.ExecAs<Todo>(r => r.GetAll());
}
public IList<Todo> GetAll(int from, int to) {
var todos = RedisManager.ExecAs<Todo>(r => r.GetLatestFromRecentsList(from,to));
return todos;
}
public Todo NewOrUpdate(Todo todo) {
RedisManager.ExecAs<Todo>(r =>
{
if (todo.Id == default(long)) todo.Id = r.GetNextSequence(); //Get next id for new todos
r.Store(todo); //save new or update
});
return todo;
}
public void DeleteById(long id) {
RedisManager.ExecAs<Todo>(r => r.DeleteById(id));
}
public void DeleteAll() {
RedisManager.ExecAs<Todo>(r => r.DeleteAll());
}
}
As I don't see any code, I'm assuming you're not maintaining the Recents list when you're adding the entites. Here's the test case for GetLatestFromRecentsList:
var redisAnswers = Redis.As<Answer>();
redisAnswers.StoreAll(q1Answers);
q1Answers.ForEach(redisAnswers.AddToRecentsList); //Adds to the Recents List
var latest3Answers = redisAnswers.GetLatestFromRecentsList(0, 3);
var i = q1Answers.Count;
var expectedAnswers = new List<Answer>
{
q1Answers[--i], q1Answers[--i], q1Answers[--i],
};
Assert.That(expectedAnswers.EquivalentTo(latest3Answers));
Redis StackOverflow is another example that uses the Recents list feature to show the latest Questions added. It maintains the recent list of questions by calling AddToRecentsList whenever a new Question is created.

AutoMapper Map to different type based on an enum?

I'm starting to implement AutoMapper, first I managed to integrate it with Castle.Windsor, which I'm already using. Now I have a Post entity which I want to map to either a LinkPostModel or an ImagePostModel. Both inherit from PostModel
1) This is what I have so far:
public class PostModelFromPostEntityConverter : ITypeConverter<Post, PostModel>
{
private readonly IPostService postService;
public PostModelFromPostEntityConverter(IPostService postService)
{
if (postService == null)
{
throw new ArgumentNullException("postService");
}
this.postService = postService;
}
public PostModel Convert(ResolutionContext context)
{
Post post = (Post)context.SourceValue;
Link link = post.Link;
if (link.Type == LinkType.Html)
{
return new LinkPostModel
{
Description = link.Description,
PictureUrl = link.Picture,
PostId = post.Id,
PostSlug = postService.GetTitleSlug(post),
Timestamp = post.Created,
Title = link.Title,
UserMessage = post.UserMessage,
UserDisplayName = post.User.DisplayName
};
}
else if (link.Type == LinkType.Image)
{
return new ImagePostModel
{
PictureUrl = link.Picture,
PostId = post.Id,
PostSlug = postService.GetTitleSlug(post),
Timestamp = post.Created,
UserMessage = post.UserMessage,
UserDisplayName = post.User.DisplayName
};
}
return null;
}
}
Obviously the point in implementing AutoMapper is removing repeat code like this, so how am I supposed to map the common stuff, before adding my custom rules (such as the if-clause)
Ideally I'd want this to be something like:
public class PostModelFromPostEntityConverter : ITypeConverter<Post, PostModel>
{
[...]
public PostModel Convert(ResolutionContext context)
{
Post post = (Post)context.SourceValue;
Link link = post.Link;
if (link.Type == LinkType.Html)
{
return Mapper.Map<Post, LinkPostModel>(post);
// and a few ForMember calls?
}
else if (link.Type == LinkType.Image)
{
return Mapper.Map<Post, ImagePostModel>(post);
// and a few ForMember calls?
}
return null;
}
}
2) After this mapping is complete. I have a "parent" mapping, where I need to map an IEnumerable<Post> the following model:
public class PostListModel : IHasOpenGraphMetadata
{
public OpenGraphModel OpenGraph { get; set; } // og:model just describes the latest post
public IList<PostModel> Posts { get; set; }
}
So basically I'd need another TypeConverter (right?), which allows me to map the posts list first, and then create the og:model
I have this, but it feels kind of clunky, I feel it could be better:
public class PostListModelFromPostEntityEnumerableConverter : ITypeConverter<IEnumerable<Post>, PostListModel>
{
public PostListModel Convert(ResolutionContext context)
{
IEnumerable<Post> posts = (IEnumerable<Post>)context.SourceValue;
PostListModel result = new PostListModel
{
Posts = posts.Select(Mapper.Map<Post, PostModel>).ToList()
};
Post first = posts.FirstOrDefault();
result.OpenGraph = Mapper.Map<Post, OpenGraphModel>(first);
return result;
}
}
3) I didn't actually run the code yet, so another question comes to mind, and that is why aren't mappings strongly typed in converters?
IEnumerable<Post> posts = (IEnumerable<Post>)context.SourceValue;
where it could actually be
IEnumerable<Post> posts = context.SourceValue;
Trying to get Necromancer badge.
Nowadays this task can be solved much easier with using ConstructUsing function specifc fields should be filled in the provided action, but all the common fields will go to ForMember execution of the mapping. Collections in this case doesn't requires any additional logic/mapping configurations. Classes that has a property of type collection as well.
cfg.CreateMap<Post, PostModel>()
.ConstructUsing(p =>
{
switch (p.Type)
{
case LinkType.Html: return new LinkPostModel
{
Title = p.Description
// other specific fields
};
case LinkType.Image: return new ImagePostModel
{
// other specific fields
};
}
return null;
})
.ForMember(x => x.PostId, m => m.MapFrom(p => p.Id));
cfg.CreateMap<PostList, PostListModel>();

Categories