Problem using "Any" method inside "Where" method in Linq MongodbDriver - c#

I'm new in using linq and having some problem . I've got a big collection of type A and a small collection of type B.
I want the list of items in A which their "id" does exist in B.
So here is what I did think could work:
List<string> list = collection_A
.Where(c => collection_B.Any(x => x.MessageId == c.Id))
.Select(c=>c.Id)
.ToList();
I'm using mongoDB linq provider in .Net and the error is : System.ArgumentException: Unsupported filter. The relation is 1-1
Actually I don't know if I should use "Join" in this case or something else.

I'd suggest that you try this:
var messageIds = new HashSet<string>(collection_B.Select(x => x.MessageId).Distinct());
List<string> list =
collection_A
.Where(c => messageIds.Contains(c.Id))
.Select(c => c.Id)
.ToList();

if i understood your problem correctly, the following code will point you in the right direction.
i've used MongoDAL for data access which is just an abstraction around the c# driver.
using System;
using System.Linq;
using MongoDAL;
namespace Example
{
class Person : Entity
{
public string Name { get; set; }
}
class BanRecord : Entity
{
public One<Person> Person { get; set; }
public string ReasonForBan { get; set; }
}
class Program
{
static void Main(string[] args)
{
new DB("testdatabase");
var person1 = new Person { Name = "Person One" };
var person2 = new Person { Name = "Person Two" };
var person3 = new Person { Name = "Person Three" };
person1.Save();
person2.Save();
person3.Save();
var ban1 = new BanRecord
{
Person = person1.ToReference(),
ReasonForBan = "Cause we can!"
};
ban1.Save();
var ban2 = new BanRecord
{
Person = person2.ToReference(),
ReasonForBan = "Cause we can!"
};
ban2.Save();
var bannedPeople = (from b in DB.Collection<BanRecord>()
join p in DB.Collection<Person>() on b.Person.ID equals p.ID into banned
from p in banned
select p).ToArray();
Console.ReadKey();
}
}
}

Related

Add element to a List when delcaring new object

Is there a way to add elements to a List when doing this:
var Foo = new MyClass() {
PropertyList = MyList,
Id = Id,
}
I would like to add elements to PropertyList. For example would be the same as: MyList.Add()
The problem is that i do not have a list called MyList but i rather have elements that i want to append to PropertyList
Updating code based on comments:
var result1 = await query
.GroupBy(c => new {
c.CommissionId, c.ActivityId
})
.Select(grp => new RegistrationStatisticViewModel() {
CommissionId = grp.Key.CommissionId,
CommissionCode = grp.First().Commission.Code,
CommissionDescription = grp.First().Commission.Description,
MinuteWorked = grp.Sum(c => c.MinuteWorked),
ActivityId = grp.Key.ActivityId,
ActivityCode = grp.First().Activity.Code,
ActivityDescription = grp.First().Activity.Description,
})
.ToListAsync();
var grps = from d in result1
group d by d.CommissionId
into grp
select new RegistrationStatisticViewModel() {
CommissionId = grp.Key,
ActivityList = new List < Activity > {
new Activity {
//ActivityId = grp.Select(d => d.ActivityId),
//Code = grp.Select(d => d.ActivityCode),
//Description = grp.Select(d => d.ActivityDescription),
}
},
CommissionCode = grp.First().CommissionCode,
CommissionDescription = grp.First().CommissionDescription,
MinuteWorked = grp.First().MinuteWorked
};
return grps;
To give context:
forget the result1 is just some data i retrieve from my database
Commission is one class and contains:
CommissionId
Code
Description
Activity is one class and contains:
ActivityId ==> type GUID
Code ==> type string
Description ==> type string
Now the var = grps is a LINQ that gets the data and then instatiates a new object (class) new RegistrationStatisticViewModel()
So the tricky part we were discussing before is when i populate ActivityList with multiple activities.
When populating the list if i use .First() or .Select() i would only get one instance and therfore the list would only have one activity.
It worked when using .ToArray() for example if i replace ActivityList with just the ActivityId of type string (so a new property on RegistrationStatisticViewModel that is not a list anymore):
I can do this ActivityId = grp.Select(d2 => d2.ActivityId).ToArray()
And it will give me an array of all the ActivityId linked to that commissionId
I am sorry if this is confusing but it is for me as well. I would thank you if you could help me. No worries if you can't you have already give me very helpful answers, so i thank you for that!
Based on your remarks, I believe this is what you are trying to achieve:
public class PersonContainer
{
public IList<Person> Persons { get; set; }
}
public class Person
{
public string Name { get; set; }
public int Age { get; set; }
}
var personContainer = new PersonContainer
{
Persons = new List<Person>
{
new Person
{
Name = "John Doe",
Age = 28,
},
new Person
{
Name = "Jane Doe",
Age = 27,
},
}
};
Here, the Persons property of PersonContainer is initialized and populated with Person elements during instantiation.

