Concat two Ienumerables of different types - c#

I have two instances of IEnumerable<T> (with the Different T). I want to combine both of them .
IEnumerable<ClassA>
IEnumerable<ClassB>
Both in CLass A and ClassB i have one common property .Lets say for example it is EmpId ..
Is there a build-in method in .Net to do that or do I have to write it myself?

Assuming you can extract the common property to a common interface, let's say IEmployee, then you could just Cast() and then Concatenate the collections:
classAItems.Cast<IEmployee>().Concat(classBItems)
Note that this will only iterate over those IEnumerables on demand. If you want to create a List containing the content of both sequences at the time you combined them, you can use ToList():
List<IEmployee> all = classAItems.Cast<IEmployee>().Concat(classBItems).ToList();
You can do the same if you only need an array using ToArray().

You can get the concatenated common property easily enough:
var empIds = first.Select(x => x.EmpId).Concat(second.Select(x => x.EmpId));
If this is not what you are after, you will have to be more specific.

You cannot combine two sequences of different types in one sequence, unless you project some of their properties in a common type and create a sequence of this type.
For instance, let we have the two following classes:
public class A
{
public int ID { get; set; }
public string FirstName { get; set; }
public int Age { get; set; }
}
and
public class B
{
public int ID { get; set; }
public string LastName { get; set; }
public int Age { get; set; }
public bool Sex { get; set; }
}
Furthermore, let that you have two sequences, one containing objects of type classA and the other containing objects of type classB. Then, if you declare a third type called classCommon, that would contain the commont properties of classA and classB,
public class classCommon
{
public int ID { get; set; }
public int Age { get; set; }
}
you could try the following:
var result = listA.Select(x => new classCommon { ID = x.ID, Age = x.Age })
.Concat(listB.Select(x => new classCommon{ ID = x.ID, Age = x.Age });

You can concat them with respect to the lowest common denominator, which is object in you case:
IEnumerable<ClassA> e1 = new List<ClassA>();
IEnumerable<ClassB> e2 = new List<ClassB>();
IEnumerable<object> c = e1.Cast<object>()
.Concat(e2.Cast<object>());
But this will not give you much, you will have to runtime check of object type in c collection.
You can create a better common denominator, like some interface IClass which has property EmpId and is implemented by both ClassA and ClassB.
If you do not care about Intellisense, you can try to use dynamic:
IEnumerable<ClassA> e1 = new List<ClassA>() { new ClassA() { A = 1 } };
IEnumerable<ClassB> e2 = new List<ClassB>();
IEnumerable<dynamic> c = e1.Cast<object>()
.Concat(e2.Cast<object>());
int a = c.First().A;
In above code, a will properly result in 1.

Related

How to implement a Linq query by converting entities to DTOs?

There are 3 entities, let's say they are presented in this way:
**1 - entity**
class A
int id ;
int Name;
[Foreign key]
int id_B;
List C;
**2 - entity**
class B
int id ;
int Name;
List A;
**3 - entity**
class C
int id;
int Name;
[Foreign Key]
int id_A;
created an entity DTO (Everything is the same only without foreign keys)
1
class ADTO
int id ;
int Name;
List C;
2
class BDTO
int id ;
int Name;
List A;
3
class CDTO
int id;
int Name;
Now the request looks like this:
var quer = (await _context.A
.Include(b => b.B)
.Include(c => c.C)
.Where(u => u.Id == 1).ToListAsync())
.Select(a => new ADto
{
Id = a.Id,
//How to get information about entity B here by converting to DTO
C = a.C.Select(cdto => new CDTO{ Id = cdto.Id, Name = cdto.Name}).ToList(),
});
How to get information about entity B here by converting to DTO?
If you are querying "A" as your top-level entity then I believe you're just missing a navigation property to it's associated "B". (As it contains the B_Id FK)
1 - entity
public class A
{
public int id { get; set; }
public string Name { get; set; }
[ForeignKey("B")]
public int id_B { get; set; }
public virtual B B { get; set; }
public virtual ICollection<C> Cs { get; set;} = new List<C>();
}
Then when you project your Entities to DTOs using Select:
var query = (await _context.A
.Where(a => a.Id == 1)
.Select(a => new ADto
{
Id = a.Id,
B = new BDTO { Id = a.B.Id /* ... */ },
Cs = a.Cs.Select(c => new CDTO{ Id = c.Id, Name = c.Name}).ToList(),
}).Single();
Note that when using .Select you do not need to use .Include to reference related entities, that is only used to eager load related entities where you want to return an entity graph. (Such as when reading the entities to update values from DTOs) Also, be wary of using any ToList operations prior to using a Select as this will load entities into memory before applying things like filters, and negates the optimization of queries to fill just what Select needs.
});
Normally, I would suggest you implement an interface, that is provided on the constructor of the resulting object
so:
public interface IDbOjbect{
int Id {get;set;}
string Name{get;set;}
}
and then on your DTO object
public Class DtoObject {
public DtoOjbect(IDbOjbect source)
{
//Mapping done here.
}
}
Because then you can implement the interface on any persistence layer object, and the mapping will still work.
Because then the linq query is simply:
DbOjbectList.Select(x => new DtoObject(x));
provided DtoOjbect implements the interface.
your C would look like this:
public partial class C {
public int id {get;set;}
public string Name {get;set;}
}
public partial class C : IDbOjbect {
}
and your CDTO would look like:
public Class CDTO{
public int Id {get;set;}
public string Name {get;set;}
public CDTO(IDbOjbect source)
{
Id = source.Id;
Name = source.name;
}
}
Want to be able to make a DTO from B?
Implement IDbOjbect on your B
by using
public partial class B {
public int id {get;set;}
public string Name {get;set;}
}
public partial class B : IDbOjbect {
}
and now any C or B can be made into a CDTO.
Best part is, you can make a generic method for your B and C, use the "Where" keyword after you generic definition, and then use the Interface as the type, now you can make a single method that does the same thing based on what has the interface implementation, and this will also work for A, if you implement the interface on A.
Without further modification.
So now that you are asking questions, you original question doesn't, lets expand.
Lets say you have a ResumeInfo Object that only B has available.
You then use the NullPointer pattern together with interface segregation principle.
So you create an interface on your resumeInfo class
Example:
public interface IResumeInfo
{
string PlaceOfEmployment {get;set;}
DateTime StartOfEmployment {get;set;}
DateTime? EndOfEmployment {get;set;}
}
Then on your ResumeInfo Object:
public partial class ResumeInfo
{
string PlaceOfEmployment {get;set;}
DateTime StartOfEmployment {get;set;}
DateTime? EndOfEmployment {get;set;}
}
public partial class ResumeInfo : IResumeInfo
{
}
Then lets say you want a single DTO object:
public class DTOUserAndResume
{
public int id {get;set;}
public string Name {get;set;}
string PlaceOfEmployment {get;set;}
DateTime StartOfEmployment {get;set;}
DateTime? EndOfEmployment {get;set;}
public DTOUserAndResume(IDbOjbect source, IResumeInfo resumeInfo)
{
Id = source.Id;
Name = source.name;
PlaceOfEmployment = resumeInfo.PlaceOfEmployment;
StartOfEmployment = resumeInfo.StartOfEmployment ;
EndOfEmployment = resumeInfo.EndOfEmployment ;
}
}
Now on B? I think you said you have resume data, but not on C?
you implement the IResumeInfo on both, but on B, you just get whatever data is there, but on C that has no data? NullOjbect Pattern.
Implement the interfacce, but make it return nothing.
So PlaceOfEmployment is always "" or Null.
Start data is always 1900-01-01 00:00:00 or whatever you want "nothing" to be on a not nullable object, and null on the end of employment.
So you simply claim that the data is the equavilant of a none-existing data set, because, it doesn't have a dataset to provide.
But you dont need to make a new DTO, you can just update the constructor on CDTO, it will also work fine. It might just get a bit confusing in regards to naming and stuff.
This should result in a call that looks like:
C = a.C.Select(cdto => new CDTO{cdto, cdto.ResumeInfo}).ToList();

