I have created my application by using code-first approach in ASP.NET MVC 4.
I have three entities. Namely, "Company", "Service" and "ServiceFeature":
public class Company
{
public int CompanyID { get; set; }
[Required]
public string Name { get; set; }
[Required]
public string Description { get; set; }
public virtual Service SuppliedService { get; set; }
}
public class Service
{
public int ServiceID { get; set; }
[Required]
public string Name { get; set; }
public virtual ICollection<ServiceFeature> ServiceFeatures { get; set; }
}
public class ServiceFeature
{
public int ServiceFeatureID { get; set; }
[Required]
public string Name { get; set; }
}
I have a search form, it consists of checkboxes for all ServiceFeatures. User will select checkboxes and get the results of Companies that are providing the Services with selected ServiceFeatures.
I get the Company list with my service as below but I'm stuck at how to include the selected ServiceFeatures in a where clause (dynamic LINQ with a for loop?)
var searchResults = _companyService.GetCompanies();
Assuming that you have a collection containing the IDs of the selected features, called requested, and assuming you want the companies supplying a service which contains ALL the selected features, you could for example do this:
var q = from c in searchResults
let sf = c.SuppliedService.ServiceFeatures
.Select(f => f.ServiceFeatureID)
.Intersect(requested)
where sf.Count() == requested.Count()
select c;
In similar cases I prefer an approach that's a bit more elaborate than a linq query with Intersect. Intersect can produce horrible queries with deep nesting, because the list with Id values to intersect over is built by SELECT and UNION commands for each Id in the list. This is not a problem when the number of Id's is low (true in your case, I assume), but with larger numbers it may throw a SQL exception.
So this is what I'd prefer:
var q = context.Companies.AsQueryable();
foreach(int i in featureIds)
{
int j = i; // prevent modified closure.
q = q.Where(c => c.SuppliedService.ServiceFeatures.Any(f => f.Id == j));
}
var result = q.ToList();
It builds a query with a number of WHERE EXISTS clauses. This can be very efficient because each EXISTS will seek (not scan) the primary index of ServiceFeature. Besides that, an INTERSECT is an implicit DISTINCT.
Well, just wanted to point this out. As said, with low numbers of records you won't notice any difference. So take whatever suits you best.
Related
now I'll try describe my situation correctly:
I have 2 models with same properties here is this models:
[Table("OperatorA")]
public class OperatorA
{
[Key]
public string Id { get; set; }
public string Number { get; set; }
public string CallTime { get; set; }
public string Duration { get; set; }
public decimal Cost { get; set; }
}
And
[Table("OperatorB")]
public class OperatorB
{
[Key]
public string Id { get; set; }
public string Number { get; set; }
public string CallTime { get; set; }
public string Duration { get; set; }
public decimal Cost { get; set; }
}
After that I have inserted data in this tables and everything works fine, but -
now I need compare this two tables:
1. Check duplicate Number property in both table and view theme.
and
2. View all Number values that is not duplicate.
Can anyone help me with controller.
public async Task<ActionResult> Index()
{
var operatorA = await db.OperatorAs.ToListAsync();
var operatorB = await db.OperatorBs.ToListAsync();
// Want get all operatorB Numbers witch not equals OperatorA Numbers
}
Be aware that a query like this can easily cause big performance problems, depending of the number of records in the lists. Getting the whole lists to your application server with await and ToListAsync and later filtering the results in your controller is usually not a good practice. Use it only if you are sure that your lists will not get longer than a few hundreds of records (as a raw estimation). Do some metrics of execution times and amount of data used by the operation.
The best solution would be to do the query filtering in the database server, with the joins and filters applied before doing the .ToList(), because when you materialize the resulting list of records the query is executed in the database, and any further filtering or processing would not be done in the database but in the application server.
This example query that uses a join between the two tables would get you the list of OperatorA elements with Number repeated in OperatorB:
var query = db.OperatorAs
.Join<OperatorA, OperatorB, string, OperatorA>(
db.OperatorBs,
a => a.Number,
b => b.Number,
(a, b) => a)
.ToList();
It generates this SQL:
SELECT
[Extent1].[Id] AS [Id],
[Extent1].[Number] AS [Number],
[Extent1].[CallTime] AS [CallTime],
[Extent1].[Duration] AS [Duration],
[Extent1].[Cost] AS [Cost]
FROM [dbo].[OperatorAs] AS [Extent1]
INNER JOIN [dbo].[OperatorBs] AS [Extent2]
ON ([Extent1].[Number] = [Extent2].[Number])
OR (([Extent1].[Number] IS NULL) AND ([Extent2].[Number] IS NULL))
If the lists are really big you can also cause performance problems even in the database server, but database servers are better prepared to deal with these issues than application servers. There are ways to fix it as well.
I have this maintenance scheduling system, and in im using MVC C# Entity Framework,
I have this table
Truck Table that has - truck id, registration no., kilometer run reading, and i have an JobOrder Table, i want to get the truck id in the truck table so that i can add a JobOrder on a specific Truck i have this JobOrder Controller
public ActionResult AddJobOrder(JobOrderModel jo, int id)
{
var AddJobOrder = db.trucks.FirstOrDefault(s => s.id == id);
var addjo = new joborder()
{
truck_no = AddJobOrder,
description = jo.Description,
worked_performed = jo.worked_performed,
quantity = jo.Quantity,
spare_parts = jo.SpareParts,
run = jo.kilometer_run,
date_finished = jo.DateFinished,
date_started = jo.DateFinished,
};
if (!ModelState.IsValid)
{
db.joborders.Add(addjo);
db.SaveChanges();
return View(jo);
}
return View(jo);
}
I receive the following error:
Error 4 Cannot implicitly convert type 'Pms_system.truck' to 'int'
This is my model
public class JobOrderModel
{
public int truck_no { get; set; }
public DateTime DateStarted { get; set; }
public DateTime DateFinished { get; set; }
public string SpareParts { get; set; }
public int Quantity { get; set; }
public string Description { get; set; }
public int kilometer_run { get; set; }
public string worked_performed { get; set; }
}
Please help me to get the truck id.
It looks like this is just a matter of getting the ID out of the truck - after all, your oddly-named AddJobOrder variable is presumably of type Truck or something similar. I suspect you just need something like:
truck_no = AddJobOrder.id,
After all, that's how you're getting at the truck ID in the query just beforehand. It's not entirely clear why you need the query at all if you only need the truck ID, which has been provided to the method anyway - all you're doing at the moment is allowing you to check whether or not there is such a record, although you should then actually do the check by seeing whether FirstOrDefault returned null.
I would also strongly advise you to take a good look at the names you're using, both in terms of capitalization and semantic names too. (Your AddJobOrder variable should be called truck or something similar by the sounds of it. The fact that it's the same name as the method is doubly confusing!)
Useing Entity framework I want to include an only the first level of children objects and not the children of child
I have these two classes:
public class BusinessesTBL
{
public string ID { get; set; }
public string FirstName { get; set; }
public string lastName { get; set; }
public ICollection<OffersTBL> OffersTBLs { get; set; }
}
public class OffersTBL
{
public int ID { get; set; }
public string Name { get; set; }
public int CatId { get; set; }
public string BusinessesTBLID { get; set; }
public virtual BusinessesTBL BusinessesTBLs { get; set; }
}
when I try to bring all offers according to CatId field, I need to return the BusinessesTBLs also, but the method also return offers again per each BusinessesTBL obj , My code is :
public IQueryable<OffersTBL> GetOffersTBLsCat(int id)
{
db.OffersTBLs.Include(s => s.BusinessesTBLs);
}
You can see the wrong result on :
http://mycustom.azurewebsites.net/api/OffersApi/GetOffersTBLsCat/4
As you can see it return all offers under each Business object while business object under each offer, And I want only to return offers with its Business object without offer under Business obj.
Could anyone help please?
I now see that a big part of the original answer is nonsense.
Sure enough, the reason for the endless loop is relationship fixup. But you can't stop EF from doing that. Even when using AsNoTracking, EF performs relationship fixup in the objects that are materialized in one query. Thus, your query with Include will result in fully populated navigation properties OffersTBLs and BusinessesTBLs.
The message is simple: if you don't want these reference loops in your results, you have to project to a view model or DTO class, as in one of the other answers. An alternative, less attractive in my opinion, when serialization is in play, is to configure the serializer to ignore reference loops. Yet another less attractive alternative is to get the objects separately with AsNoTracking and selectively populate navigation properties yourself.
Original answer:
This happens because Entity Framework performs relationship fixup, which is the process that auto-populates navigation properties when the objects that belong there are present in the context. So with a circular references you could drill down navigation properties endlessly even when lazy loading is disabled. The Json serializer does exactly that (but apparently it's instructed to deal with circular references, so it isn't trapped in an endless loop).
The trick is to prevent relationship fixup from ever happing. Relationship fixup relies on the context's ChangeTracker, which caches objects to track their changes and associations. But if there's nothing to be tracked, there's nothing to fixup. You can stop tracking by calling AsNoTracking():
db.OffersTBLs.Include(s => s.BusinessesTBLs)
.AsNoTracking()
If besides that you also disable lazy loading on the context (by setting contextConfiguration.LazyLoadingEnabled = false) you will see that only OffersTBL.BusinessesTBLs are populated in the Json string and that BusinessesTBL.OffersTBLs are empty arrays.
A bonus is that AsNoTracking() increases performance, because the change tracker isn't busy tracking all objects EF materializes. In fact, you should always use it in a disconnected setting.
You have deactivated lazy loading on OffersTBLs making it non-virtual. What if you activate lazy loading? like this:
public class BusinessesTBL
{
public string ID { get; set; }
public string FirstName { get; set; }
public string lastName { get; set; }
//put a virtual here
public virtual ICollection<OffersTBL> OffersTBLs { get; set; }
}
Then, be sure to not call/include OffersTBLs when serializing. If the OffersTBLs are still returning, it is because you are fetching them somewhere in your code. If this is happening, edit your question and paste all the code, including the serializing logic.
Since OffersTBL has an association to BusinessesTBL and BusinessesTBL to OffersTBL you can loop infinitly throw the Entities like OffersTBL.BusinessesTBL.OffersTBL.BusinessesTBL and so on.
To control the nested depth of the Entities i'm usually using helperclasses with the needed properties in them.
For BusinessesTBL
public class BusinessesTBLHelper
{
private BusinessesTBLHelper(BusinessesTBL o){
ID = o.ID;
FirstName = o.FirstName;
lastName = o.LastName;
OffersTBLids = new List<int>();
foreach(OffersTBL offersTbl in o.OffersTBLs){
OffersTBLids.Add(offersTbl.ID);
}
}
public string ID { get; set; }
public string FirstName { get; set; }
public string lastName { get; set; }
public IEnumerable<int> OffersTBLids { get; set; } //no references anymore
}
And same for your OffersTBL Entity.
public class OffersTBLHelper
{
private OffersTBLHelper(OffersTBL o){
ID = o.ID;
Name = o.Name;
CatId = o.CatId;
BusinessesTBLID = o.BusinessesTBLID;
BusinessesTBLs = new BusinessesTBLHelper(o.BusinessesTBLs);
}
public string ID { get; set; }
public string Name{ get; set; }
public intCatId{ get; set; }
public string BusinessesTBLID { get; set; }
public BusinessesTBLHelper BusinessesTBLs { get; set; }
}
On quering database you can directly create the new helperobjects from queryresult:
public IEnumerable<OffersTBLHelper> GetOffersTBLsCat(int id)
{
return db.OffersTBLs.where(s => s.CatId == id).Select(x=> new OffersTBLHelper(x)).ToList();
}
Now you have all the OffersTBL with BusinessesTBLs under. The loop stops here because the BusinessesTBLs have no OffersTBL under it. However, it only has them Ids in a List for further referencing and identifying.
Assuming that the object isnt null and just empty:
public IQueryable<OffersTBL> GetOffersTBLsCat(int id)
{
db.OffersTBLs.Include(s => s.BusinessesTBLs).Where(x => !x.BusinessesTBLs.OffersTBLs.Any());
}
Edit: Filter before the include:
public IQueryable<OffersTBL> GetOffersTBLsCat(int id)
{
db.OffersTBLs.Where(x => !x.BusinessesTBLs.OffersTBLs.Any())
.Include(s => s.BusinessesTBLs);
}
I am currently trying to develop my first .NET MVC application and so learning the main concepts.
In my application I have a table that displays a list of animals from an animals table. In the table I am also trying to display the animal breed, but I am pulling the breed from the Breed table on the foreign key stored in the Animal table
I am currently trying to use a Navigation Property to display the Breed text and not the ID so I
altered my Animal model to look like this
public partial class Animal
{
public int AnimalId { get; set; }
public Nullable<int> UserId { get; set; }
public string TagNo { get; set; }
public Nullable<int> Species { get; set; }
public Nullable<bool> Sex { get; set; }
public Nullable<int> AnimalBreed { get; set; }
public Nullable<System.DateTime> DOB { get; set; }
public Nullable<int> OwnershipStatus { get; set; }
public Nullable<System.DateTime> DateAdded { get; set; }
public Nullable<bool> BornOnFarm { get; set; }
public virtual Breed Breed { get; set; }
}
And my breed model looks like
public partial class Breed
{
public int id { get; set; }
public Nullable<int> SpeciesID { get; set; }
public string Breed1 { get; set; }
}
In my view I am trying to display the Breeds field from my animal model as shown below, but the breed column is just empty
<td>
#Html.DisplayFor(modelItem => item.Breed.Breed1)
</td>
Also finally, here is the code that i am using to send the model to the view
List<Animal> Animal1 = (from animals in db.Animals
where animals.Species == 2 && animals.OwnershipStatus == 1
&& animals.UserId == WebSecurity.CurrentUserId
select animals).ToList();
return View(Animal1);
First, don't pluralize single items. It creates confusion in your code:
public virtual Breed Breed { get; set; }
-or-
public virtual ICollection<Breed> Breeds { get; set; }
The virtual attribute allows lazy-loading (a query to fetch the breed will be issued the first time you try to access it). You pretty much always want to include virtual with the property so Entity Framework does not unnecessarily issue joins if you don't actually end up using the property. However, in this case, you are, so you'll want to tell EF to eager-load it by including .Include("Breed") in your query. However, that's just for optimization; it's not your problem here.
Your problem here is that Razor doesn't know how to display Breed. It's not a normal type, obviously, because you created it. So, what you really need is to display the actual property on Breed that you want:
#Html.DisplayFor(m => m.Breed.Breed1)
There's an alternate method, but it's more complex and probably overkill for this scenario. If you really want to use Breed directly, then you need to define a display template for Breed. You do that by adding a new folder to Views\Shared named DisplayTemplates. Inside that folder, add a view named Breed.cshtml. The name of the view here corresponds to the class name, not the property name. Inside that view, you'd do something like:
#model Namespace.To.Breed
#Html.DisplayFor(m => m.Breed1)
Then, in your view you could just do:
#Html.DisplayFor(m => m.Breed)
And Razor will use the display template to render the appropriate thing. Like I said, it's overkill for this, but in more complex object rendering, it might come in handy.
If lazy loading is not enabled in your DbContext, then you have to explicitly load (or use eager loading) the navigation properties.
See http://msdn.microsoft.com/en-us/data/jj574232.aspx
You'll end-up with something like:
var res = (from animals in db.Animals.Include("Breeds")
where animals.Species == 2 & animals.OwnershipStatus == 1
& animals.UserId == WebSecurity.CurrentUserId
select animals).ToList();
I use breezejs in my Durandal web application.
Here is my code to get my invoice & lines behind it:
var getInvoiceById = function (invoiceId, invoiceObservable, forceRemote) {
// Input: invoiceId: the id of the invoice to retrieve
// Input: forceRemote: boolean to force the fetch from server
// Output: invoiceObservable: an observable filled with the invoice
if (forceRemote)
queryCacheInvoice = {};
var query = entityQuery.from('Invoices')
.where('id', '==', invoiceId)
.expand("Client, Client.Contacts, Lines")
.orderBy('Lines.Number');
var isInCache = queryCacheInvoice[invoiceId];
if (isInCache && !forceRemote) {
query = query.using(breeze.FetchStrategy.FromLocalCache);
} else {
queryCacheInvoice[invoiceId] = true;
query = query.using(breeze.FetchStrategy.FromServer);
}
return manager.executeQuery(query)
.then(querySucceeded)
.fail(queryFailed);
function querySucceeded(data) {
invoiceObservable(data.results[0]);
}
};
And here is the models for Invoice:
public class Invoice
{
[Key]
public int Id { get; set; }
public string Number { get; set; }
public DateTime? Date { get; set; }
public int? ClientId { get; set; }
public string Comment { get; set; }
public double? TotalExclVAT { get; set; }
public double? TotalInclVAT { get; set; }
public double? TotalVAT { get; set; }
public bool? WithoutVAT { get; set; }
public virtual List<InvoiceLine> Lines { get; set; }
public virtual Client Client { get; set; }
}
Please notice that for each invoice I have many invoice lines:
public virtual List<InvoiceLine> Lines { get; set; }
And here is the models for InvoiceLine:
public class InvoiceLine
{
[Key]
public int Id { get; set; }
[Required]
public int Number { get; set; }
[Required]
public string Description { get; set; }
public int InvoiceId { get; set; }
public Invoice Invoice { get; set; }
}
The problem: when I execute this breeze query I got the error below:
Error retreiving data. unable to locate property: Lines on type: Invoice
The problem is around the orderBy clause. I have a 1-to-many relationship between the Invoice and the InvoiceLine so it seems I cannot perform an order by in this case.
My question: how to proceed to be able to sort my lines of invoice by number?
Thanks.
Short answer: You can't. This is a limitation of Entity Framework, not Breeze.
You cannot filter, select, or order the related entities that you include with "expand" in an EF LINQ query.
You will probably manage the sort order of related entities on the client, e.g., the display of your order line items.
Note also that the collection of entities returned by a Breeze navigation path is unordered. I wasn't sure what happens if you tried to sort a Breeze entity navigation collection (e.g., Order.LineItems). I was afraid that would cause Breeze to think that you had made changes to the entities ... because a sort would seem to remove-and-add entities to the collection as it sorted. Your EntityManager would think there were changes pending when, in fact, nothing of substance has changed.
I tried an experiment and it all seems to work fine. I did something like this with Northwind:
fetched the Orders of a Customer ("Let's Stop N Shop")
checked the sequence of cust.Orders(); they have unordered OrderIDs: [10719, 10735, 10884, 10579]
executed a line like this: cust.Orders().sort(function(left, right){return left.OrderID() < right.OrderID()? -1 : 1})
checked the sequence of cust.Orders() again; this time they are sorted: [10579, 10719, 10735, 10884]
checked the customer's EntityManager.hasChanges() ... still false (no changes).
I confess that I am happily surprised. I need to write a proper test to ensure that this works reliably. And I have to make sure that the Knockout binding to the navigation property displays them in the sorted order. But I'm encouraged so far.
Important Notes:
Breeze won't keep the list sorted. You'll have to do that if you add new orders or if Breeze adds new orders to the collection as a result of subsequent queries.
Your sort affects every view that is bound to this navigation property. If you want each view to have its own sort of the entities in that collection, you'll have to maintain separate, view-specific collections that shadow the navigation property collection.
If I am wrong about all of this, you'll have to manage a shadow collection of the related entities, sorted as you wish, for each ViewModel.
Update 2 June
I suspected that we would have to let KO know about the array change after sort by calling valueHasMutated. I took 15 minutes for an experiment. And it seems to work fine.
I created a new KO Todo app from the ASP.NET SPA template (there's currently a phantom complaint about a missing jQuery.UI library which is totally unnecessary anyway).
I added a "sort link" to the index.cshtml just above the delete-TodoList link:
Sort
Then I implemented it in viewModel.js:
var sortAscending = true;
var viewmodel = {
...
sortList: sortList,
...
};
...
function sortList(list) {
list.todos().sort(function(left, right) {
return (sortAscending ? 1 : -1) *
(left.title().toLowerCase() < right.title().toLowerCase() ? -1 : 1);
});
sortAscending = !sortAscending; // reverse sort direction
list.todos.valueHasMutated(); // let KO know that we've sorted
}
Works like a charm. Sorting, adding, deleting Todos at will. The app is saving when expected as I add and delete ... but not during save.
Give valueHasMutated a try.