ReactiveUI and casting in WhenAny - c#

What I am doing is this:
Item.PropertyChanged += (sender, args) =>
{
if(sender is IInterface)
DoSomethingWith(((IInterface)sender).PropertyFromInterface);
}
how would I go about implementing such a stream in RxUI?
I tried this:
this.WhenAny(x => (x.Item as IInterface).PropertyFromInterface, x.GetValue())
.Subscribe(DoSomethingWith);
but it seems that is not possible to do.
Would I have to make a property like this? ->
private IInterface ItemAsInterface { get { return Item as IInterface; } }
I made a workaround for now like this:
this.WhenAny(x => x.Item, x => x.GetValue()).OfType<IInterface>()
.Select(x => x.PropertyFromInterface).DistinctUntilChanged()
.Subscribe(DoSomethingWith);
but what I actually want is getting the propertychanged updates for "PropertyFromInterface" while Item is of IInterface.

How about:
this.WhenAny(x => x.Item, x => x.Value as IInterface)
.Where(x => x != null)
.Subscribe(DoSomethingWith);
Update: Ok, I vaguely understand what you want to do now - here's how I would do it:
public ViewModelBase()
{
// Once the object is set up, initialize it if it's an IInterface
RxApp.MainThreadScheduler.Schedule(() => {
var someInterface = this as IInterface;
if (someInterface == null) return;
DoSomethingWith(someInterface.PropertyFromInterface);
});
}
If you really want to initialize it via PropertyChanged:
this.Changed
.Select(x => x.Sender as IInterface)
.Where(x => x != null)
.Take(1) // Unsubs automatically once we've done a thing
.Subscribe(x => DoSomethingWith(x.PropertyFromInterface));