How to query multiple inherited sub-classes in a single query in Entity Framework

I have a big problem of querying diverse types of inherited subentities in a single query in Entity Framework. My essential aim is providing all of my data model structure in a single JSON string by eager loading. And the tricky point is "the inherited subclasses may contain another inherited subclass". The example seen below will clearly explain the situation.
Assume that I have a simple class structure like this:
public class Teacher
{
public int id { get; set; }
public string fullname{ get; set; }
//navigation properties
public virtual HashSet<Course> courses{ get; set; }
}
public class Course
{
public int id { get; set; }
public string coursename{ get; set; }
//foreign keys
public int TeacherId{ get; set; }
//navigation properties
public virtual Teacher teacher{ get; set; }
public virtual HashSet<Course> prerequisites{ get; set; }
}
Course has some subclasses GradedCourse and UngradedCourse
B1 or B2 may have a list of subentities consists of entities of types B1 or B2.
public class GradedCourse : Course
{
public string gradeType{ get; set; }
}
public class UngradedCourse: Course
{
public string successMetric { get; set; }
}
Now by this structure I want to provide a JSON structure from my WEBApi yielding list of Teacher objects including both GradedCourse and UngradedCourse with their subentities and specific fields. I have a query like this but it does not compile
db.Teachers.Select(t => new
{
t.id,
t.fullName
courses = t.courses.OfType<GradedCourses>()
.Select(g => new
{
id = g.id,
coursename = g.coursename,
prerequisites = g.prerequisites, // this is the list of other subentities
gradeType = g.gradeType
}
).Concat(t.courses.OfType<UngradedCourses>()
.Select(u => new
{
id = u.id,
coursename = u.coursename,
prerequisites = g.prerequisites, // this is the list of other subentities
successMetric= u.successMetric // subclass specific field
}
)
)
}
)
The problem is concating two different types of objects (they have different fields which is not possible for SQL UNION)
How can I handle this? Any help will open my mind. Thanks in advance for the professionals :)
It does not compile because the element type of 2 sets is not the same. So you just need to make them the same before being able to do anything:
db.Teachers.Select(t => new
{
t.id,
t.fullName
courses = t.courses.OfType<GradedCourses>()
.Select(g => new
{
id = g.id,
coursename = g.coursename,
prerequisites = g.prerequisites, // this is the list of other subentities
isGradedCourse = true,
gradeTypeOrMetric = g.gradeType
}).Concat(t.courses.OfType<UngradedCourses>()
.Select(u => new
{
id = u.id,
coursename = u.coursename,
prerequisites = g.prerequisites, // this is the list of other subentities
isGradedCourse = false,
gradeTypeOrMetric= u.successMetric // subclass specific field
}))
//finally select what of your choice
.Select(e => new {
id = e.id,
coursename = e.coursename,
prerequisites = e.prerequisites,
gradeType = e.isGradedCourse ? e.gradeTypeOrMetric : "",
successMetric = e.isGradedCourse ? "" : e.gradeTypeOrMetric
})
});
You still benefit the query being executed on server side without having to pull all teachers to local (and then being able to cast the entities - which is not supported in LinqToEntity query).