How do get sum() of a property using the .Net neo4j client?

I using neo4j 2.2.1, and I have code like this:
MATCH (n:person)-[r:BUY]->(p:product) WHERE p.name IN ['Bag','Book','Pencil'] RETURN SUM(r.total) AS st, n ORDER BY st DESC LIMIT 1
and I try to convert this code to C#,
class:
public class person
{
public string name { get; set; }
}
public class product
{
public string name { get; set; }
}
public class buy
{
public int total { get; set; }
}
Here is my query
public void search1()
{
var data1 = new[] { Bag, Book, Pencil };
var cypher = client.Cypher
.Match("(n:person)-[r:BUY]->(p:product)")
.Where(" p.name IN {data1}")
.WithParams(new { data1 })
.Return((n, r) => new
{
person1 = n.As<person>(),
buy1 = r.As<buy>(),
})
.OrderByDescending("sum(r.total)").Limit(1); //this is an error
foreach (var result in cypher.Results)
{
result1 = result.person1.name;
total1 = result.buy.total; // I want to get sum(r.total) but I can't
}
}
So, what's wrong in my query, and how do I fix it?
I think you should find this will work as you want:
Cypher.Match("(n:Person)-[r:BUY]->(p:product)")
.Where("p.name in {data1}")
.WithParams(new {data1})
.Return(n => new {
Person = n.As<Person>(),
Total = Return.As<int>("SUM(r.Total)")
})
.OrderByDescending("Total")
.Limit(1)
I don't know where the a parameter you're returning in your original query is coming from, as you don't math on a anywhere.
I think you just need to add a WITH - using an alias for the summed property - to your cypher statement. I've got a similar query where I've done that and it worked.
Like so:
var cypher = client.Cypher
.Match("(n:person)-[r:BUY]->(p:product)")
.Where(" p.name IN {data1}")
.WithParams(new { data1 })
.With("a,r,sum(r.total) as sumtotal")
.Return((a, r) => new
{
person1 = a.As<person>(),
buy1 = r.As<buy>(),
})
.OrderByDescending("sumtotal").Limit(1);

Getting List of Model1 with List of Model2 in it LINQ - MVC 4 EntityFramework 5