Checked back with my old questions, I was looking for something like this solution:
this.WhenAny(x => x.Item, x => x.GetValue())
.OfType<IInterface>()
.Select(x => x.WhenAny(y => y.PropertyFromInterface, y => y.Value)
.Switch()
.Subscribe(DoSomethingWith);
The missing link for me was the .Switch method.
Additionally, I wanted the observable to not do anything if the property is NOT of the needed type:
this.WhenAny(x => x.Item, x => x.Value as IInterface)
.Select(x => x == null ?
Observable.Empty :
x.WhenAny(y => y.PropertyFromInterface, y => y.Value)
.Switch()
.Subscribe(DoSomethingWith);
(e.g. When I set this.Item to an instance of IInterface, I wanted DoSomethingWith to listen to changes to that instance's PropertyFromInterface, and when this.Item gets set to something different, the observable should not continue to fire until this.Item is an instance of IInterface again.)

Related

ReactiveList problems

We are relatively new to ReactiveUI so this may explain why we are having some issues with getting a view model working.
In our view model we have a ReactiveList of a class , for which there is a 'selected' in the class.
In the view model we want to have a 'AnySelected' property such that if there is at least 1 item in the list marked as selected then AnySelected is true.
We are having difficulty in getting this work.
As a small test application, with just strings, we have tried this, but messages around changes occurring don't appear.
public class TestRx : ReactiveObject
{
private ReactiveList<string> mySelectedItems;
public ReactiveList<string> MySelectedItems
{
get { return mySelectedItems; }
set { this.RaiseAndSetIfChanged(ref mySelectedItems, value); }
}
public TestRx()
{
var firstList = new ReactiveList<string>();
var t = this.WhenAnyValue(x => x.MySelectedItems);
var t1 = t.Select(x => x ?? new ReactiveList<string>());
var changed = t1.Select(x => x.Changed.Select(_ => Unit.Default));
var itemChanged = t1.Select(x => x.ItemChanged.Select(_ => Unit.Default));
var countChanged = t1.Select(x => x.CountChanged.Select(_ => Unit.Default));
t.Subscribe(x => Debug.WriteLine("T HAS CHANGED {0}", x == firstList));
t1.Subscribe(z => Debug.WriteLine("T1 Changed {0}", z == firstList));
changed.Subscribe(x => Debug.WriteLine("Changed :"));
itemChanged.Subscribe(x => Debug.WriteLine("Item Changed :"));
var replacementList = new ReactiveList<SelItem>(new[] {
new SelItem() { Selected = false }
});
Debug.WriteLine("***********************Assign 1st list");
MySelectedItems = firstList;
Thread.Sleep(100);
Debug.WriteLine("***********************Adding item 2 list");
MySelectedItems.Add("a new string");
// we don't get any debug messages as a result of the above
Thread.Sleep(100);
Debug.WriteLine("***********************Assign null");
MySelectedItems = null;
Thread.Sleep(100);
}
}
What are we doing wrong ?
This is a common pattern, but it's a bit tricky to implement, because you have to handle all of the following scenarios:
The list is set
The list items change
The "Selected" property on any items change. Keep in mind, the items you want to watch, change because of #1 or #2.
How do I do it?
Here's one way to do it. It's complicated, and that hints at a place where future versions of RxUI could make things Better, but here's what you can do for now.
IObservable<bool> WhenAnyAreTrue(IEnumerable<ViewModel> currentElements)
{
// NB: 'Unit' here means, we don't care about the actual value, just
// that something changed
var notifyWhenAnySelectedItemChanges = currentElements
.Select(x => x.WhenAny(y => y.Selected, _ => Unit.Default).Skip(1))
.Merge();
return notifyWhenAnySelectedItemChanges
.StartWith(Unit.Default)
.Select(_ => currentElements.Any(x => x.Selected));
}
// Any time MySelectedItems change or when the items in it change,
// create a new WhenAnyAreTrue and switch to it
this.WhenAnyObservable(x => x.MySelectedItems.ItemsChanged)
.Select(_ => WhenAnyAreTrue(MySelectedItems))
.Switch()
.ToProperty(this, x => x.AnySelected, out anySelected);

Lambda expression: Where clause for Include list

So I got something like this:
var myObj = db.SomeObject
.Include("Tasks")
.SingleOrDefault(x => x.Id == someObjectId);
if (myObj != null)
{
myObj.Tasks = myObj.Tasks.OrderBy(x => x.Number).ToList();
}
Here I want to be able to put a condition (where) on my Include, for instance:
.where task.IsDeleted == false
Thusfar I have not found a solution.
I am aware of the fact that I could use the where together with where I order the tasks but this however, does not run on the database but in stead uses memory. I want it to run on the database.
Does anyone here know how I can do this?
If so, is there also a way to put the order by condition to the included list of tasks?
Something like this, returs you your original object with its child collection filtered and sorted.
SomeObject a = db.SomeObjects.Where(x => x.Id == someobjectid)
.Select(
x =>
new
{
someObject = x,
task = x.Tasks.Where(task => task.IsDeleted == false)
.OrderBy(task => whatever)
})
.Select(x => x.someObject).Single();
It's actually loosing the collection of activities in the last select so you can do this :
SomeObject a = db.SomeObjects.Where(x => x.Id == someobjectid)
.Select(
x =>
new
{
someObject = x,
task = x.Tasks.Where(task => task.IsDeleted == false)
.OrderBy(task => whatever)
});
return a.FirstOrDefault().someObject;
To do this is EF, you need to specify a projection with a Select clause.
Something like this will get just the data you want from the db:
var anonymous = db.SomeObject.Where( x => x.Id == someObjectId )
.Select( x => new
{
SomeObject = x,
Tasks = x.Tasks
.Where( o => !o.IsDeleted )
.OrderBy( o => ... )
}
)
.SingleOrDefault()
;
You will end up with an instance of the anonymous type , but you can easily fix that up on the client:
MyObject myObject = anonymous.SomeObject;
myObject.Tasks = anonymous.Tasks;
The simple answer is: You can't do that.
Why? Because you query SomeObject.
Each returned SomeObject contains all referenced data, because if it wouldn't it wouldn't represent the actual object in the database.
What about getting them separately:
var myObj = db.SomeObject
.SingleOrDefault(x => x.Id == someObjectId);
var tasks = db.SomeObject
.Where(x => x.Id == someObjectId)
.SelectMany(x => x.Tasks)
.Where(x => !x.Deleted);

Is it possible to refactor this nHibernate Linq query?

Currently I have the following code:
switch (publicationType)
{
case PublicationType.Book:
return Session.Query<Publication>()
.Where(p => p.PublicationType == PublicationType.Book)
.OrderByDescending(p => p.DateApproved)
.Take(10)
.Select(p => new PublicationViewModel
{
...
});
case PublicationType.Magazine:
return Session.Query<Publication>()
.Where(p => p.PublicationType == PublicationType.Magazine)
.OrderByDescending(p => p.DateApproved)
.Take(10)
.Select(p => new PublicationViewModel
{
...
});
case PublicationType.Newspaper
....
}
As you can see the query is the same each time except for the publicationType condition. I tried to refactor this by creating a method that takes a Func e.g.
private IEnumerable<PublicationViewModel> GetPublicationItems(Func<PublicationType, bool>> pubQuery)
{
return Session.Query<Publication>()
.Where(pubQuery)
.OrderByDescending(p => p.DateApproved)
.Take(10)
.Select(p => new PublicationViewModel
{
...
});
}
private bool IsBook(PublicationType publicationType)
{
return publicationType == PublicationType.Book;
}
and then calling this method like
GetPublicationItems(IsBook);
But when I do this I get the error:
InvalidCastException: Unable to cast object of type 'NHibernate.Hql.Ast.HqlParameter' to type 'NHibernate.Hql.Ast.HqlBooleanExpression'.
Is there another way to do this?
It sounds like you don't really need a function - you just need a PublicationType:
private IEnumerable<PublicationViewModel>
GetPublicationItems(PublicationType type)
{
return Session.Query<Publication>()
.Where(p => p.PublicationType == type)
.OrderByDescending(p => p.DateApproved)
.Take(10)
.Select(p => new PublicationViewModel
{
...
});
}
If you really need it more general than that, you probably just need to change your code to use an expression tree instead of a delegate (and change the input type):
private IEnumerable<PublicationViewModel> GetPublicationItems(
Expression<Func<Publication, bool>> pubQuery)
{
return Session.Query<Publication>()
.Where(pubQuery)
.OrderByDescending(p => p.DateApproved)
.Take(10)
.Select(p => new PublicationViewModel
{
...
});
}
You won't be able to call it with GetPublicationItems(IsBook) at that point though. You could do:
GetPublicationItems(p => p.PublicationType == PublicationType.Book)
Or:
private static readonly Expression<Func<Publication, bool>> IsBook =
p => p.PublicationType == PublicationType.Book;
...
GetPublicationItems(IsBook)
Is there a reason you can't just use publicationType in the query?
return Session.Query<Publication>()
.Where(p => p.PublicationType == publicationType)
.OrderByDescending(p => p.DateApproved)
.Take(10)
.Select(p => new PublicationViewModel
{
...
});
Your mistake was confusing Delegates with Expression trees.
Func is a delegate, that cannot be turned into SQL.
You could just have written it like so:
Session.Query<Publication>()
.Where(p => p.PublicationType == yourPubilcationType)
...
Or, if you want to pass a filter that method, like you hinted in your sample:
IEnumerable<PublicationViewModel> GetPublicationItems(Expression<Func<PublicationType, bool>> pubQuery)
{
return Session.Query<Publication>()
.Where(pubQuery)
.OrderByDescending(p => p.DateApproved)
.Take(10)
.Select(p => new PublicationViewModel
{
...
});
}

syntax in LINQ IEnumerable<string>

This is my code that works great:
IPRepository rep = new IPRepository();
var Q = rep.GetIp()
.Where(x => x.CITY == CITY)
.GroupBy(y => o.Fam)
.Select(z => new IpDTO
{
IId = z.Key.Id,
IP = z.Select(x => x.IP).Distinct()
});
IP is IEnumerable<string>
I need to add to this code above a call to a function PAINTIP(ip).
I need to send each one of the elements that will be inside IP to a function PAINTIP(ip).
therefore i need to use some kind of foreach function but i cannot figure out how.
rep.GetIp()
.Where(x => x.CITY == CITY)
.GroupBy(y => o.Fam)
.Select(z => new IpDTO
{
IId = z.Key.Id,
IP = z.Select(x => x.IP).Distinct()
})
.SelectMany(item => item.IP)
.ToList()
.ForEach(PAINTIP)
You can do something like this :
var result = from item in collection
.Where(i => i.condition == value)
.Select(i => new{ Name = i.name, Description = i.Description })
.ToList().ForEach(i => Function(i));
Hope this help !
Very similiar to the answer of this question
LINQ equivalent of foreach for IEnumerable<T>
There is no For Each for an IEnumerable but there is for List
items.ToList().ForEach(i => i.DoStuff());
Edit
I'm not sure you can do it in the way you want to other than this:
ep.GetIp()
.Where(x => x.CITY == CITY)
.GroupBy(y => o.Fam)
.Select(z => new IpDTO
{
IId = z.Key.Id,
IP = z.Select(x => x.IP).Distinct()
})
.ToList().ForEach(IpObj => IpObj.IP.ToList().ForEach(ip => PAINTIP(ip));
This way you get your list of IpDTO objects out first. Then Enumerate over each and then in turn over each IpDTO objects IP IEnumerable. I haven't seen a way to do it where you can Enumerate inside the creation of your IpDTO object. If someone has an example of how to do so I'd like to see to learn myself.

How do I put all types implementing a certain generic interface in a dictionary?

Given a particular interface ITarget<T> and a particular type myType, here's how you would determine T if myType implements ITarget<T>. (This code snippet is taken from the answer to an earlier question.)
foreach (var i in myType.GetInterfaces ())
if (i.IsGenericType
&& i.GetGenericTypeDefinition() == typeof(ITarget<>))
return i.GetGenericArguments ()[0] ;
However, this only checks a single type, myType. How would I create a dictionary of all such type parameters, where the key is T and the value is myType? I think it would look something like this:
var searchTarget = typeof(ITarget<>);
var dict = Assembly.GetExecutingAssembly().[???]
.Where(t => t.IsGenericType
&& t.GetGenericTypeDefinition() == searchTarget)
.[???];
What goes in the blanks?
var searchTarget = typeof(ITarget<>);
var dict = Assembly.GetExecutingAssembly()
.GetTypes()
.SelectMany(t => t.GetInterfaces()
.Where(i => i.IsGenericType
&& (i.GetGenericTypeDefinition() == searchTarget)
&& !i.ContainsGenericParameters),
(t, i) => new { Key = i.GetGenericArguments()[0], Value = t })
.ToDictionary(x => x.Key, x => x.Value);
Note that if you have multiple classes implementing ITarget<> and using the same generic type argument -- for example, class Foo : ITarget<string> and class Bar : ITarget<string> -- then the ToDictionary call will fail with an ArgumentException complaining that you can't add the same key twice.
If you do need a one-to-many mapping then you have a couple of options available.
Use ToLookup rather than ToDictionary to generate a Lookup<K,V>:
var dict = Assembly.GetExecutingAssembly()
.GetTypes()
.SelectMany(/* ... */)
.ToLookup(x => x.Key, x => x.Value);
If you prefer to work with something like a Dictionary<K,List<V>> then you could do this:
var dict = Assembly.GetExecutingAssembly()
.GetTypes()
.SelectMany(/* ... */)
.GroupBy(x => x.Key, x => x.Value)
.ToDictionary(g => g.Key, g => g.ToList());

Categories