Ef many to many linq query that gets objects that have all list members

I am using entity framework. I have two domain models.
public class Animal
{
public int Id { get; set; }
public string Name { get; set; }
public virtual ICollection<Feature> Features { get; set; }
}
public class Feature
{
public int Id { get; set; }
public string Name { get; set; }
public virtual ICollection<Animal> Animals { get; set; }
}
Animal and feature have many to many relationship so I have these tables in DB:
Animal
Feature
AnimalFeature
I get a list
var featureList = new List<Feature> { new Feature { Name = "f1" }, new Feature { Name = "f2" } };
How do i get a list of animals which contains all features in featureList
Any help will be appreciated. I'm stuck on this one really bad.
You can try something like this:
var features = { "f1","f2" };
var query = from a in dc.Animals
where ( from f in a.Features
where features.Contains(f.Name)
select f).Count() == features.Length
select a;
I suggest you use a primitive type collection instead a List of Features because EF only works with primitive types and enum types when you need to check if several elements are contained in a specific set. You can't compare two custom objects in your where condition. Remember this query is going to be translated to sql, and EF doesn't know how to translate the comparison between two objects. So, as I said before, you should use primitive types or enum types in this kind of query. You can select the names or Ids of the features you need to have the animals and compare with that collection as I did above.
Anyway, I think you can also do this another query using the Any extension method:
var featureList = new List<Feature> { new Feature { Name = "f1" }, new Feature { Name = "f2" } };
var query = from a in dc.Animals
where ( from f in a.Features
where featureList.Any(e=>e.Name==f.Name)
select f).Count() == featureList.Count
select a;
But I particularly prefer the first variant.