I have a requirement where I need to get a List of Model1 (List) using Linq, the Model1 have List of Model2 (List) in it and I need to fetch that also. For this I have created a Linq but m getting following error:
LINQ to Entities does not recognize the method
'System.Collections.Generic.List1 [OurCourse]
ToList[OurCourse](System.Collections.Generic.IEnumerable1
[OurCourse])' method, and this method cannot be translated into a
store expression.
Please refer below for detail:
I have two tables Colleges and Courses, with following columns:
College: ID, Name, Contact, City, Address
Cource: ID, CollegeID, Name, Years
My project have two view models for them, as follows:
public class OurCollege
{
public string Name { get; set; }
public string Contact { get; set; }
public List<OurCourse> MyCourses { get; set; }
}
public class OurCourse
{
public string Name { get; set; }
public int NumberOfYears { get; set; }
}
Here the the query query which I have prepared but I am getting the error:
var colleges = db.Colleges
.Select(cld => new OurCollege()
{
Name = cld.Name,
Contact = cld.Contact,
MyCourses = cld.Course
.Select(crs => new OurCourse()
{
Name = crs.Name,
NumberOfYears = crs.Years
}).ToList()
}).ToList();
How about doing like this:
MyCourses = from crs in cld.Course
select new OurCourse
{
Name = crs.Name,
NumberOfYears = crs.Years
}
Your complete query will look now:
var colleges = db.Colleges
.Select(cld => new OurCollege()
{
Name = cld.Name,
Contact = cld.Contact,
MyCourses = (from crs in cld.Course
select new OurCourse
{
Name = crs.Name,
NumberOfYears = crs.Years
}).ToList()
}).ToList();
Actually LINQ to Entities converts your query into SQL.It doesn't know how to translate ToList() in SQL
An alternate is to change you List<T> to IEnumerable<T> and remove ToList() frim your original code:
public IEnumerable<OurCourse> MyCourses { get; set; }
and in query:
var colleges = db.Colleges
.Select(cld => new OurCollege()
{
Name = cld.Name,
Contact = cld.Contact,
MyCourses = cld.Course
.Select(crs => new OurCourse()
{
Name = crs.Name,
NumberOfYears = crs.Years
})
}).ToList();
For more details visit this Entity Framework ToList() in nested type (LINQ to Entities does not recognize the method)
A foreach loop will work, you can write the query something like this
var colleges = db.Colleges
.Select(x => new OurCollege()
{
CollegeId = x.CollegeId,
Name = x.Name,
Contact = x.Contact
}).ToList();
foreach (var college in colleges)
{
college.MyCourse = db.Course.Where(x => x.CollegeId == college.CollegeId)
.Select(x => new OurCourse()
{
Name = x.Name,
NumberOfYears = x.Years
}).ToList()
}

OrderBy selector fails while projecting anonymous type

