I'm swapping a project into BreezeJS and having trouble getting this query right. I have a query I was using in LINQ that was doing it perfectly. It is:
var transactionTypes = from tpt in db.TransactionPropertyTypes
from et in tpt.TransactionTypes
where tt.TransactionTypeName == transactionType.TransactionTypeName
select tpt;
Where transactionType is an object I've passed in.
My model matches up perfectly. Basically there are three tables. TransactionPropertyTypes, TransactionTypes and a one linking the foreign keys together. Any help is greatly appreciated!
So far my best shot in breeze is:
breeze.EntityQuery.from("TransactionPropertyTypes")
.from("TransactionTypes")
.where("TransactionType.TransactionTypeName", "==", type)
.using(man).execute()
The error the console prints is:
Error: The left hand side of a binary predicate cannot be a literal expression, it must be a valid property or functional predicate expression: TransactionType.TransactionTypeName
Any help is appreciated!
Only one 'from' clause
Steve's solution won't work because you can't have double "from" clauses.
Let me restate in English what I think you're intentions are.
Give me all instances of TransactionPropertyType that have a particular TransactionType among their TransactionTypes.
I will assume that your TransactionPropertyType has a collection navigation property of transactionTypes.
If I've guessed correctly, you're looking for an "any" query.
Something like this should work:
query = EntityQuery.from('TransactionPropertyTypes')
.where('transactionTypes', 'any',
'name', 'eq', desiredTransType.name);
Many to many
Upon re-reading I see that TransactionPropertyType and TransactionType are related many-to-many. Breeze doesn't support many-to-many relationships yet.
But you also said that you can surface the mapping table that implements the relationship as an entity.
You didn't name it; I'll call it the TransPropTransTypeMap. I'll assume that there is a through navigation path that goes TransactionPropertyType.transPropTransTypeMaps.transactionType.
The re-written query becomes:
query = EntityQuery.from('TransactionPropertyTypes')
.where('transPropTransTypeMaps', 'any',
'transactionType.name', 'eq', desiredTransType.name);
I just added an ngDocCode:query_with_predicates test that does the same kind of thing for the Northwind m-to-m relationship, 'Order-OrderDetail-Product', in which OrderDetail plays the role of the mapping entity.
it("an ANY predicate through a many-to-many mapping table", function (done) {
// Get first Order with an OrderDetail that has a product named 'Chai'
// OrderDetail is the mapping table in this scenario:
// Order <-(1,M)-> OrderDetail <-(M,1)-> Product
var pred = new Predicate('OrderDetails', 'any',
'Product.ProductName', 'eq', 'Chai');
EntityQuery.from('Orders')
.where(pred)
.top(1)
.expand('OrderDetails.Product')
.using(em).execute().then(success).then(done, done);
function success(data){
var order = data.results[0] || {};
var hasChai = order.OrderDetails.some(function(od){
return od.Product.ProductName === 'Chai';
});
expect(hasChai).to.be.true;
}
});
Create a Predicate Object
Breeze allows for complex predicates to be created and built upon:
var Predicate = breeze.Predicate;
var mypred = Predicate.Create("TransactionType.TransactionTypeName", "==", valueToFind);
// If more clauses, add them like this
if (2ndValueToFind) {
mypred = mypred.and(Predicate.create('AnotherField', '==', 2ndValueToFind));
}
breeze.EntityQuery.from("TransactionPropertyTypes")
.from("TransactionTypes")
.where(mypred)
.using(man).execute()
I don't believe this will solve the many-to-many problem, but it is very useful.
Related
I've the following Entity Model : Employee has a Company and a Company has Employees.
When using the Include statement like below:
var query = context.Employees.Include(e => e.Company);
query.Dump();
All related data is retrieved from the database correctly. (Using LEFT OUTER JOIN on Company table)
The problem is hat when I use the GroupBy() from System.Linq.Dynamic to group by Company.Name, the Employees are missing the Company data because the Include is lost.
Example:
var groupByQuery = query.GroupBy("new (Company.Name as CompanyName)", "it");
groupByQuery.Dump();
Is there a way to easily retrieve the applied Includes on the 'query' as a string collection, so that I can include them in the dynamic GroupBy like this:
var groupByQuery2 = query.GroupBy("new (Company, Company.Name as CompanyName)", "it");
groupByQuery2.Dump();
I thought about using the ToString() functionality to get the SQL Command like this:
string sql = query.ToString();
And then use RegEx to extract all LEFT OUTER JOINS, but probably there is a better solution ?
if you're creating the query in the first place - I'd always opt to save the includes (and add to them if you're making a composite query/filtering).
e.g. instead of returning just 'query' return new QueryContext {Query = query, Includes = ...}
I'd like to see a more elegant solution - but I think that's your best bet.
Otherwise you're looking at expression trees, visitors and all those nice things.
SQL parsing isn't that straight either - as queries are not always that simple (often a combo of things etc.).
e.g. there is a `span' inside the query object (if you traverse a bit) which seems to be holding the 'Includes' but it's not much help.
I am currently writing a web-based 'recipe' application using LINQ and Entity Framework 5.0. I've been struggling with this query for awhile, so any help is much appreciated!
There will be a search function where the users can enter a list of ingredients that they want the recipe results to match. I need to find all recipes where the associated ingredient collection (name property) contains the text of every record in a list of strings (the user search terms). For example, consider the following two recipes:
Tomato Sauce: Ingredients 'crushed tomatoes', 'basil', 'olive oil'
Tomato Soup: Ingredients 'tomato paste', 'milk', 'herbs
If the user used the search terms 'tomato' and 'oil' it would return the tomato sauce but not the tomato soup.
var allRecipes = context.Recipes
.Include(recipeCategory => recipeCategory.Category)
.Include(recipeUser => recipeUser.User);
IQueryable<Recipe> r =
from recipe in allRecipes
let ingredientNames =
(from ingredient in recipe.Ingredients
select ingredient.IngredientName)
from i in ingredientNames
let ingredientsToSearch = i where ingredientList.Contains(i)
where ingredientsToSearch.Count() == ingredientList.Count()
select recipe;
I've also tried:
var list = context.Ingredients.Include(ingredient => ingredient.Recipe)
.Where(il=>ingredientList.All(x=>il.IngredientName.Contains(x)))
.GroupBy(recipe=>recipe.Recipe).AsQueryable();
Thank you for your help!
Just off the top of my head i would go for something like this
public IEnumerable<Recipe> SearchByIngredients(params string[] ingredients)
{
var recipes = context.Recipes
.Include(recipeCategory => recipeCategory.Category)
.Include(recipeUser => recipeUser.User);
foreach(var ingredient in ingredients)
{
recipes = recipes.Where(r=>r.Ingredients.Any(i=>i.IngredientName.Contains(ingredient)));
}
//Finialise the queriable
return recipes.AsEnumerable();
}
You can then call it using:
SearchByIngredients("tomatoes", "oil");
or
var ingredients = new string[]{"tomatoes", "oil"};
SearchByIngredients(ingredients );
What this is going to do is attach where clauses to the queriable recipes for each of your search terms. Multiple where clauses are treated as ANDs in SQL (which is what you want here anyway). Linq is quite nice in the way that we can do this, at then end of the function we finalise the queriable essentially saying all that stuff that we just did can get turned into a single query back to the DB.
My only other note would be you really want to be indexing/full text indexing the Ingredient name column or this wont scale terribly well.
Here is a foreach statement I'd like to express in Linq to Entities. It loops through child entities (attachments) of a parent entity (currentFactSheet) to see if an attachment exists with a particular FileName. How can I condense this procedural code into a Linq to Entites statement?
FactSheet currentFactSheet = mainWindow.GetCurrentFactSheet();
bool attachmentExists = false;
foreach (var thisAttachment in currentFactSheet.AttachmentsNav)
{
if (thisAttachment.FileName == nameOfAttachedFile)
{
attachmentExists = true;
}
}
This is a partial image showing FactSheet (left) and the Attachment entity associated via a navigation property named AttachmentsNav:
I want to query in memory entities to avoid a round trip to the database. I've found examples like this that search only the parent level. I've made many attempts, but they never bring up intellisense with the field names on my child entity (specifically Attachment.FileName).
Thanks in advance.
Try this:
bool attachmentExists = currentFactSheet.AttachmentsNav.Any(a => a.FileName == nameOfAttachedFile);
I am trying to bind distinct records to a dropdownlist. After I added distinct function of the linq query, it said "DataBinding: 'System.String' does not contain a property with the name 'Source'. " I can guarantee that that column name is 'Source'. Is that name lost when doing distinct search?
My backend code:
public IQueryable<string> GetAllSource()
{
PromotionDataContext dc = new PromotionDataContext(_connString);
var query = (from p in dc.Promotions
select p.Source).Distinct();
return query;
}
Frontend code:
PromotionDAL dal = new PromotionDAL();
ddl_Source.DataSource = dal.GetAllSource();
ddl_Source.DataTextField = "Source";
ddl_Source.DataValueField = "Source";
ddl_Source.DataBind();
Any one has a solution? Thank you in advance.
You're already selecting Source in the LINQ query, which is how the result is an IQueryable<string>. You're then also specifying Source as the property to find in each string in the databinding. Just take out the statements changing the DataTextField and DataValueField properties in databinding.
Alterantively you could remove the projection to p.Source from your query and return an IQueryable<Promotion> - but then you would get distinct promotions rather than distinct sources.
One other quick note - using query syntax isn't really helping you in your GetAllSources query. I'd just write this as:
public IQueryable<string> GetAllSource()
{
PromotionDataContext dc = new PromotionDataContext(_connString);
return dc.Promotions
.Select(p => p.Source)
.Distinct();
}
Query expressions are great for complicated queries, but when you've just got a single select or a where clause and a trivial projection, using the dot notation is simpler IMO.
You're trying to bind strings, not Promotion objects... and strings do not have Source property/field
Your method returns a set of strings, not a set of objects with properties.
If you really want to bind to a property name, you need a set of objects with properties (eg, by writing select new { Source = Source })
I want to create an Entity Object from a LinQ statement, but I don't want to load all its columns.
My ORDERS object has a lot of columns, but I just want to retrieve the REFERENCE and OPERATION columns so the SQL statement and result will be smaller.
This LinQ statement works properly and loads all my object attributes:
var orders = (from order in context.ORDERS
select order);
However the following statement fails to load only two properties of my object
var orders = (from order in context.ORDERS
select new ORDERS
{
REFERENCE = order.REFERENCE,
OPERATION = order.OPERATION
});
The error thrown is:
The entity or complex type
'ModelContextName.ORDERS' cannot be
constructed in a LINQ to Entities
query.
What is the problem? Isn't it possible to partially load an object this way?
Thank you in advance for your answers.
ANSWER
Ok I should thank you both Yakimych and Dean because I use both of your answers, and now I have:
var orders = (from order in context.ORDERS
select new
{
REFERENCE = order.REFERENCE,
OPERATION = order.OPERATION,
})
.AsEnumerable()
.Select(o =>
(ORDERS)new ORDERS
{
REFERENCE = o.REFERENCE,
OPERATION = o.OPERATION
}
).ToList().AsQueryable();
And I get exactly what I want, the SQL Statement is not perfect but it returns only the 2 columns I need (and another column which contains for every row "1" but I don't know why for the moment) –
I also tried to construct sub objects with this method and it works well.
No, you can't project onto a mapped object. You can use an anonymous type instead:
var orders = (from order in context.ORDERS
select new
{
REFERENCE = order.REFERENCE,
OPERATION = order.OPERATION
});
The problem with the above solution is that from the moment you call AsEnumerable(), the query will get executed on the database. In most of the cases, it will be fine. But if you work with some large database, fetching the whole table(or view) is probably not what you want. So, if we remove the AsEnumerable, we are back to square 1 with the following error:
The entity or complex type 'ModelContextName.ORDERS' cannot be constructed in a LINQ to Entities query.
I have been struggling with this problem for a whole day and here is what I found. I created an empty class inheriting from my entity class and performed the projection using this class.
public sealed class ProjectedORDERS : ORDERS {}
The projected query (using covariance feature):
IQueryable<ORDERS> orders = (from order in context.ORDERS
select new ProjectedORDERS
{
REFERENCE = order.REFERENCE,
OPERATION = order.OPERATION,
});
Voilà! You now have a projected query that will map to an entity and that will get executed only when you want to.
I think the issue is creating new entities within the query itself, so how about trying this:
context.ORDERS.ToList().Select(o => new ORDERS
{
REFERENCE = o.REFERENCE,
OPERATION = o.OPERATION
});