IQueryable<a> to list<b> type conversion, when a & b having similar signature using LINQ

I'm having some basic problem with linq, I tried to google but didn't get the desired reuslt.
Lets say We have two class with almost similar signature like these:
public class A
{
public int a { get; set; }
public int b { get; set; }
public int c { get; set; }
}
public class B
{
public int x { get; set; }
public int y { get; set; }
public int z { get; set; }
}
Here A is data context class which is generated by the framework while creating the .edmx file & B is my custom class to carry data to UI. I'm querying the data base using A & getting a iqueryable list. Now I need to convert this list into another list of type B. How can I do that?
You need to use Select to project from one type to another, then ToList to create a list:
// Property names changed to follow .NET naming conventions
var list = query.Select(a => new B { X = a.A, Y = b.B, Z = c.C })
.ToList();
The compiler and runtime wouldn't care if your types had the same properties (not that they do in this case - they have different names) - they're still entirely separate types.
You will use projections when selecting.
var myQuery = (from a in context.A
select new B{
x = a.A,
y = a.B,
z = a.C
});
return myQuery.ToList();
Assume your query method returns an IEnumerable<A> you can do the following.
IEnumerable<A> myList = SomeMethod();
var myModelList = myList.Select(a => new B(){x = a.A, y = a.B, z = a.C});

Order Collection by child child collection

I have the following objects
public class ObjectA{
public string Name { get; set; }
public ICollection<ObjectB> ObjectBCollection { get; set; }
}
public class ObjectB{
public string Name { get; set; }
public ICollection<ObjectC> ObjectCCollection { get; set; }
}
public class ObjectC{
public string Name { get; set; }
public DateTime Date { get; set; }
public InternalType Type { get; set; }
}
public enum InternalType {
TypeA,
TypeB,
TypeC
}
Now i want to order a List of ObjectA by the Dates in ObjectC that are closests to the current date. To make things a little more interesting, I also want it sorted by the InteralType. But I want TypeB have priority over TypeA and TypeC comes last.
I was thinking of creating an extra value that presents the integer value of the timespan between the current date and the Date property and multiply that by the Type property, but I can't figure out how to actually do that.
First of all if you want specific ordering in Enum you can do this:
public enum InternalType : int
{
TypeA = 2,
TypeB = 1,
TypeC = 3
}
Next if I understood your question correctly you have:
var collection = new List<ObjectA>();
which you need to sort by ALL dates in child elements. You can use Linq expressions for this:
List<KeyvaluePair<DateTime, ObjectA>> collectionWithDates = collection
.Select
(
objectA => new KeyValuePair<DateTime, ObjectA>
(
objectA
.SelectMany(a => a.ObjectBCollection)
.SelectMany(b => b.ObjectCCollection)
.OrderBy(c => c.Date).ThenBy(c => (int)c.Type)
.Last()
.Date,
objectA
)
)
.ToList();
To get ordered list of ObjectA you just need to:
var orderedCollection = collectionWithDates
.OrderBy(d => d.Key)
.Select(d => d.value)
.ToList();
I believe this should work. However I didn't tested it.
Correct me in comments if I misunderstood requirements.
Last thing to add - as far as I know, Linq expressions are not the fastest way to sort collections.

Categories