How to take elements from the same collection based on other atrributes - linq

Hi I have a collection of objects:
MyClasss{
int Id,
int ?OtherId
}
I want to take do sth like:
collection.Where(x.Id == y.OtherId).
Ho to perform it in linq?

I think you're looking for:
collection.Where(x => collection.Any(y => x.Id == y.OtherId));
Be aware that this will also pull out ones where the OtherId is equal to the Id on the same object.

I think your problem is that Id is an int, while OtherId is a nullable int.
You have a sequence of objects of MyClass, and you want to keep only those objects where "Id equals OtherId". Or to be precise:
Keep only those objects where OtherId is not null, and OtherId == Id.
The easiest method would be:
IEnumerable<MyClass> myCollection = ...
IEnumerable<MyClass> result = myCollection
.Where(myClass => myClass.OtherId.HasValue && myClass.Value == myClass.Id);

Related

Getting a column by string name

I'm trying to update a record given the customer Id, the row Id, and a dynamic column name.
Thus far I have the following, with the trouble spot marked by ***:
public void UpdateRecord(int Id, string rval, string column, string value)
{
var rId = GetRvalId(rval);
var entry = _context.Customers
.Where(x => x.Id == Id && x.RVals.Id == rId && x.***column?*** == column).First();
entry = value;
}
I haven't been able to find a good example of how to do this.
Addition after comments at the end
The reason you couldn't find examples is because it is not a good design.
Your method is very error prone, difficult to test and horrible to maintain. What if someone types the incorrect column name? What if you try to assign a string to the customer's birthday? And even if you would implement some string checking for column names and proposed values, then your program wouldn't work anymore after someone changes the names or the types of the columns.
So let's redesign!
Apparently you have a Customer with an Id and a property Rvals. This property Rvals also has a property Id.
You also have a function GetRValId that can convert a string rval to an int rvalId.
What you want, is given an Id and a string rval, you want to update one of the columns of the first Customer with this Idand rValId.
Side questions: Can there be more than one Customer with Id? In that case: are you sure Id is an ID? What do you want if there are more matching Customers? Update all customers or update only the first one? Which customer do you define as the first customer?
Leaving the side questions aside. We want a function signature that reports errors at compile time if you use non-existing customer properties, or if you try to assign a string to a Birthday. Something like this perhaps?
Update the name of the customer:
int customerId = ...
string rval = ...
string proposedName = "John Doe";
UpdateCustomerRecord(id, rval, customer => customer.Name = proposedName);
Update the Birthday of the customer:
DateTime proposedBirthday = ...
UpdateCustomerRecord(id, rval, customer => customer.Birthday = proposedBirthday)
This way you can't use any column that does not exist, and you can't assign a string to a DateTime.
You want to change two values in one call? Go ahead:
UpdateCustomerRecord(id, rval, customer =>
{
customer.Name = ...;
customer.Birthday = ...;
});
Convinced? Let's write the function:
public void UpdateCustomerRecord(int customerId, string rval, Action<Customer> action)
{
// the beginning is as in your function:
var rId = GetRvalId(rval);
// get the customer that you want to update:
using (var _Context = ...)
{
// get the customer you want to update:
var customerToUpdate = _Context.Customers
.Where(customer => customer.Id == Id
&& customer.RVals.Id == rId)
.FirstOrDefault();
// TODO: exception if there is no customerToUpdate
// perform the action and save the changes
action(customerToUpdate);
_context.SaveChanges();
}
Simple comme bonjour!
Addition after comments
So what does this function do? As long as you don't call it, it does nothing. But when you call it, it fetches a customer, performs the Action on the Customer you provided in the call, and finally calls SaveChanges.
It doesn't do this with every Customer, no it does this only with the Customer with Id equal to the provided Id and customer.RVals.Id == ... (are you still certain there is more than one customer with this Id? If there is only one, why check for RVals.Id?)
So the caller not only has to provide the Id, and the RVal, which define the Customer to update, but he also has to define what must be done with this customer.
This definition takes the form of:
customer =>
{
customer.Name = X;
customer.BirthDay = Y;
}
Well if you want, you can use other identifiers than customer, but it means the same:
x => {x.Name = X; x.BirthDay = Y;}
Because you put it on the place of the Action parameter in the call to UpdateCustomerRecord, I know that x is of type Customer.
The Acton statement means: given a customer that must be updated, what must we do with the customer? You can read it as if it was a Function:
void Action(Customer customer)
{
customer.Name = ...
customer.BirthDay = ...
}
In the end it will do something like:
Customer customerToUpdate = ...
customerToUpdate.Name = X;
customerToUpdate.BirthDay = Y;
SaveChanges();
So in the third parameter, called Action you can type anything you want, even call functions that have nothing to do with Customers (probably not wise). You have an input parameter of which you are certain that it is a Customer.
See my earlier examples of calling UpdateCustomerRecord, one final example:
UpdateCustomerRecord( GetCustomerId(), GetCustomerRVal,
// 3rd parameter: the actions to perform once we got the customerToUpdate:
customer =>
{
DateTime minDate = GetEarliestBirthDay();
if (customer.BirthDay < minDate)
{ // this Customer is old
customer.DoThingsThatOldPeopleDo();
}
else
{ // this Customer is young
customer.DoThingsThatYoungPeopleDo();
}
}
}
So the Action parameter is just a simpler way to say: "once you've got the Customer that must be updated, please perform this function with the Customer
So if you only want to update a given property of the customer write something like:
UpdateCustomerRecord(... , customer =>
{
Customer.PropertyThatMustBeUpdated = NewValueOfProperty;
}
Of course this only works if you know which property must be updated. But since you wrote "I am trying to update a specific cell." I assume you know which property the cells in this column represent.
It is not possible to pass the column name as the string value in LINQ. Alternate way to do it, if you have the limited number of the column name which can be passed then it can be achieved as below:
public void UpdateRecord(int Id, string rval, string column, string value)
{
var rId = GetRvalId(rval);
var entry = _context.Customers
.Where(x => x.Id == Id &&
x.RVals.Id == rId &&
(x.column1 == value || column == column1) &&
(x.column2 == value || column == column2) &&
(x.column3 == value || column == column3) &&
(x.column4 == value || column == column4) &&
(x.column5 == value || column == column5) &&
)).First();
entry = value;
}
UpdateRecord(5, "rval", "column1", "value");
UpdateRecord(5, "rval", "column2", "value");
UpdateRecord(5, "rval", "column3", "value");
Here, suppose you have the 5 columns that can be passed while calling the funcion UpdateRecord then you can add the 5 clauses in the WHERE as above.
Other way to do it dynamic LINQ
var entry = db.Customers.Where(column + " = " + value).Select(...);

Difficult sort order with Linq to SQL

I have a List of objects that I need sorted a particular way.
The relevant table fields are:
ID (int)
IsMandatory (bit)
ParentID (int nullable)
Code (varchar)
I need them sorted by IsMandatory=true first, then by Code, but anything with a ParentID must be sorted by Code but appear straight after the row with the same ID as ParentID (and these records will always have IsMandatory set to NULL).
Some sample data, and this is also in the order in which they should appear when ordered:
ID=1, IsMandatory=1, ParentID=NULL, Code="A"
ID=2, IsMandatory=NULL, ParentID=1, Code="A"
ID=3, IsMandatory=NULL, ParentID=1, Code="B"
ID=4, IsMandatory=1, ParentID=NULL, Code="B"
ID=5, IsMandatory=0, ParentID=NULL, Code="C"
ID=6, IsMandatory=NULL, ParentID=5, Code="A"
ID=7, IsMandatory=0, ParentID=NULL, Code="D"
How would this best be accomplished in a Linq to SQL orderby?
It was a difficult sort!
The reason for the difficulty stems from the fact you are ordering on the Parents record properties first and then the actual records properties.
I tried to make the variables as self explanatory as possible, but if you have any questions please ask!
var query = from x in context.Table
let parent = list.FirstOrDefault(y => x.ParentID == y.ID)
let parentIsMandatory = parent == null ? x.IsMandatory : parent.IsMandatory
let parentIsMandatoryOrder = parentIsMandatory == true ? 0 : 1
let parentCode = parent == null ? x.Code : parent.Code
let parentId = x.ParentID ?? x.ID
let isParent = x.ParentID == null ? 0 : 1
orderby parentIsMandatoryOrder, parentCode, parentId, isParent, x.Code
select x;

Linq: Nullable Object must have Value

My query is as follows:
int ID = db.Q_Table.Find(item.PassedInID).ID;
I already found a solution for my issue, however i am wondering why i must write it like so
1.
Nullable(int) ID = db.Q_Table.Find(item.PassedInID).ID;
2.
db.Q_Table.Where(w => w.PassedInID== item.PassedInID).Select(s => s.ID ).SingleOrDefault();
It wouldn't let me put int int he < in the above code -.-...
I am curious why i have to code it to a nullable int? I really didn't want to code it like 2nd solution because its more code :). Yes i have made sure there are values in the database and from the below image you can see my database doesn't accept nulls.
Thanks for any answers
There is a difference between int and Nullable<int> or int?, you can't directly assign a Nullable<int> to int (Nullable<T>), Consider:
int? x = 123;
int y = x; //This would be an error
But you can use null-coalescing operator
int? x = 123;
int y = x ?? 0;
Now for your case, your ID seems to be a mapped to column in database which allows null. That will map to C# Nullable<int>, if you want to assign the result to an int you can do:
int ID = db.Q_Table.Find(item.PassedInID).ID ?? 0;
That will give your variable the value of ID or 0 if it is null.
ID is not an int but rather a Nullable<int> (or short form int?).
This is typically the case when the underlying database column is nullable.
There is also no < operator defined on an int?. If you want to do that, you have to check for the presence of a value first:
db.Q_Table.Where(w => w.PassedInID.HasValue && w.PassedInID.Value == item.PassedInID)
.Select(s => s.ID ).SingleOrDefault();
that's probably because db.Q_Table.Find returns a nullible int.
you can probably also do the following if you wanted to
int? ID = db.Q_Table.Find(item.PassedInID).ID;
I guess you could do something like the following, it is a little less code:
int? t;
int ID = (t = db.Q_Table.Find(item.PassedInID).ID) == null ? -1 : t;
Of course, now you will have to handle -1 ID as a special case later in your code. I expect you just want a nullable int.

Why is this LINQ to EF query not working?

I have a problem with the following method:
public IQueryable<Customer> ListCustomers(int? parentId)
{
Debug.Assert(parentId == null);
//var list = _customerRepository.List().Where(c => c.ParentId == null);
var list = _customerRepository.List().Where(c => c.ParentId == parentId);
return list;
}
I have one Customer in my DB with a ParentId of null. When I call the method with ListCustomers(null), list is empty for the return statement. If I swap the commented out line in and query with a hard-coded null, then list contains my one customer.
What could cause this difference between these two queries? Why is the one with c.ParentId == parentId not returning anything?
Becouse the Nullable type the linq provider will not generate the proper IS NULL check. See this answer for further information: https://stackoverflow.com/a/785501/1195510
EF translates your query with int? to something like this:
DECLARE #parentId Int = null
SELECT ... WHERE ParentId = #parentId
When this is executed on the database, it doesn't do what you expect because in SQL [column] = NULL is always false.
I agree EF could handle this better, but as a workaround, you can write something like this:
.Where( c => !parentId.HasValue
? !c.ParentId.HasValue
: c.ParentId.Value == parentId.Value
)
EF will then generate a ( somewhat verbose ) SQL statement with the correct IS NULL predicates.
with nullable types you have to use it like this:
.Where(c=> object.Equals(c.ParentId, parentId))

Entity Framework 4 - What is the syntax for joining 2 tables then paging them?

I have the following linq-to-entities query with 2 joined tables that I would like to add pagination to:
IQueryable<ProductInventory> data = from inventory in objContext.ProductInventory
join variant in objContext.Variants
on inventory.VariantId equals variant.id
where inventory.ProductId == productId
where inventory.StoreId == storeId
orderby variant.SortOrder
select inventory;
I realize I need to use the .Join() extension method and then call .OrderBy().Skip().Take() to do this, I am just gettting tripped up on the syntax of Join() and can't seem to find any examples (either online or in books).
NOTE: The reason I am joining the tables is to do the sorting. If there is a better way to sort based on a value in a related table than join, please include it in your answer.
2 Possible Solutions
I guess this one is just a matter of readability, but both of these will work and are semantically identical.
1
IQueryable<ProductInventory> data = objContext.ProductInventory
.Where(y => y.ProductId == productId)
.Where(y => y.StoreId == storeId)
.Join(objContext.Variants,
pi => pi.VariantId,
v => v.id,
(pi, v) => new { Inventory = pi, Variant = v })
.OrderBy(y => y.Variant.SortOrder)
.Skip(skip)
.Take(take)
.Select(x => x.Inventory);
2
var query = from inventory in objContext.ProductInventory
where inventory.ProductId == productId
where inventory.StoreId == storeId
join variant in objContext.Variants
on inventory.VariantId equals variant.id
orderby variant.SortOrder
select inventory;
var paged = query.Skip(skip).Take(take);
Kudos to Khumesh and Pravin for helping with this. Thanks to the rest for contributing.
Define the join in your mapping, and then use it. You really don't get anything by using the Join method - instead, use the Include method. It's much nicer.
var data = objContext.ProductInventory.Include("Variant")
.Where(i => i.ProductId == productId && i.StoreId == storeId)
.OrderBy(j => j.Variant.SortOrder)
.Skip(x)
.Take(y);
Add following line to your query
var pagedQuery = data.Skip(PageIndex * PageSize).Take(PageSize);
The data variable is IQueryable, so you can put add skip & take method on it. And if you have relationship between Product & Variant, you donot really require to have join explicitly, you can refer the variant something like this
IQueryable<ProductInventory> data =
from inventory in objContext.ProductInventory
where inventory.ProductId == productId && inventory.StoreId == storeId
orderby inventory.variant.SortOrder
select new()
{
property1 = inventory.Variant.VariantId,
//rest of the properties go here
}
pagedQuery = data.Skip(PageIndex * PageSize).Take(PageSize);
My answer here based on the answer that is marked as true
but here I add a new best practice of the code above
var data= (from c in db.Categorie.AsQueryable().Join(db.CategoryMap,
cat=> cat.CategoryId, catmap => catmap.ChildCategoryId,
cat, catmap) => new { Category = cat, CategoryMap = catmap })
select (c => c.Category)
this is the best practice to use the Linq to entity because when you add AsQueryable() to your code; system will converts a generic System.Collections.Generic.IEnumerable to a generic System.Linq.IQueryable which is better for .Net engine to build this query at run time
thank you Mr. Khumesh Kumawat
You would simply use your Skip(itemsInPage * pageNo).Take(itemsInPage) to do paging.

Resources