For example, I have object:
public class person : RealmObject {
public string firstname { get; set; }
public string secondname { get; set; }
public int age { get; set; }
public string address { get; set; }
}
How to make query that will give me only list of addresses or firstnames + secondnames?
Or firstname and age.
In general Select is not currently supported...
As long as your Select is projecting a RealmObject, it is supported, but you are asking for a projection that changes the type from RealmObject to another type and that is not support:
https://github.com/realm/realm-dotnet/issues/916
If you really need to break object's connection to the Realm database, you can take your base query to a list (.ToList) and then perform a Select projection on the result:
realm.All<Dog>().ToList().Select(dog => dog.Name);
Update:
For your first + second name question, I would add that as a read-only property on your RealmObject subclass. Since that property has a custom setter/getter it will not be persisted in the Realm data store.
public class person : RealmObject
{
public string firstname { get; set; }
public string secondname { get; set; }
public int age { get; set; }
public string address { get; set; }
public string name { get { return firstname + " " + secondname; } }
}
Then you can do something like:
var realm = Realms.Realm.GetInstance("stackoverflow.db");
var me = new person() { firstname = "Sushi", secondname = "Hangover", age = 99, address = "Local sushi bar" };
realm.Write(() => realm.Add<person>(me));
var user = realm.All<person>().Where((person p) => p.firstname == "Sushi" && p.secondname == "Hangover").FirstOrDefault();
Console.WriteLine($"{user.name} is {user.age} years old and is currently at {user.address}");
Output:
Sushi Hangover is 99 years old and is currently at Local sushi bar
Update2:
If you are looking to iterate through a returned collection of RealmObjects:
var users = realm.All<person>().Where((person p) => p.age < 40);
Console.WriteLine($"There are {users.Count()} users under 40 years of age and they are located at:");
foreach (var user in users)
{
Console.WriteLine($"{user.firstname} is currently at {user.address}");
}
You could try something like the below:
var realm = Realm.GetInstance();
var addresses = realm.All<person>()
.ToList()
.Select(person => person.address)
.ToList();
var firstAndSecondNames = realm.All<person>()
.ToList()
.Select(person => new
{
FirstName = person.firstName,
SecondName = person.secondName
})
.ToList();
Related
Using C# MVC5 Visual studio 2015.
I have a method that contains the following code:
public List<OffersOnPropertyViewModel> Build(string buyerId)
{
var filtered = _context.Properties.Where(x => x.Offers.Any(c => c.BuyerUserId == buyerId)).ToList();
var model = filtered.Select(c =>
{
var item = new OffersOnPropertyViewModel()
{
PropertyType = c.PropertyType,
NumberOfBedrooms = c.NumberOfBedrooms,
StreetName = c.StreetName,
Offers = c.Offers.Where(d => d.BuyerUserId == buyerId).Select(x => new OfferViewModel
{
Id = x.Id,
Amount = x.Amount,
CreatedAt = x.CreatedAt,
IsPending = x.Status == OfferStatus.Pending,
Status = x.Status.ToString(),
BuyerUserId = x.BuyerUserId
}),
};
return item;
}).ToList();
//TODO: refactor, shorten linq, duping where clause
return model;
}
Here is the model:
public class Property
{
[Key]
public int Id { get; set; }
[Required]
public string PropertyType { get; set; }
[Required]
public string StreetName { get; set; }
[Required]
public string Description { get; set; }
[Required]
public int NumberOfBedrooms { get; set; }
[Required]
public string SellerUserId { get; set; }
public bool IsListedForSale { get; set; }
public ICollection<Offer> Offers { get; set; }
}
In the DB Offers table has the property id as its FK.
The method fails at runtime saying the Value cannot be null.
When I step through I notice the filtered results (in the example its 1 result), is saying offers is null. Although the query just filtered the results based on "x.Offers".
I simply need a way to retrieve a list of property's that have offers made by the buyerId provided. Is my approach wrong? or am i missing a one liner?
Thanks
You will need to add Include() to your LINQ query to bring in child objects, as follows:
var filtered = _context.Properties.Include("Offers")
.Where(x => x.Offers.Any(c => c.BuyerUserId == buyerId)).ToList();
The reason your filter works with the Any() is because when generating the SQL query, this part forms the WHERE clause and is not included in the SELECT.
I have no problems passing Linq query results to views via a viewmodel with or without a .Select() method as long as I am selecting only a single column. When I tried using the .Select() option with a renamed column like this:
var custodians = _custodian.Contacts
.Where(c => !(c.personid.StartsWith("RMR") || c.personid.StartsWith("GMS")))
.Select(c => new { c.contactid, name = c.lname + ", " + c.fname})
.ToList();
it creates creates a System.Collections.Generic.List<<>f__AnonymousType1<int, string>> type list
I have an existing viewModel that I am passing to my view:
public class AssetViewModel
{
public string PsgcTagNumber { get; set; }
public string[] AssetAttributes { get; set; }
public string Message { get; set; }
public Asset Asset { get; set; }
public Location Location { get; set; }
public string Custodian { get; set; }
public ?????? AllContacts { get; set; }
}
What I cant figure out is the datatype to use for the AllContacts property of the viewModel.
Anyone point me in the right direction?
You'll need to define a class.
public class Contact {
public int contactid {get;set;}
public string name {get;set;}
}
.Select(c => new Contact { contactid = c.contactid, name = c.lname + ", " + c.fname})
public Contact[] AllContacts { get; set; }
Or just leave the entity alone, without doing a Select method on your query, and use it in your viewmodel - you could add a FormattedName property or something like that to handle your name.
Your anonymous type result is exactly what your select is producing new { c.contactid, name = c.lname + ", " + c.fname} - a list of int<->string or a list of { int contactid, string name }
If you want to use an existing Model, like your AssetViewModel.AllContacts you need to define its type first, as #Joe Enos stated and then update your query a little bit:
var vm = new AssetViewModel
{
PsgcTagNumber =...,
...,
Custodian =...,
AllContacts = _custodian.Contacts
.Where(c => !(c.personid.StartsWith("RMR") || c.personid.StartsWith("GMS")))
.Select(c => new Contact { c.contactid, name = c.lname + ", " + c.fname})
.ToList();
}
So then you have it: your view model, initiated and ready to be passed forward
I got a List that contains all the employees, now I need to dig in to a specific employee on a new page. I want to get all the values from the employee where the ID is 1 for example. Is there a sollution for this in LINQ?
It's practically a Query SELECT * FROM Employee WHERE id = 1;
class Employee
{
public int EmployeeID { get; set; }
public string LastName { get; set; }
public string FirstName { get; set; }
public string Title { get; set; }
public string TitleOfCourtesy { get; set; }
public DateTime BirthDate { get; set; }
public DateTime HireDate { get; set; }
public string Address { get; set; }
public string City { get; set; }
public string Region { get; set; }
public string PostalCode { get; set; }
public string Country { get; set; }
public string HomePhone { get; set; }
public string Extension { get; set; }
//public Image Photo { get; set; }
public string Notes { get; set; }
public int ReportsTo { get; set; }
public string PhotoPath { get; set; }
}
I tried it like this but it doesn't work:
List<Employee> employees = Database.getEmployees();
var uniqUsers = employees.Where(x => employees.Contains(x.EmployeeID == 1)).ToList();
Where employee is type of IEnumerable<Employee>
If you are expecting 1 record:
var result = employee.FirstOrDefault(x => x.EmployeeID == 1); // Returns Employee
If you are expecting more than 1 record:
var result = employee.Where(x => x.EmployeeID == 1); // Return IEnumerable<Employee>
Please note, when using FirstOrDefault if there is no items in your collection (or doesn't match your lambda) then it will return default T which in your case will be Employee and it will be null.
If you want a "single" item that meets that critera use the Single Linq statement:
Employee employee = employees.Single(e => e.EmployeeID == 1);
or
Employee employee = employees.SingleOrDefault(e => e.EmployeeID == 1);
if you want the query to return null instead of throwing an exception if there is not an item in the list that meets that criteria.
Let EmployeeList is the current List of Employees. You can use LINQ to filter the required details as like the specified query by using this(IT will give you all sublist satisfies the specified condition):
int empIdToSearch=1;
List<Employee> FilteredList=EmployeeList.Where(x=>x.EmployeeID ==empIdToSearch).ToList();
If the EmployeeID is unique then there will be one item in the list with particular ID, You can use FirstOrDefault to get the First item from the collection that satisfies the condition.ie.,
Employee EmployeeObject= FilteredList.FirstOrDefault(x => x.EmployeeID == empIdToSearch);
The concept that you need to get is how most linq queries operate.
When you say .Where(x => x.EmployeeID == 1) then x is a single empolyee as if you said:
foreach(Employee x in employees)
{
if(x.EmployeeID == 1)
// take it
}
So the correct syntax would be:
List<Employee> uniqUsers = employees.Where(x => x.EmployeeID == 1).ToList();
Single Optional Result:
Employee uniqUser = employees.SingleOrDefault(x => x.EmployeeID == 1);
Single Mandatory Result:
Employee uniqUser = employees.Single(x => x.EmployeeID == 1);
First Optional Result:
Employee uniqUser = employees.FirstOrDefault(x => x.EmployeeID == 1);
First Mandatory Result:
Employee uniqUser = employees.First(x => x.EmployeeID == 1);
We can fetch the records from collection in two ways.
Linq to sql like query
var employee= from emp in employees where emp.ID==1;
Linq to extension methods.
var employee = employees.Where(emp=>emp.ID==1);
Linq supports a query syntax that is closer to SQL.
var employee1 = from employee in employees where employee.EmployeeID == 1 select employee;
foreach (var x in employee1)
{
Console.WriteLine(x.EmployeeID);
}
The compiler converts all query syntax to method syntax. Not all things can be done with query syntax. The 'from' comes before the 'select' so auto-complete is more useful. It is important to note the linq query is not executed until it is used. The foreach loop is where it is first used in this example.
I would like to select a where statement that adds items to a list where only product codes match. I have it so it gets all of the products sold in the sale but I would like there were statement to get only products in this sale.
PS: This is really hard to explain
Model
public class userSales
{
public string Name { get; set; }
public string UserName { get; set; }
public int Sale_Id { get; set; }
public int CostumerID { get; set; }
public string Sale_Date { get; set; }
public string Paid { get; set; }
public Nullable<int> Sale_Cost { get; set; }
public string Discount_Code { get; set; }
public List<SaleProduct> saleProductsList { get; set; }
}
public class SaleProduct
{
public int SaleID { get; set; }
public string ProductCode { get; set; }
public int ProductCount { get; set; }
public string Image_Path { get; set; }
public string Shoot_Date { get; set; }
public string Shoot_Info { get; set; }
}
Linq statement where I'm having trouble:
var test = (from _ClientData in db.ClientDatas
join _salesInfo in db.Sales_Infoes
on _ClientData.CostumerID
equals _salesInfo.CostumerID
where _ClientData.UserName == _userName
select new userSales()
{
CostumerID = _ClientData.CostumerID,
Name = _ClientData.Name,
UserName = _ClientData.UserName,
Sale_Id = _salesInfo.Sale_Id, // This is the item i would like to use in my were statement
Sale_Date = _salesInfo.Sale_Date,
Sale_Cost = _salesInfo.Sale_Cost,
Discount_Code = _salesInfo.Discount_Code,
Paid = _salesInfo.Paid,
// Problem here
saleProductsList = db.SaleProducts.Where()
}).ToList();
Got to this based on the answer:
var reult = db.ClientDatas.Where(a => a.UserName == _userName)
.Join(db.Sales_Infoes,
a => a.CostumerID,
b => b.CostumerID,
(a, b) => new userSales()
{
CostumerID = a.CostumerID,
Discount_Code = b.Discount_Code,
Sale_Cost = b.Sale_Cost,
Sale_Id= b.Sale_Id,
Name = a.Name,
Sale_Date = b.Sale_Date,
UserName = a.UserName,
Paid = b.Paid,
saleProductsList = db.SaleProducts.Where(c => c.SaleID == b.Sale_Id).ToList()
}).ToList();
You're not looking for a where, you're looking for a join. Where filters the results on a single table, join intersects two tables which is actually what you want here.
var result = db.Sales_Infoes.Where(x => x.UserName == _userName)
.Join(db.ClientDatas,
x => x.Sale_Id,
y => y.Sale_id,
(x, y) => new userSales() {
// x is SalesInfo obj y is ClientDatas obj do assignement here
Name = y.Name,
Sale_Date = y.Sale_date
}).ToList();
Just fyi I haven't had a chance to test that but it's the basic idea. You don't need a select like in your statement because the last argument I'm passing into join is the lambda (x, y) => ... in that case x and y are the current row from each table (that we've gotten from applying our where to the user sales table then joining those results into the salesproduct table) so whatever projections you want to do occur there. The other two method args above that are the telling join which fields to compare, it's the 'key selector' lambda expression for each table.
I have these 3 classes:
Employee
Student
Person
Code:
public class Employee
{
public Guid Id { get; set; }
public string Name { get; set; }
public int Age { get; set; }
public string Gender { get; set; }
public long TimeStamp { get; set; }
}
public class Student
{
public Guid Id { get; set; }
public string Name { get; set; }
public int Age { get; set; }
public long TimeStamp { get; set; }
}
public class Person<br>
{
public string Name { get; set; }
public int Age { get; set; }
}
I create 4 Lists :
var studentList = new List<Student>();// fill the List with a lot of Stundents
var employeeList = new List<Student>(); // fill the List with a lot of employees
var personList1 = new List<Person>();
var personList2 = new List<Person>();
Select all students and employees
var allStudents = studentList.Select(a => a); // does not make a lot of sence but for testing
var allEmployee = employeeList.Select(b => b);
I want to map allStudents to
personList1.AddRange(allStudents.Select(a => new Person()
{
Age = a.Age,
Name = a.Name
} ));
I want to get all Employees where the value of TimeStape is not mentioned in the allStundent List
var allEmployeesWithDifferentTimeStampThanStundent =
allEmployee.Where(a => !allStudents.Select(b =>b.TimeStamp).Contains(a.TimeStamp));
mapping again
personList2.AddRange(allEmployeesWithDifferentTimeStampThanStundent.Select
(a => new Person()
{
Age = a.Age,
Name = a.Name
} ));
merge both lists
personList1.AddRange(personList2);
Is there a better and more efficient way to do this?
The personList2 variable appears only to be there as an intermediate for projecting to the Person type -- if that's the case, you could skip its creation and use query syntax like so:
var personsFromNonMatchingEmployees =
from employee in allEmployee
join student in allStudents
on employee.TimeStamp equals student.TimeStamp into studentsWithMatchingTimeStamp
where !studentsWithMatchingTimeStamp.Any()
select new Person { Age = employee.Age, Name = employee.Name };
personList1.AddRange(personsFromNonMatchingEmployees);
This is similar to the other GroupJoin approach since the compiler translates the above into a GroupJoin call. The use of join/group-join necessarily performs better than the Where..Contains approach since it makes use of hashing - in other words, it's an algorithmic Big-O improvement that should be quite noticeable for any more than a few Student or Employee instances.
By selecting the new Person object in the query, I'm able to bypass the personList2 list altogether. I find that I'm almost always able to eliminate temporary lists by doing selects like this that project to the type that I'm really interested in. I also left out the () on the new Person { .. } since the compiler doesn't require it.
Shy of changing up the inheritance and making Employee : Person & Student : Person, I don't think there's much more to improve.
You can use GroupJoin to find all employees without a matching Student record with the same timestamp:
var employeesDiffTS = allEmployee
.GroupJoin(allStudents, e => e.TimeStamp, s => s.TimeStamp, (e, students) => new { Emp = e, HasMatch = students.Any() })
.Where(em => !em.HasMatch)
.Select(em => em.Emp)
personList2.AddRange(employeeDiffTS.Select(a => new Person { Age = a.Age, Name = a.Name }));
personList1.AddRange(personList2);