Linq with DbContext - item with two keys complicate scenario - c#

I have this scenario:
public class A
{
public string Name{get;set;}
public int Number{get;set;}
}
public class B
{
public A AInstance{get;set;}
}
and I have this function:
public List<B> GetBFromA(List<A> aList)
{
...
List<B> database_bInstances = db.GetBInstances();
//here I want linq query that will filter to me the B instances from the database, according to the aList
}
I hope the idea is clear. the scenario is that I get all the B instances from the database and filter all the B according to the list of A in the input. if for some B its A instance(identified by Name and Number exists in the input aList it will stay if not it will removef from the B list).
Edit: I am using entity framework DbContext! those it is not letting me to make:
database_bInstances.Where(b => aList.Any(a => (a.Name == b.AInstance.Name)
&& (a.Number == b.AInstance.Number)))
and it throws exception: Unable to create a constant value of type A. Only primitive types or enumeration types are supported in this context.

If Equals works for A then this will work
List<B> database_bInstances = db.GetBInstances();
List<B> new_list = database_bInstances
.Where(b => aList.Contains(b.AInstance));
If not then do this:
List<B> database_bInstances = db.GetBInstances();
List<B> new_list = database_bInstances
.Where(b => aList.Any(a => b.AInstance.Name == a.Name && b.AInstance.Number == a.Number));

This expression will give you the desired Bs.
database_bInstances.Where(b => aList.Any(a => (a.Name == b.AInstance.Name)
&& (a.Number == b.AInstance.Number)))
While this will work, it does not look to me like the best thing to do, but without more information I can not make a better suggestion.
If you have properly implemented Equals and GetHashCode or if you use an O/R mapper that ensures reference equality for objects representing the same database row you can just use Contains instead of Any.
database_bInstances.Where(b => aList.Contains(b.AInstance))

Related

Linq in from ObservableCollection in ObservableCollection

I am tring to get the value "thisValueIwant". Is there any possibility to get this value so easy? Or Maybe there is another solution for these 2 ObservableCollection
public class Foo
{
public int number { get; set; }
public ObservableCollection<FooInFoo> Details { get; set; }
}
public class FooInFoo
{
public string thisValueIwant { get; set; }
}
public class TestClass
{
public void Test()
{
ObservableCollection<Foo> FooCollection = new ObservableCollection<Foo>();
FooCollection.Add(new Foo{
number =1,
Details = new ObservableCollection<FooInFoo>{ new FooInFoo {thisValueIwant = "very important string"}}
});
string x = (from f in FooCollection
where f.number == 1
select ???)
}
}
Since ObservableCollection<FooInFoo> Details is a collection, you have to decide which details you want: the first, last, any or all.
Assuming you want the first:
var d = FooCollection.Where(f => f.Number == 1).FirstOrDefault()?.Details.FirstOrDefault()?.thisValueIwant;
Or the last:
var d = FooCollection.Where(f => f.Number == 1).FirstOrDefault()?.Details.LastOrDefault()?.thisValueIwant;
Or all (materialized as an array):
var ds = FooCollection.Where(f => f.Number == 1).FirstOrDefault()?.Details.Select(d => d.thisValueIwant).ToArray();
An ObservableCollection<T> is a Collection<T> which implements IEnumerable<T>. Therefore the fact that your FooCollection is an observable collection is not important, you can regard it as a sequence of Foo, an IEnumerable<Foo> and equally an IEnumerable<FooInFoo>
Your code will be like (sorry, I only know how to write in Method format)
In baby steps:
IEnumerable<Foo> AllFooWithNumber1 = FooCollection
.Where(foo => foo.Number == 1);
If you are certain there is exactly one continue with:
Foo fooWithNumber1 = AllFooWithNumber1.Single();
Consider using SingleOrDefault if you are not certain that there is one.
Once you have the Foo that you want, you can select the Details:
IEnumerable<FooInFoo> detailsOfFooWithNumber1 = fooWithNumber1.Details;
FooInFoo detailIWant = detailsOfFooWithNumber1
.Where(detail => some expression that uses detail...)
.SingleOrDefault();
string thisValueIWant = defailtIWant.thisValueIWant;
Or in one statement:
string thisValueIWant = FooCollection
.Where(foo => foo.Number == 1)
.Single()
.Details
.Where(detail => ...)
.Single()
.thisValueIWant;
Problems might arise if you are not certain there is one Single element.
If you want to check foo.Number for a given value AND all details for some predicate, consider using Enumerable.SelectMany. This is used whenever you have sequences of sequences (arrays within arrays). With SelectMany you enumerate over all these inner arrays as if it was one array:
IEnumerable<string> valuesIWant = FooCollection
.Where(foo => foo.Number == 1)
.SelectMany(foo => foo.Details)
// now you have one sequence of all FooInFoo that are Details within
// Foo objects with Number 1
.Where(detail => expression that selects the FooInFoo you want)
.Select(detail => detail.thisValueIWant);
You need my ObservableComputations library maybe. Using this library you can code like this:
Expression<Func<string>> expr = () =>
FooCollection
.Filtering(а => f.number == 1)
.FirstComputing().Value
.Using(f => f != null
? f.Details.FirstComputing().Using(fif =>
fif.Value != null ? fif.Value.thisValueIwant : null).Value
: null).Value;
Computing<string> x = expr.Computing();
// x.Value is what you want
x is INotifyPropertyChanged and notifies you about changes of computing result of expr. Do not forget make all properties mentioned in the code above notify of changes through the INotifyPropertyChanged interface.

