My VS intellisense does not work when i do lambda queries, like Join, GroupJoin, etc. The properties of the second model never appear in the suggestions. I'm sorry for my english :)
See the images:
As #JeroenMostert said, this is a known bug. If you really want the intellisense, you can specify your types; with result2 you'll get intellisense.
You just have to decide if having to explicitly set your types is worth it, especially as it means you can't really return an anonymous object.
Personally, I don't think making your code more verbose is worth it as not having intellisense won't prevent you from setting up your lambda.
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace ConsoleApp1
{
class Program
{
static void Main(string[] args)
{
var people = new List<Person>();
var employees = new List<Employee>();
var result = employees.Join(people, x => x.Id, y => y.Id, (x, y) => new JoinedItem{ Id = x.Id, Name = y.Name });
var result2 = employees.Join<Employee, Person, int, JoinedItem>(people, x => x.Id, y => y.Id, (x, y) => new JoinedItem { Id = x.Id, Name = y.Name });
}
}
public class Person
{
public int Id { get; set; }
public string Name { get; set; }
}
public class Employee
{
public int Id { get; set; }
public string Name { get; set; }
}
public class JoinedItem
{
public int Id { get; set; }
public string Name { get; set; }
}
}
Apparently there is a workaround, by placing the second key selector and the result selector between brackets ().
class Person
{
public string Name { get; set; }
}
class Pet
{
public string Name { get; set; }
public Person Owner { get; set; }
}
public static void JoinEx1()
{
Person magnus = new Person { Name = "Hedlund, Magnus" };
Person terry = new Person { Name = "Adams, Terry" };
Person charlotte = new Person { Name = "Weiss, Charlotte" };
Pet barley = new Pet { Name = "Barley", Owner = terry };
Pet boots = new Pet { Name = "Boots", Owner = terry };
Pet whiskers = new Pet { Name = "Whiskers", Owner = charlotte };
Pet daisy = new Pet { Name = "Daisy", Owner = magnus };
List<Person> people = new List<Person> { magnus, terry, charlotte };
List<Pet> pets = new List<Pet> { barley, boots, whiskers, daisy };
var query =
people.Join(pets,
person => person,
(pet => pet.Owner), // intellisense here
((person, pet) => // intellisense here
new { OwnerName = person.Name, Pet = pet.Name }));
Afterwards the brackets can be removed, but intellisense helps a lot on complicated object structures.
Related
I have question about how I could use Linq grouping the same combination that the list has then relate to a two list.
Example:
I have theses classes.
public class PetCategoryOwner
{
public string PetCategory { get; set; }
public string Owner { get; set; }
}
public class PetCategoriesOwners
{
public IEnumerable<string> PetCategories { get; set; }
public IEnumerable<string> Owners { get; set; }
}
The example data.
Owner
Pet Category
Higa
Terry
Higa
Charlotte
Oliver
Terry
Oliver
Charlotte
Oliver
Chausie
Price
Chausie
Liam
Terry
Liam
Chartreux
var petCategoryOwner = new List<PetCategoryOwner>()
{
new PetCategoryOwner { Owner = "Higa", PetCategory = "Terry"},
new PetCategoryOwner { Owner = "Higa", PetCategory = "Charlotte"},
new PetCategoryOwner { Owner = "Oliver", PetCategory = "Terry"},
new PetCategoryOwner { Owner = "Oliver", PetCategory = "Charlotte"},
new PetCategoryOwner { Owner = "Oliver", PetCategory = "Chausie"},
new PetCategoryOwner { Owner = "Price", PetCategory = "Chausie"},
new PetCategoryOwner { Owner = "Liam", PetCategory = "Terry"},
new PetCategoryOwner { Owner = "Liam", PetCategory = "Chartreux"}
};
Expected values
Owner
Pet Category
Group
Higa
Terry
A
Higa
Charlotte
A
Oliver
Terry
A
Oliver
Charlotte
A
Oliver
Chausie
B
Price
Chausie
B
Liam
Terry
C
Liam
Chartreux
C
var petCategoriesOwners = new List<PetCategoriesOwners>()
{
new PetCategoriesOwners()
{
PetCategories = new List<string>() { "Terry", "Charlotte" },
Owners = new List<string>() { "Oliver", "Higa" }
},
new PetCategoriesOwners()
{
PetCategories = new List<string>() { "Chausie" },
Owners = new List<string>() { "Oliver", "Price" }
},
new PetCategoriesOwners()
{
PetCategories = new List<string>() { "Chartreux", "Terry" },
Owners = new List<string>() { "Liam" }
}
}
In order to solve your problem you need to do two steps: group by owners and merge owners based on the fact other group's set is a subset of current owner. You can try to achieve it by running below LINQ query:
public class PetCategoriesOwners
{
public List<string> PetCategories { get; set; }
public List<string> Owners { get; set; }
}
var petCategoriesOwners = petCategoryOwner
.GroupBy(x => x.Owner)
.Select(x => new
{
Owner = x.Key,
Categories = x.Select(y => y.PetCategory)
})
.OrderBy(x => x.Categories.Count())
.Aggregate(new List<PetCategoriesOwners>(), (acc, current) =>
{
var currentCategories = current.Categories.ToList();
var matches = acc.Where(group => group.PetCategories.All(x => currentCategories.Contains(x)));
foreach(var match in matches)
{
match.Owners.Add(current.Owner);
currentCategories = currentCategories.Except(match.PetCategories).ToList();
}
if (currentCategories.Any())
{
acc.Add(
new PetCategoriesOwners() {
Owners = new List<string>() { current.Owner },
PetCategories = currentCategories
});
}
return acc;
});
So it's important to group by Owner, process groups in ascending order in terms of length. The Aggregate method basically tries to find if previosly entered item overlaps with currently processed one. If it happens then we take those intersecting elements, add owner there and remove those from current element. If any element is left then we create own group for such owner.
Edit: .NET Fiddle
Note: This answer is based on a false interpretation of the question post. In it, I have assumed that petCategoriesOwners is an input and that the content of the table containing the Group column is the expected output.
You could achieve what you want to do by using a combination of SelectMany(), .Select(), .Where() and .Contains() from System.Linq.
Slightly simplified, by using ints for the group value rather than char, this is a possible implemenation:
public class GroupedOwnerAndPetCategory
{
public string Owner { get; set; }
public string PetCategory { get; set; }
public int Group { get; set; }
}
var grouped = petCategoriesOwners
.SelectMany((ownerPetMix, index) => petCategoryOwner
.Where(ownerPetPair =>
ownerPetMix.Owners.Contains(ownerPetPair.Owner) &&
ownerPetMix.PetCategories.Contains(ownerPetPair.PetCategory))
.Select(ownerPetPair => new GroupedOwnerAndPetCategory
{
Owner = ownerPetPair.Owner,
PetCategory = ownerPetPair.PetCategory,
Group = index
}))
.ToList();
Using your example input, grouped contains the following entries:
Higa Terry 0
Higa Charlotte 0
Oliver Terry 0
Oliver Charlotte 0
Oliver Chausie 1
Price Chausie 1
Liam Terry 2
Liam Chartreux 2
Example fiddle here.
I have two classes Person and Animal
using System;
using MongoDB.Bson;
using MongoDB.Bson.Serialization.Attributes;
namespace MongoTesting.Documents
{
public class Person
{
[BsonId]
[BsonRepresentation(BsonType.String)]
public Guid PersonId { get; set; } = Guid.NewGuid();
public Guid PetId { get; set; } = Guid.Empty;
public string Name { get; set; } = "Person";
}
}
using System;
using MongoDB.Bson;
using MongoDB.Bson.Serialization.Attributes;
namespace MongoTesting.Documents
{
public class Animal
{
[BsonId]
[BsonRepresentation(BsonType.String)]
public Guid AnimalId { get; set; } = Guid.NewGuid();
public bool IsMammal { get; set; }
public string Description { get; set; } = "Animal";
}
}
Which are serialized into IMongoCollections
public IMongoCollection<Person> PersonCollection { get; set; }
public IMongoCollection<Animal> AnimalCollection { get; set; }
...
PersonCollection = Database.GetCollection<Person>("PersonCollection");
AnimalCollection = Database.GetCollection<Animal>("AnimalCollection");
Using the C# 2.7.0 MongoDB .Net driver and the MongoDB 3.4 mongod server daemon.
I am trying to write a specific type of query given this setup, specifically the one in the SSCCE below
using System;
using System.Linq;
using System.Collections.Generic;
using MongoTesting.Documents;
using MongoTesting.DatabaseInterface;
using MongoDB.Driver;
namespace MongoTesting
{
class Program
{
static void Main(string[] args)
{
MongoInterface _mongo = new MongoInterface();
_mongo.Open();
//CreateDocuments(_mongo);
Console.Out.WriteLine("Total Persons: " + _mongo.PersonCollection.CountDocuments(Builders<Person>.Filter.Empty));
Console.Out.WriteLine("Total Animals: " + _mongo.AnimalCollection.CountDocuments(Builders<Animal>.Filter.Empty));
var peopleWithMammalianPetsQuery =
from person in _mongo.PersonCollection.AsQueryable()
join animal in _mongo.AnimalCollection.AsQueryable() on person.PetId equals animal.AnimalId
where animal.IsMammal
select person;
var peopleWithMammalianPets = peopleWithMammalianPetsQuery.ToList();
peopleWithMammalianPets.ForEach(person => { Console.Out.WriteLine("Person: " + person.Name); });
Console.ReadLine();
}
public static void CreateDocuments(MongoInterface _mongo)
{
Animal dog = new Animal() { IsMammal = true, Description = "Dog" };
Animal cat = new Animal() { IsMammal = true, Description = "Cat" };
Animal bird = new Animal() { IsMammal = false, Description = "Bird" };
Animal snake = new Animal() { IsMammal = false, Description = "Snake" };
Person bob = new Person() { PetId = dog.AnimalId, Name = "Bob" };
Person sue = new Person() { PetId = cat.AnimalId, Name = "Sue" };
Person sam = new Person() { PetId = bird.AnimalId, Name = "Sam" };
Person eve = new Person() { PetId = snake.AnimalId, Name = "Eve" };
_mongo.PersonCollection.InsertMany(new List<Person>() { bob, sue, sam, eve });
_mongo.AnimalCollection.InsertMany(new List<Animal>() { dog, cat, bird, snake });
}
}
}
Where MongoInterface represents a class which can connect to the MongoDB server, get access to the IMongoDatabase, and manipulate PersonCollection and AnimalCollection.
My specific issue with the code above, is that, when ran the following exception is thrown during the execution of the peopleWithMammalianPetsQuery.
An unhandled exception of type 'System.NotSupportedException' occurred in
MongoDB.Driver.dll
Additional information: $project or $group does not support {document}.
I have searched around and I cannot find something the exactly duplicates this issue and solves it. There are many reports of the exception message though they all seem to differ in the usage which produced it (even some seem to have been fixed). I have also seen multiple posts showing very similar code (specifically a join) working without issue.
How can I perform the query described above?
Is is possible to have a linq query that populates a class with List for any outer join subqueries?
I've tried various variations of this, but can't get it to work.
Another option would be to populate the class by having more queries, but that would be bad performance wise.
Here's an example, where I try to populate MyClass, using a single query
var result = from p in PersonTable
join cars in CarTable on p.id equals cars.id_person into carsGroup.DefaultIfEmpty()
select new MyClass
{
Person = new Person
{
Id = p.id,
Name = p.name
},
Cars = new List<Car>()
{
Id = carsGroup....??
}
}
public class MyClass
{
public Person Person { get; set; }
public List<PersonCar> Cars { get; set; }
}
public class Person
{
public int Id { get; set; }
public string Name { get; set; }
}
public class PersonCar
{
public int Id { get; set; }
pubint int IdPerson {get; set;}
public string Description { get; set; }
}
The LINQ query you have provide is incorrect. The following is a Test that will demonstrate functionality that you're probably looking for:
[TestMethod]
public void TestMethod1()
{
var PersonTable = new List<Person>
{
new Person
{
Id = 1,
Name = "Test1"
},
new Person
{
Id = 2,
Name = "Test2"
},
};
var CarTable = new List<PersonCar>
{
new PersonCar
{
Id = 1,
IdPerson = 2
},
new PersonCar
{
Id = 2,
IdPerson = 3
}
};
var result = (from person in PersonTable
join cars in CarTable on person.Id equals cars.IdPerson into carsGroup
from args in carsGroup.DefaultIfEmpty()
select new MyClass
{
Person = person,
Cars = carsGroup.ToList()
}).ToList();
Assert.AreEqual(2, result.Count);
Assert.AreEqual(1, result.Count(res => res.Cars.Count == 0));
Assert.AreEqual(1, result.Count(res => res.Cars.Count == 1));
}
Consider the following simple example of Students and Teachers;
// person
public class Person
{
public ObjectId Id { get; set; }
public string Name { get; set; }
public Person() {
Id = ObjectId.GenerateNewId(DateTime.Now);
}
}
// student has a Classroom
public class Student : Person
{
public string Classroom { get; set; }
}
// teacher has a Dictionary<ObjectId, Student> Students
public class Teacher : Person
{
[BsonDictionaryOptions(DictionaryRepresentation.ArrayOfDocuments)]
public Dictionary<ObjectId, Student> Students { get; set; }
public Teacher() {
Students = new Dictionary<ObjectId, Student>();
}
}
class Program
{
static void Main(string[] args)
{
var server = MongoServer.Create("mongodb://localhost/database?safe=true");
var database = server.GetDatabase("sandbox");
var collection = database.GetCollection<Teacher>("teachers");
collection.Drop();
// create students
var s1 = new Student() { Name = "s1", Classroom = "foo" };
var s2 = new Student() { Name = "s2", Classroom = "foo" };
var s3 = new Student() { Name = "s3", Classroom = "baz" };
var s4 = new Student() { Name = "s4", Classroom = "foo" };
// teacher 1
var t1 = new Teacher() { Name = "t1" };
t1.Students.Add(s1.Id, s1);
t1.Students.Add(s2.Id, s2);
collection.Insert(t1);
// teacher 2
var t2 = new Teacher {Name = "t2"};
t2.Students.Add(s3.Id, s3);
collection.Insert(t2);
// add teacher 3
var t3 = new Teacher() {Name = "t3"};
t3.Students.Add(s4.Id, s4);
collection.Insert(t3);
// select via key
var onlyt1 = collection.AsQueryable().Where(t => t.Students.ContainsKey(s1.Id)).ToList();
Console.WriteLine("onlyt1 : {0}", onlyt1.ToJson());
Console.ReadLine();
}
}
I can select via the key (shown above), but how do I find all the teachers who have students with classroom of "foo"? I want to write something like;
// select via value
var shouldBeJustT1andT3 = collection.AsQueryable().Where(t => t.Students.Values.Where(s => s.Classroom == "foo")).ToList();
You can use Any to get any teacher for whom there are students in a given classroom "foo":
List<Teacher> shouldBeJustT1andT3 = collection.Where(
teacher => teacher.Students.Any(student => student.Classroom == "foo")
).ToList();
Edit
Since Mongo's IQueryable isn't supporting Any by default, maybe you could just use Where and Count instead of Any:
List<Teacher> shouldBeJustT1andT3 = collection.Where(
teacher => teacher.Students.Where(student => student.Classroom == "foo").Count() > 0
).ToList();
Can't you have Students of type just ICollection<Person>?
Then you don't need query dictionary's values but flat objects' list, i.e. where s.ID == x && s.Classroom == "blah".
Dictionary makes sense to find object by key only, i.e. t.Students[studentId].
To find teachers: see dbaseman's answer, he's correct.
I got two classes, like:
public class Person
{
public long Id { get; set; }
public string Name { get; set; }
}
public class Vampire
{
public long Id { get; set; }
}
Then, I have two lists, a list of persons and a list of vampires. All vampires are persons.
What I need is two children lists of persons, infected and notInfected. I'm building the two lists with a for, but I know it's possible using linq or something.
Any help?
Something like this:
var vampireIds = new HashSet<long>(vampireList.Select(x => x.Id));
var infectedPersons = personList.Where(x => vampireIds.Contains(x.Id));
var regularPersons = personList.Where(x => !vampireIds.Contains(x.Id));
I would go with something like the following:
void Main()
{
var list = new List<Person>(){ new Person(){ Id = 1 }, new Vampire(){ Id = 2 } };
var infected = list.Where (x => x is Vampire);
var notInfected = list.Except(infected);
}
public class Person
{
public long Id { get; set; }
public string Name { get; set; }
}
public class Vampire : Person
{
}
If only a person can be a Vapire, you could inherit Vampire from Person and then iterate through all persons and see if they are Vampires; if yes -> add to Vampire list, otherwise to non-Vampire list.
Try this:
var people = new List<Person>
{
new Person {Id = 1, Name = "John"},
new Person {Name = "Dave", Id = 2},
new Person {Id = 3, Name = "Sarah"}
};
var vamps = new List<Vampire> {new Vampire {Id = 1}};
var theInfected = people.Where(p => vamps.Select(v => v.Id).Contains(p.Id));
var theAfraid = people.Except(theInfected);
foreach (var person in theInfected)
{
System.Console.WriteLine(person.Name + " Is Infected!");
}
foreach (var person in theAfraid)
{
System.Console.WriteLine(person.Name + " Is Afraid!");
}
Hope it's helpful.