When using Entity Framework 5, why would this work?
var query = Categories.Select(c => new
{
Products = c.Products.OrderBy(p => p.Name)
});
While this won't?
Func<Product, string> selector = p => p.Name;
var query = Categories.Select(c => new
{
Products = c.Products.OrderBy(selector)
});
The thrown exception is: Unsupported overload used for query operator 'OrderBy'.
The query variable name hints that you may be using Entity Framework, LINQ to SQL or some other IQueryable<T> based API. You are passing the selector as Func. The underlying query provider cannot translate Func to SQL (or any other language, whatever you may be using).
Change the type of selector to Expression<Func<Product, string>> (the rest can remain the same, because lambda expressions can be interpreted either as a delegate or an expression tree. That's why you can's use var with lambdas - the compiler just can't tell if you want the lambda to be a delegate or expression tree) and see if that fixes your problem. You haven't provided enough information for me to be 100% sure, but it should. The OrderBy overloads accepting an Expression should be able to walk the expression tree and translate it to the underlying query. It's somewhat a guess, but I hope it helps you.
Making selector an Expression<Func<Product, string>> won't work directly and will not compile because c.Products is not an IQueryable<T>. It is just a collection type implementing only IEnumerable<T>. Enumerable.OrderBy does not accept an expression as parameter, only a delegate.
But EF still needs an expression, not a delegate. The trick is to use AsQueryable() on the navigation collection:
Expression<Func<Product, string>> selector = p => p.Name;
var query = Categories.Select(c => new
{
Products = c.Products.AsQueryable().OrderBy(selector)
});
Here is a working example of the query working okay and not causing a problem. I skipped the database for simplicity and went directly to an in-memory object, which is probably why it's working for me. I think Honza is right, that this is an issue related to the ORM layer. You can run the below example
Here is an example for linqpad:
void Main()
{
var Categories = new List<Category>() { new Category { CategoryName = "CatName", Products = new List<Product>() { new Product { Name = "ProductName1" } } } };
Func<Product, string> selector = p => p.Name;
var sb = new StringBuilder();
var query = Categories.Select(c => new
{
Products = c.Products.OrderBy(selector)
});
foreach (var x in query)
{
sb.AppendLine(x.Products.First().Name);
}
Console.WriteLine(sb.ToString());
Console.Read();
}
public class Product
{
public string Name { get; set; }
}
public class Category
{
public string CategoryName { get; set; }
public List<Product> Products { get; set; }
}
// Define other methods and classes here
Here is a version for visual studio console app:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace ConsoleApplication2
{
class Program
{
static void Main(string[] args)
{
var Categories = new List<Category>() { new Category { CategoryName = "CatName", Products = new List<Product>() { new Product { Name = "ProductName1" } } } };
Func<Product, string> selector = p => p.Name;
var sb = new StringBuilder();
var query = Categories.Select(c => new
{
Products = c.Products.OrderBy(selector)
});
foreach (var x in query)
{
sb.AppendLine(x.Products.First().Name);
}
Console.WriteLine(sb.ToString());
Console.Read();
}
public class Product
{
public string Name { get; set; }
}
public class Category
{
public string CategoryName { get; set; }
public List<Product> Products { get; set; }
}
}
}

LINQ joining values from different classes

I am new to LINQ and sorry if my question have been asked
I have 2 classes
public class Person
{
int ID {get;set;}
string FirstName {get;set;}
string LastName {get;set;}
}
and
public class House
{
int ID {get;set;}
string Address {get;set;}
string ZipCode {get;set;}
int PersonId {get;set;}
}
I am saving the list of houses in a IEnumerable List
IEnumerable<House> ListHouses = GetAllHouses();
GetAllHouses return the list of houses from the database
I want to use Lamda select in LINQ in order to do the following
var st = ListHouses .Select(h => new
{
id = h.ID,
Address= h.Address,
Zip= h.ZipCode ,
PersonFirstName = GetPersonByID(h.PersonId ).FirstName,
PersonLastname = GetPersonByID(h.PersonId ).lastname
});
Where GetPersonByID returns an object of Type Person that has the given ID. and then I take his first name and last name.
My question is this:
Instead of Getting the Person 2 times for the variables (personFirstName and PersonLastName) Is there a way I can get it one time and then used it. Something like
PersonForId = GetPersonByID(h.PersonId)
PersonFirstName = PersonLastName.FirstName,
PersonLastname = PersonLastName.lastname
I'm looking for something similar to Join in SQL where you join a value from another table.
Thanks you very much for any help
You're extremely close! Using your code (and making all properties on House and Person public), here is a method using the LINQ Join method:
var st = GetAllHouses().Join(GetAllPersons(),
outerKey => outerKey.PersonId,
innerKey => innerKey.ID,
(house, person) => new
{
house.ID,
house.Address,
house.ZipCode,
PersonFirstName = person.FirstName,
PersonLastname = person.LastName
});
Note: I would recommend the GetAllPersons() and the GetAllHouses() methods return IQueryable rather than IEnumerable. Doing so will build the expression (including the join), which means LINQ-to-SQL (or Entities) will build a proper SQL statement with the JOIN included, instead of enumerating the collections and then joining.
Additional information on such can be found here: Returning IEnumerable<T> vs. IQueryable<T>
using System;
using System.Linq;
class Customer
{
public int ID { get; set; }
public string Name { get; set; }
}
class Order
{
public int ID { get; set; }
public string Product { get; set; }
}
class Program
{
static void Main()
{
// Example customers.
var customers = new Customer[]
{
new Customer{ID = 5, Name = "Sam"},
new Customer{ID = 6, Name = "Dave"},
new Customer{ID = 7, Name = "Julia"},
new Customer{ID = 8, Name = "Sue"}
};
// Example orders.
var orders = new Order[]
{
new Order{ID = 5, Product = "Book"},
new Order{ID = 6, Product = "Game"},
new Order{ID = 7, Product = "Computer"},
new Order{ID = 8, Product = "Shirt"}
};
// Join on the ID properties.
var query = from c in customers
join o in orders on c.ID equals o.ID
select new { c.Name, o.Product };
// Display joined groups.
foreach (var group in query)
{
Console.WriteLine("{0} bought {1}", group.Name, group.Product);
}
}
}
Output
Sam bought Book
Dave bought Game
Julia bought Computer
Sue bought Shirt

Categories