C# Create a list of objects from an 2 IEnumerable instances containing only those objects with a value found in the second list

I have 2 different classes:
public class ClassOne
{
public string ClassOneID { get; set; }
...
}
public class ClassTwo
{
public string ClassTwoID { get; set; }
...
}
I have IEnumerable instances of each. I want to return a List<ClassOne> that contains only the ClassOne items whose ClassOneID is equal to the ClassTwoID of a ClassTwo object from the second IEnumerable instance (if that makes sense!). I was thinking the following:
var list = new List<ClassOne>();
list.AddRange(classOneEnumerable.Where(o =>
classTwoEnumerable.Select(c => c.ClassTwoID == o.ClassOneID).First()));
This logic is contained within code that is some days off building/testing, so I am not actually able to run it just yet. I am not sure if what I have come up with is actually correct, and was hoping someone could put me right if I am mistaken.
var list = (from classOne in classOneEnumerable
from classTwo in classTwoEnumerable
where classOne.ClassOneID == classTwo.ClassTwoID
select classOne).ToList();
var list2 = (from classOne in classOneEnumerable
join classTwo in classTwoEnumerable
on classOne.ClassOneID equals classTwo.ClassTwoID
select classOne).ToList();
Both queries will yield the same results.
The existing answers are fine if you can handle O(n2). Otherwise I would sort the inner values so that you can get n log(n) performance.
Try this
var list = classOneEnumerable.Where(o => classTwoEnumerable
.Any(c => c.ClassTwoID == o.ClassOneID)))
.ToList();

Select object from nested collection using Linq

