Nested query/Navigation Property collection - breeze

Assume the following models: (example taken from Breeze DocCode)
public class Customer {
public Guid CustomerID { get; internal set; }
public ICollection<Order> Orders { get; set; }
}
public class SomeDetail{
public string name{ get; set; }
}
public class Order {
public int OrderID {get; set;}
public Guid? CustomerID {get; set;}
public SomeDetail detail {get; set;}
}
Nested queries against single Navigation Properties are clear to me. How could this be done if the Navigation Property is a collection? Something like this:
var query = EntityQuery.from("Customers")
.where("Orders.detail.name", "==", someName);
As "Text":
Select all Customers where the name of the detail of any order this customer has equals someCondition?
I am running into errors here because
.where("Orders.detail.name, "=", someCondition)
is not possible due to the collection.
Is there a short way to check for this conditions without building up a number off collections and filtering per hand?
Any help much appreciated here.

As of Breeze 1.4.6, we have added support for two new query operators: "any" and "all"
This means that your query can now look something like this.
var query = EntityQuery.from("Customers")
.where("Orders", "any", "detail.name", "==", someName);
See: http://www.breezejs.com/documentation/query-examples

Related

ICollection does not contain a definition for Any

I'm writing code using eager loading to load the related data from Assigned using the Include method. I'm trying to get the value for IsAvailable, as to access the data from Assigned and check if there is a record which have the value of ReturnDate is equal to null. I keep on getting an error saying
ICollection does not contain a definition for Any
public ActionResult Index()
{
var item = db.Item.Include(h => h.Assigned).Select(b => new ItemViewModel
{
ItemID = b.ItemID,
ItemName = b.ItemName,
Category = b.Category,
Description = b.Description,
Model = b.Model,
IsAvailable = !b.Assigned.Any(h => h.ReturnDate == null)
}).ToList();
return View(item);
}
ItemViewModel
public class ItemViewModel
{
public int ItemID { get; set;}
[Display(Name = "Item")]
public string ItemName { get; set; }
[Required(ErrorMessage = "Category is required")]
public string Category { get; set; }
public string Model { get; set; }
public string Description { get; set; }
public bool IsAvailable { get; set; }
}
Item class
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.ComponentModel.DataAnnotations;
using System.Collections;
namespace Inventoryproto3.Models
{
public class Item
{
public int ItemID { get; set; }
[Required(ErrorMessage ="Please enter a title"),Display(Name ="Item")]
public string ItemName { get; set;}
[Required(ErrorMessage ="Category is required")]
public string Category { get; set;}
public string Model { get; set;}
public string Description{get; set;}
public ICollection Assigned { get; set; }
}
}
'System.Collections.Generic.ICollection' does not contain a definition for 'Any' and no accessible extension method 'Any' accepting a first argument of type 'System.Collections.Generic.ICollection' could be found (are you missing a using directive or an assembly reference?
Screenshot of error
Every Item has a property Assigned, which is of type ICollection.
If you look at the definition of Queryable.Any you'll find:
public static bool Any<TSource> (this IQueryable<TSource> source,
Expression<Func<TSource,bool>> predicate);
Therefore property Assigned should implement ICollection<TSource> where TSource is the actual type you expect to find in your collection.
The proper solution would be to change the type of property Assigned, such that you know that the collection only contains items of the type you expect. Alas you chose to give us a lot of unimportant information, but you forgot to mention the class of items in Assigned, so let's assume it's of type MyClass.
class MyClass
{
public Datetime ReturnDate {get; set;}
...
}
class Item
{
...
public ICollection<MyClass> Assigned {get; set;}
}
This will solve your problem: Any(...) works, and you are certain that people can only put MyClass objects in the collection.
If you don't want to limit your collection to MyClass objects, you can at least limit yourself to classes with property ReturnDate:
interface IMyInterface
{
public Datetime ReturnDate {get; set;}
}
class Item
{
...
public ICollection<IMyInterface> Assigned {get; set;}
}
If you really want that people can add other things than MyClass objects in the collection, something I strongly advise against, you'll have to specify what you want:
Ignore items in the collection that are not MyClass objects? Use OfType
Just hope that no one ever put something else in the collection? Use Cast, but then again: why not prevent that someone puts an incorrect object in the class, and define ICollection<MyClass>?
OfType probably does not works AsQueryable, in that case you should move the data to your local process using AsEnumerable. A third reason to use a correct type for ICollection right.
IsAvailable = !b.Assigned.OfType<MyClass>().Any(h => h.ReturnDate == null);
IsAvailable = !b.Assigned.Cast<MyClass>().Any(h => h.ReturnDate == null);
The first one will ignore items of your collection with other types. The latter one will throw an exception if there is another type in your collection (fourth reason to make it an ICollection<MyClass>

MVC varing relations between tables

I'm new to MVC and am used to using Forms. My question is suppose I have basic models setup for four tables with the following fields;
Branch
------
Branch_Nu
Branch_Address
Orders
-----
Order_Nu
Branch_Nu
Product_Nu
Customer_Nu
totcost
Products
---------
Product_Nu
Product
Price
Customer
----------
Customer_Nu
Name
Address
City
St
Zip
I'm interested in the following scenarios,
I want to see all orders for a branch;
Branch->orders
I want to see all orders for a customer from a particular branch;
Branch->orders->products
|-->customer
I want to see all orders for a customer regardless of branch they
purchased from;
customer->orders->branch
I want to see all branches that sold a particular product;
products->orders->Branch
I want to see which customers bought a particular product;
products->orders->customer
Question is can I use different controllers for the different scenarios using the same basic models that is submitted to different controller methods, or do I need different models for the different scenarios which is then submitted to different controller methods?
If I were using forms I would just have a different select statements and forms for each scenario, in MVC?
your entity framework models represent the relationships between your classes. In a code first approach, your models will dictate the design of your database. Take the following three classes for example:
public class Cat
{
[Key]
public int Id {get; set;}
public string Name {get; set;}
//Foreign key to animalgroup table
public int AnimalGroupId {get; set;}
//navigation property to AnimalGroup
//allows you to do myCat.AnimalGroup outside of this class to retrieve
//the associated animal group
public virtual AnimalGroup AnimalGroup {get; set;}
}
public class Dog
{
[Key]
public int Id {get; set;}
public string Name {get; set;}
public bool IsFluffy {get; set;}
//Foreign key to animalgroup table
public int AnimalGroupId {get; set;}
//navigation property to AnimalGroup
//allows you to do myDog.AnimalGroup outside of this class to retrieve
//the associated animal group
public virtual AnimalGroup AnimalGroup {get; set;}
}
public class AnimalGroup
{
[Key]
public int Id {get; set;}
public string Name {get; set;}
public virtual ICollection<Cat> Cats {get; set;}
public virtual ICollection<Dog> Dogs {get; set;
}
this represents two one-to-many relationships. A AnimalGroup can contain multiple Cats and an AnimalGroup can contain many Dogs. You can write queries to do CRUD operations. A very simplistic example is the following:
//create and save an animal group
AnimalGroup group = new AnimalGroup();
group.Name = "my animal group";
_dbContext.AnimalGroups.Add(group);
_dbContext.SaveChanges();
//create and save a cat associated with the animal group
Cat myCat = new Cat();
cat.Name = "kitty";
cat.AnimalGroupId = group.Id;
_dbContext.Cats.Add(myCat);
_dbContext.SaveChanges();
Question is can I use different controllers for the different scenarios using the same basic models
Yes
do I need different models for the different scenarios which is then submitted to different controller methods
You don't need them, but generally speaking using a model for a different logical function tends (not always) to break the Single Responsibility Principle.
In your instance it appears you are viewing the same data by different views (not logic). An order is an order, for viewing purposes anywhere on your site, generally I use the same model. Placing an order (different logic function) I would most likely have a new model.
Considering you are viewing the same data this is a classic example of the best use of MVC Templates.
If I were using forms I would just have a different select statements and forms for each scenario, in MVC?
I would probably design it like:
I want to see all orders for a branch;
Branch->orders
public class BranchController() { public ActionResult Orders() {}}
I want to see all orders for a customer from a particular branch;
Branch->orders->products
|-->customer
public class BranchController()
{
public ActionResult Orders() {}
public ActionResult CustomerOrders() {}
public ActionResult ProductOrders() {}
}
// ETC
A model example:
public class OrderVM
{
//I would get rid of hungarian notation
// https://stackoverflow.com/questions/111933/why-shouldnt-i-use-hungarian-notation
public int OrderId { get; set; }
public int BranchId { get; set; }
public int ProductId { get; set; }
public int CustomerId { get; set; }
}
Then store how the html is rendered in /views/shared/templates/display/OrderVM.cshtml so it can be used throughout the application, allowing overrides per controller/area as well.

How to access properties of complex type in OData

How can we form an OData query to access the Name property of complex property ProductDetails in the ProductDTO class?
public class ProductDTO
{
public int Id { get; set; }
public string ProductName { get; set; }
public string Description { get; set; }
public ProductDetails ProductDetails { get; set; }
}
public class ProductDetails
{
public string Name { get; set; }
public string Price { get; set; }
public string Discount { get; set; }
public string ManufacturedDate { get; set; }
}
This query gives me ProductDetails:
/Products?$select=ProductDetails
{"#odata.context":"http://localhost:59909/$metadata#Products(ProductDetails)","value":[{"ProductDetails":{"Name":"Laptop","Price":"100299","Discount":"1000","ManufacturedDate":"12:01:2016
09:30:875"}}]}
According to this post, this isn't possible for a $select. However, it isn't clear what you are trying to achieve from your question so I thought I would post this in case it helps. For a single object, you can get the value of a nested property like this, here is an example using the TripPin example OData service: http://services.odata.org/V4/TripPinServiceRW/Airports('KLAX')/Location/Address/$value Here, the Location property is a complex type and we are getting just the value of the Address property on that object.
Try to use $expand clause
In my situation i had to add a complexType first:
builder.ComplexType<BalanceSheet>();
Additionally I found that different query should be used to get the complex type on UI
Instead of calling http://url/api/AccountDetails?$select=name,accountNumber,balance another url should be used:
http://url/api/AccountDetails?$select=name,accountNumber,balance&$expand=balance
you can only see complex properties like balance via $expand
Also, important to have $expand feature turned on. To do that add it before you add the edm model:
endpoints.Select().Expand().Filter().OrderBy().Count().MaxTop(10);
endpoints.MapODataRoute("odata", "odata", this.GetEdmModel());
See details here: https://stackoverflow.com/a/55476645/2050539

MVC Filter Database with IIdentity

In my MVC Site a Teacher login and should view his classes and all books used in his classes.
The teacher should not be able to see someone else class or book.
Here's my model
public class Teacher
{
public int Id { get; set; }
public string Name {get; set;}
}
public class Books
{
public int Id {get; set;}
public string Title {get; set;}
public int CourseId {get; set;}
}
public class Course
{
public int Id {get; set;}
public int Name {get; set;}
public int TeacherId {get; set;}
}
I use a RepositoryPattern with UnitOfWork.
In order to get all classes tobe showned I should put in my Controller this kind of line :
var classes = classRepository.All.Where(x => x.TeacherId == currentTeacher.Id)
Things will get worst with the BookControler since I need to check all classes of the currentTeacher:
var classes = classRepository.All.Where(x => x.TeacherId == currentTeacher.Id)
var books = bookRepository.All.Where(x => classes.Contains(y => y.Id == x.CourseId)
It seems to me that this kind of approach may lead to some functional bug.
what I would like to do is that when a teacher login my repositories auto-filter in order to keep only datas concerning the currentTeacher, therefore in my controller I would have :
var classes = classRepository.All; // Already filtered on currentTeacher
or
var books = bookRepository.All; // Already filtered on currentTeacher
Is this possible? And how?
Create a new All(IPrincipal user) method in which you pass in the current authenticated user. This All method will lookup the Identity.Name value in your object context's Teacher collection to get the ID which is then returned.
i.e. .Where(x => x.TeacherId == currentTeacher.Id) is the return value from this new All method.

Entity Framework 4.1 Code First: Single column foreign-key to multiple entities

I've been trying to create model in EF 4.1 to represent a database schema with a single table and column holding foreign keys from two other tables, but have had little luck with both annotations and the fluent API. A sample model is shown here:
public class User
{
...
public virtual ExtendedAttribute ExtendedAttributes { get; set; }
}
public class Account
{
...
public virtual ExtendedAttribute ExtendedAttributes { get; set; }
}
public class ExtendedAttribute
{
public Guid Id {get; set;}
public Guid ItemId {get; set;} // both Account.Id and User.Id stored here
public string Value { get; set; }
}
Currently the configuration for these entities looks something like this for both User and Account modelBuilders:
this.HasOptional(u => u.ExtendedAttributes).WithRequired();
Any thoughts on how to do achieve? Many thanks.
It is even not possible with the database itself and EF will not put any abstraction for that. You must have separate column and navigation property for each entity.

Resources