Apply condition on one to many relationship in entity framwork - linq

I have two objects with one to many relationship. I want to apply condition on second collections of object. Below is my code.
DateTime date = DateTime.Now.AddDays(-15);
IQueryable<DB.ProductionDB.CDCPortalApplicant> applicant =
from app in productionEntities.CDCPortalApplicant.Include("CDCPortalUnit")
where app.FirstName == firstName
&& app.LastName == lastName
&& app.EmailAddress == emailAddress
&& app.Created > date
&& app.CDCPortalUnit.PropertyId == propertyId
select app;
Here CDCPortalUnit has more than one object for CDCPortalApplicant object
app.CDCPortalUnit.PropertyId == propertyId
Actually I want to retrieve the CDCPortalApplicant object based properyID which is available in CDCPortalUnit object. CDCPortalApplicant object have multiple instance of CDCPortalUnit object.

The answer depends on the structure of your CDCPortalUnit entity.
You can change this line
&& app.CDCPortalUnit.PropertyId == propertyId
To
&& app.CDCPortalUnit.Single(portalUnit => portalUnit.SomeProperty == someValue).PropertyId == propertyId

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(...);

Use Linq query to compare date only with DateTime field

I need to compare just the date only in a Linq query that involves a datetime field. However, the syntax below results in the following error message
The specified type member 'Date' is not supported in LINQ to Entities. Only initializers, entity members, and entity navigation properties are supported.
Does anyone know how to extract just the date out of a datetime field?
var duplicate = from a in _db.AgentProductTraining
where a.CourseCode == course.CourseCode &&
a.DateTaken.Date == course.DateTaken.Date &&
a.SymNumber == symNumber
select a;
It might seem a little roundabout, but you can use the SqlFunctions class' DateDiff method for doing this. Just pass in both values and use "Day" for finding the difference between them in days (which should be 0 if they are on the same day).
Like the following:
from a in _db.AgentProductTraining
where a.CourseCode == course.CourseCode &&
SqlFunctions.DateDiff("DAY", a.DateTaken, course.DateTaken) == 0 &&
a.SymNumber == symNumber
select a;
You can use EntityFunctions.TruncateTime() under the namespace System.Data.Objects
Ex.
db.Orders.Where(i => EntityFunctions.TruncateTime(i.OrderFinishDate) == EntityFunctions.TruncateTime(dtBillDate) && i.Status == "B")
Works like charm.
UPDATE
This function works only when you querying entities through LINQ. Do not use in LINQ-Object.
For EF6 use DbFunctions.TruncateTime() under System.Data.Entity namespace.
You can do it like bellow:
var data1 = context.t_quoted_value.Where(x => x.region_name == "Pakistan"
&& x.price_date.Value.Year == dt.Year
&& x.price_date.Value.Month == dt.Month
&& x.price_date.Value.Day == dt.Day).ToList();
you must use System.Data.Entity.DbFunctions.TruncateTime
try this
DateTime dt =course.DateTaken.Date;
var duplicate = from a in _db.AgentProductTraining
where a.CourseCode == course.CourseCode &&
a.DateTaken == dt &&
a.SymNumber == symNumber
select a;
if a.DateTaken contains Time also, then refer these links to modify your date.
The Date property cannot be used in LINQ To Entities.
Compare Dates using LINQ to Entities (Entity Framework)
'Date' is not supported in LINQ to Entities. Only initializers, entity members, and entity navigation properties are supported
http://forums.asp.net/t/1793337.aspx/1
This is how I ended by doing a similar Date search which I had to consider time (Hour and Minutes portion) also
from x in _db.AgentProductTraining
where
x.CreatedOn.Year == mydacourse.DateTakente.Year
&& x.CreatedOn.Month == course.DateTaken.Month
&& x.CreatedOn.Day == course.DateTaken.Day
&& x.CreatedOn.Hour == course.DateTaken.Hour
&& x.CreatedOn.Minute == course.DateTaken.Minute
select x;
Hey when I was building a query this below worked
DateTime date = Convert.ToDateTime(SearchText);
query = query.Where(x => x.Date.Month == date.Month
&& x.Date.Day == date.Day
&& x.Date.Year == date.Year);
// Let me know if this worked for you as it pulled the date that was searched for me
Take off the .Date
If the field is a DateTime it can be compared with ==
var duplicate = from a in _db.AgentProductTraining
where a.CourseCode == course.CourseCode &&
a.DateTaken == course.DateTaken &&
a.SymNumber == symNumber
select a;

Linq with Logic