I have a class structure something like this:
class MyClass
{
public IEnumerable<AttributeGroup> AttributeGroups { get; set; }
}
class AttributeGroup
{
public IEnumerable<Attribute> Attributes { get; set; }
}
class Attribute
{
public string SomeProp { get; set; }
}
I need to get all 'Attributes' which has a specific 'SomeProp' value no matter which Attribute Group they belong to.
For example, SomeProperty== 'A' can be found in both MyClassObj.AttributeGroup[0] and MyClassObj.AttributeGroup[5] and I need to write a Linq (or something like that) to fetch two objects from these two different attributegroups.
Any suggestion?
First select all attributes from all attribute groups, then only select the ones with your property.
IEnumerable<Attribute> attributes =
myClassInstance
.AttributeGroups
.SelectMany(x => x.Attributes)
.Where(x => x.SomeProperty == 'A');
Other Linq-style syntax:
IEnumerable<Attribute> attributes =
from attributeGroup in myClassInstance.AttributeGroups
from attribute in attributeGroup.Attributes
where attribute.SomeProperty == 'A'
select attribute;
Have a look at SelectMany (http://msdn.microsoft.com/en-us/library/bb534336.aspx).
For example:
myClassObjs.SelectMany(o => o.AttributeGroups.SelectMany(g => g.Attributes)).Where(a => a.SomeProp == "A")
This line selects all Attribute objects of all AttributeGroups of all MyClass objects where SomeProp equals "A". a in the lambda expression for Where is of type Attribute.
Your example isn't clear; I can't tell what you mean by, "two object from these two different attributegroups". I'll guess that you want the groups that have attributes with the property in question:
from g in MyClassObj.AttributeGroups
where g.Any(attr => attr.SomeProperty == "A")
select g

Casting to a derived type in a LINQ to Entities query with Table Per Hierarchy inheritance

I have a LINQ to entities model with Table Per Hierarchy inheritance. I have a query over the base type, and I want to do specific type-dependent logic. For example:
IQueryable<BaseType> base = ...
// this works fine
var result = base.Select(b => b is DerivedType1 ? 1 : 2).ToList();
// this doesn't compile to SQL
var result2 = base.Select(b => b is DerivedType1 ? ((DerivedType1)b).DerivedProperty : null).ToList();
Is there any way to do something like this without processing IQueryables of each derived type separately:
// I'd rather not do this:
var resultA = base.OfType<DerivedType1>().Select(d => d.DerivedProperty);
var resultB = base.OfType<DerivedType2>().Select(d => default(int?));
var result = resultA.Concat(resultB).ToList();
Direct casting to an entity type like (DerivedType1)b isn't supported with LINQ-to-Entities but the as operator (b as DerivedType1) is, hence you could try:
var result2 = base
.Select(b => b is DerivedType1
? (b as DerivedType1).DerivedProperty
: null)
.ToList();
OfType<DerivedType1>()
will return an IEnumerable, if possible, try to change to base-type to IEnumerable instead of IQueryable, you might en up in some SQL restrictions when using IQueryable.
That is of course if you are not actually quering a database?
You can use EntityFramework.Extended to improve the performance of the query instead of doing 2 round trips to DB.
var resultA = base.OfType<DerivedType1>().Select(d => d.DerivedProperty).Future();
var resultB = base.OfType<DerivedType2>().Select(d => default(int?)).Future();
var result = resultA.Concat(resultB).ToList();
In this case only one round trip to bd is executed.
This framework is very useful for many other things int EF
You could have a method on your base type that's overridden in your derived types to provide the relevant property value.
public class MyBaseClass
{
public virtual int GetSomething()
{
throw new NotImplementedException();
}
}
public class MyDerivedClass1 : MyBaseClass
{
public int SomeProperty { get; set; }
public override int GetSomething()
{
return this.SomeProperty;
}
}
public class MyDerivedClass2 : MyBaseClass
{
public int SomeOtherProperty { get; set; }
public override int GetSomething()
{
return this.SomeOtherProperty;
}
}
Then you could:
var result = base.Select(b => b.GetSomething()).ToList();
Try this, I have never done anything with needing to do this kind of this but this should do it. Also if you use base, first of all don't because it is a keyword but if you must, use #base the # in front of the name denotes that it is not used as a keyword.
var resultA = base.Select(aVar =>
(aVar is DerivedType1) ?
(DerivedType)(((DerivedType1)aVar).DerivedProperty)
:
(DerivedType)(default(int?))
).ToList();

LINQ to select items in collections with differing types

How would one implement LINQ to extract the Guid's from one collection of objects of type A such that they can exclude these Guids from another collection of objects of type B. Object A and Object B both have a Guid field called 'ID."
I have the following:
ObservableCollection<Component> component Component has a
field called ID of type Guid
ObservableCollection<ComponentInformation> ComponentInformationCollection ComponentInformation
has a field called ID of type Guid
My implementation:
component =>
{
if (component != null)
{
var cancelledComponents = new List<ComponentInformation>();
foreach (Component comp in component)
{
cancelledComponents.Add(new ComponentInformation() { ID = comp.ID });
}
this.ComponentInformationCollection.Remove(cancelledComponents);
}
});
I believe there is a more elegant solution which I've been working at to solve but the issue I keep running into is creating a 'new ComponentInformation' such that the types do not give me an error.
====== FINAL SOLUTION =======
var cancelledComponentIDs = new HashSet<Guid>(component.Select(x => x.ID));
this.ComponentInformationCollection.Remove(
this.ComponentInformationCollection.Where(x => cancelledComponentIDs.Contains(x.ID)).ToList());
Thank you to:
Jason - I used this as a template for my final solution (listed below).
Servy - While I could have used a comparer, I think for this particular scenario a comparer was not neccessary because of its one-time-use type of situation.
ComponentInformationCollection is a Silverlight DependencyProperty that will trigger a INotifyChangedEvent (MVVM pattern) when altered, so the solution above worked best for my situation.
I would do this:
var ids = new HashSet<Guid>(
component.Select(x => x.ID)
);
var keepers = ComponentInformationCollection.Where(x => !ids.Contains(x.ID));
If Component doesn't already define an Equals and GetHashCode that uses the ID to do the compare you can define a comparer such as this:
class ComponentComparer : IEqualityComparer<Component>
{
public int Compare(Component a, Component b)
{
return a.ID.CompareTo(b.ID);
}
public int GetHashCode(Component a)
{
return a.ID.GetHashCode();
}
}
Then you can just use:
var result = componentCollectionA.Except(componentCollectionB, new ComponentComparer());
(written off of the top of my head; may require minor modifications to get it to compile.)
LINQ will allow you to find the GUIDs you need, but LINQ sequences are generally immutable; you'll still need to use some kind of loop to actually change the collection. The trick is getting the correct instances of your original collection that you want to remove.
Implementing one of the equality/comparison interfaces is one way to go, and if you need to compare your objects for equality in multiple places, is definitely the way to go. If you don't want to do that, this should get you what you want:
var removeme = (from x in this.ComponentInformationCollection
join y in component on x.ID equals y.ID
select x).ToList();
removeme.ForEach(x => this.ComponentInformationCollection.Remove(x));
Thinking out loud (meaning I didn't create a project and types and compile this), but how about:
var cancelledComponents = component.Select(c=> new ComponentInformation() {ID = c.ID}).ToList();
cancelledComponents.ForEach(c => ComponentInformationCollection.Remove(c));
There are a number of ways to solve this... this is a pretty simple Linq statement to query the ones you are looking for from the collection.
var keep = typeAList.Where(a => typeBList.FirstOrDefault(b => a.ID == b.ID) == null);
Here is the little test app I put together to demo it.
class Program
{
static void Main(string[] args)
{
List<TypeA> typeAList = new List<TypeA>();
typeAList.Add(new TypeA() { ID = Guid.NewGuid() });
typeAList.Add(new TypeA() { ID = Guid.NewGuid() });
typeAList.Add(new TypeA() { ID = Guid.NewGuid() });
List<TypeB> typeBList = new List<TypeB>();
typeBList.Add(new TypeB() { ID = typeAList[0].ID });
typeBList.Add(new TypeB() { ID = typeAList[1].ID });
//this is the statement
var keep = typeAList.Where(a => typeBList.FirstOrDefault(b => a.ID == b.ID) == null);
}
}
class TypeA
{
public Guid ID { get; set; }
}
class TypeB
{
public Guid ID { get; set; }
}

Categories