I have simple Linq statement (using EF4)
var efCars = (from d in myentity.Cars
where d.CarName == inputCar.CarName
&& d.CarIdNumber == inputCar.IdNumber
&& d.Make == inputCar.Make
select d.Car);
I want it to be smarter so that it will only query across one or more of the 3 fields IF they have values.
I can do a test before, and then have a separate linq statement for each permutation of valyes for inputcar
(i.e. one for all 3, one for if only carname has a value, one for if carname AND CarIdNumber has a value etc etc)
but there must be a smarter way
Thanks!
If "has no value" means null then you can use the null coalescing operator ?? to say take the first value if populated, otherwise take the second:
var efCars = (from d in myentity.Cars
where d.CarName == (inputCar.CarName ?? d.CarName
&& d.CarIdNumber == (inputCar.IdNumber && d.CarIdNumber)
&& d.Make == (inputCar.Make && d.Make)
select d.Car);
This basically says if a value exists it must match, otherwise treat it as matching
However if instead you're saying "when a special value (empty string) ignore it, otherwise match" then you can do one of two approaches (or possibly more!):
where (inputCar.CarName == "" || d.CarName == inputCar.CarName)
where (string.IsNullOrEmpty(inputCar.CarName) || d.CarName == inputCar.CarName)
For performance (when dealing with database queries) it can sometimes be beneficial to let EF generate queries based on the filters, instead of using one generic query. Of course you will need to profile whether it helps you in this case (never optimize prematurely), but this is how it would look if you dynamically build your query:
var efCars =
from car in myentity.Cars
select car;
if (inputCar.CarName != null)
{
efCars =
from car in efCars
where care.CarName == inputCar.CarName
select car;
}
if (inputCar.IdNumber != null)
{
efCars =
from car in efCars
where care.CarIdNumber == inputCar.IdNumber
select car;
}
if (inputCar.Make != null)
{
efCars =
from car in efCars
where care.Make == inputCar.Make
select car;
}
where (inputCar.CarName != null || d.CarName == inputCar.CarName) &&...

EntityFramework/LINQ - get filtered content from associated table/type

In my Entityframework model, I have a type/table “ModelElement”, which is linked to a type/table “ElementToComponentMapping”. The navigation/foreign keys are "ModelID" and "ElementNo".
I need to write a method that returns an instance of ModelElement based on certain filtering conditions, where the content of the ElementToComponentMapping linked to the ModelElement instance is also included. The challenge is that I also need to filter on what I return from the ElementToComponentMapping, which means it doesn’t look like I can use .Include
So this doesn't work, as I can't use the included/navigation type in the where clause
public ModelElement GetModelElement(int modelID, int modelElementNo, int version)
{
return (from c in context.ModelElements.Include("ElementToComponentMapping")
where c.ModelID == modelID && c.ElementNo == modelElementNo
&& c.ElementToComponentMappings.Where(m => m.version == version)
select c).FirstOrDefault();
}
My second attempt was to query out the main "ModelElement" object first, then query out the associated "ElementToComponentMappings" separately, and set that as the property of the "ModelElement"
public ModelElement GetModelElement(int modelID, int modelElementNo, int version)
{
ModelElement newElement = (from c in context.ModelElements
where c.ModelID == modelID && c.ElementNo == modelElementNo
select c).FirstOrDefault();
newElement.ElementToComponentMappings =
(from m in context.ElementToComponentMappings
where m.ModelID == modelID
&& m.ElementNo == modelElementNo
&& m.version == version
select m).FirstOrDefault();
return newElement;
}
But this also doesn't work, as the type returned by directly querying for the "ElementToComponentMappings" object is different from the "ElementToComponentMappings" property on the "ModelElement" object.
This seems like a simple operation - get value of foreign key-linked tables, where you filter on what you get from the content of the FK tables, so hopefully I'm just missing something obvious here...?
The type is different because your newElement.ElementToComponentMappings is collection but your query returns only single instance.
You can try to use this:
context.ContextOptions.LazyLoadingEnabled = false;
var newElement = (from c in context.ModelElements
where c.ModelID == modelID && c.ElementNo == modelElementNo
select c).FirstOrDefault();
var mapping = (from m in context.ElementToComponentMappings
where m.ModelID == modelID
&& m.ElementNo == modelElementNo
&& m.version == version
select m).FirstOrDefault();
// now check if newElement.ElementToComponentMappings contains your single item
You can also try to use this:
context.ContextOptions.LazyLoadingEnabled = false;
var newElement = (from c in context.ModelElements
where c.ModelID == modelID && c.ElementNo == modelElementNo
select c).FirstOrDefault();
((EntityCollection<ElementToComponentMappings>)newElement.ElementToComponentMappings)
.CreateSourceQuery()
.FirstOrDefault(m.version == version); // You don't need to check FKs here
// now check if newElement.ElementToComponentMappings contains your single item
Neither of these methods works if your type is proxied and lazy loading is enabled because this expected automatic relation fixup will not mark navigation property as loaded (if you have lazy loading enabled next access to the property will trigger lazy loading and load all other entities).

Help required to optimize LINQ query

I am looking to optimize my LINQ query because although it works right, the SQL it generates is convoluted and inefficient...
Basically, I am looking to select customers (as CustomerDisplay objects) who ordered the required product (reqdProdId), and are registered with a credit card number (stored as a row in RegisteredCustomer table with a foreign key CustId)
var q = from cust in db.Customers
join regCust in db.RegisteredCustomers on cust.ID equals regCust.CustId
where cust.CustomerProducts.Any(co => co.ProductID == reqdProdId)
where regCust.CreditCardNumber != null && regCust.Authorized == true
select new CustomerDisplay
{
Id = cust.Id,
Name = cust.Person.DisplayName,
RegNumber = cust.RegNumber
};
As an overview, a Customer has a corresponding Person which has the Name; PersonID is a foreign key in Customer table.
If I look at the SQL generated, I see all columns being selected from the Person table. Fyi, DisplayName is an extension method which uses Customer.FirstName and LastName. Any ideas how I can limit the columns from Person?
Secondly, I want to get rid of the Any clause (and use a sub-query) to select all other CustomerIds who have the required ProductID, because it (understandably) generates an Exists clause.
As you may know, LINQ has a known issue with junction tables, so I cannot just do a cust.CustomerProducts.Products.
How can I select all Customers in the junction table with the required ProductID?
Any help/advice is appreciated.
The first step is to start your query from CustomerProducts (as Alex Said):
IQueryable<CustomerDisplay> myCustDisplay =
from custProd in db.CustomerProducts
join regCust in db.RegisteredCustomers
on custProd.Customer.ID equals regCust.CustId
where
custProd.ProductID == reqProdId
&& regCust.CreditCardNumber != null
&& regCust.Authorized == true
select new CustomerDisplay
{
Id = cust.Id,
Name = cust.Person.Name,
RegNumber = cust.RegNumber
};
This will simplify your syntax and hopefully result in a better execution plan.
Next, you should consider creating a foreign key relationship between Customers and RegisteredCustomers. This would result in a query that looked like this:
IQueryable<CustomerDisplay> myCustDisplay =
from custProd in db.CustomerProducts
where
custProd.ProductID == reqProdId
&& custProd.Customer.RegisteredCustomer.CreditCardNumber != null
&& custProd.Customer.RegisteredCustomer.Authorized == true
select new CustomerDisplay
{
Id = cust.Id,
Name = cust.Person.Name,
RegNumber = cust.RegNumber
};
Finally, for optimum speed, have LINQ compile your query at compile time, rather than run time by using a compiled query:
Func<MyDataContext, SearchParameters, IQueryable<CustomerDisplay>>
GetCustWithProd =
System.Data.Linq.CompiledQuery.Compile(
(MyDataContext db, SearchParameters myParams) =>
from custProd in db.CustomerProducts
where
custProd.ProductID == myParams.reqProdId
&& custProd.Customer.RegisteredCustomer.CreditCardNumber != null
&& custProd.Customer.RegisteredCustomer.Authorized == true
select new CustomerDisplay
{
Id = cust.Id,
Name = cust.Person.Name,
RegNumber = cust.RegNumber
};
);
You can call the compiled query like this:
IQueryable<CustomerDisplay> myCustDisplay = GetCustWithProd(db, myParams);
I'd suggest starting your query from the product in question, e.g. something like:
from cp in db.CustomerProducts
join .....
where cp.ProductID == reqdProdID
As you have found, using a property defined as an extension function or in a partial class will require that the entire object is hydrated first and then the select projection is done on the client side because the server has no knowledge of these additional properties. Be glad that your code ran at all. If you were to use the non-mapped value elsewhere in your query (other than in the projection), you would likely see a run-time exception. You can see this if you try to use the Customer.Person.DisplayName property in a Where clause. As you have found, the fix is to do the string concatenation in the projection clause directly.
Lame Duck, I think there is a bug in your code as the cust variable used in your select clause isn't declared elsewhere as a source local variable (in the from clauses).

Resources