not getting child category when returning parent category in linq entity framework - linq

I want to return the parent category along with the subcategory. There is a self join in my table. This is my class:
public partial class CategoryMaster
{
public int Id { get; set; }
public string Name { get; set; }
public Nullable<int> ParentId { get; set; }//parentid is link to Id
public virtual ICollection<CategoryMaster> CategoryMaster1 { get; set; }
public virtual CategoryMaster CategoryMaster2 { get; set; }
}
I use this query to return the parent category and the child category:
var GetallCategories = context.CategoryMaster.Include("CategoryMaster1")
.Where(d => d.ParentId == null)
.OrderBy(d => d.Name)
.Select(d => new SelectListItem { Text = d.Name,
Value = d.Id.ToString()
})
.ToList();
The problem is that it is just returning parent category. Whats wrong with my query?

The first problem is your where clause. You told it to ONLY bring back CategoryMasters with a null parent. So you won't get any children out of the DB.
We have a more rigid (read: slower) pattern we follow where I work with our EF queries, so I've never done exactly what you're doing here, but I think all you have to do is move the where clause, like this:
var GetallCategories = context.CategoryMaster.Include("CategoryMaster1")
.OrderBy(d => d.Name)
.ToList();
.Where(d => d.ParentId == null)
.Select(d => new SelectListItem { Text = d.Name,
Value = d.Id.ToString()
})
The ToList forces a DB call and then the where and select after it get processed after it comes back from the DB, at which point, it's gotten all the data with no where clause. If I understand correctly, you do want all the data, you just want your list to contain only the parents at the top level and then drill down to get to the children.
So if you add any where clause before the tolist, it's going to restrict what comes back from the DB before it even tries to build the hierarchy of objects. If you look at the actual sql generated by EF, you can see this. This means that if you legitimately need a where clause - say you want to bring back all parents with a lastname of "Smith" plus their children - you have to make it more complex. If you need a meaningful where caluse to filter parents in the DB (which you might for performance) and you have to allow for an unknown number of generations of children, this becomes VERY difficult to do with EF includes.

Related

Is there a way I can paginate the included list in a linq query

Hope you're doing well,
I was trying to optimize my reads with entity framework, where I arrived at a position, where I get a record from database by id, and I want to include a one-to-many related list, but I don't want to get all data of the list, just a few, so I want to kind of paginate it.
I want to do this process as long as data is in IQueryable state, I don't want to load all data of list in memory and that paginate it as enumerable.
Let's say the query is like below:
var author = await _dbContext.Authors.Where(x => x.Id == id)
.Include(x => x.Books) // <-- paginate this !!??
.FirstOrDefaultAsync();
Entities represent Data state. Pagination and presentation concerns are View state. Entity Framework can help bridge that gap, but it does so by enabling projection so that you can build View state from Data state. Don't pass entities to views, instead build and pass ViewModels to represent the data in accordance to translations and limitations you want for the view.
For instance if you want to pass Author details with their 5 most recent books:
public class AuthorViewModel
{
public int Id { get; set; }
public string Name { get; set; }
// Any other relevant fields...
public ICollection<BookViewModel> RecentBooks = new List<BookViewModel>();
}
public class BookViewModel
{
public int Id { get; set; }
public string Name { get; set; }
public DateTime PublishedDate { get; set; }
// Any other relevant fields...
}
var author = await _dbContext.Authors
.Where(x => x.Id == id)
.Select( x => new AuthorViewModel
{
Id = x.Id,
Name = x.Name,
RecentBooks = x.Books
.OrderByDescending(b => b.PublishedDate)
.Select(b => new BookViewModel
{
Id = b.Id,
Name = b.Name,
PublishedDate = b.PublishedDate
}).Take(5)
.ToList()
}).SingleOrDefault();
This gives you the benefit of structuring the data how you want to present it, while generating efficient queries against the database. You can configure tools like Automapper to perform this kind of mapping to use it's ProjectTo<AuthorViewModel>() as a more succinct alternative.

Include entities on a relationship with a custom property in EF Core

Is there a way to easily include entities from a relationship on a custom property in EF Core in a single query? For example, I have the following structure:
public class Report {
public int TenantId { get; set; }
public string Name { get; set; }
public List<Document> Documents { get; set; }
//...
}
public class Document {
public int TenantId { get; set; }
public int Id { get; set; }
//...
}
I can't change this structure, i.e. I can't add a property to the Report class to create another relation because Reports are not exclusively related to Documents and I don't want a reference to a Report in a Document.
In my DbContext I declared the relation like this:
modelBuilder.Entity<Document>(b => {
b.HasIndex(e => new { e.TenantId });
});
modelBuilder.Entity<Report>(b => {
b.HasIndex(e => new { e.TenantId });
b.HasMany(r => r.Documents).WithOne().HasPrincipalKey("TenantId");
});
What I want is to include all Documents in the Report for the specified TenantId. Think of the following database excerpt:
Documents
Id
TenantId
1
1
2
1
3
1
4
2
Reports
Name
TenantId
abc
1
def
2
When fetching all reports I want to have the possibility to include all of the tenant's Documents. For example, fetching the Report abc (Tenant 1) should also populate the List in the Report object with Documents 1-3 (because they're all Tenant 1). When fetching the Report def it should only populate the List with Document 4.
Unfortunately it doesn't work to just call...
reportRepository.GetAll().Where(r => r.Name == 'abc').Include(r => r.Documents)
... the list is always empty.
In fact, I could achieve exactly what I'm trying to accomplish with something like this:
var report = reportRepository.GetAll().Where(r => r.Name == 'abc').First();
report.Documents = documentRepository.GetAll().Where(d => d.TenantId == report.TenantId).ToList();
... but I would really like to have a cleaner solution that doesn't require two queries. This would also make it easier for later reusability because there are more of these structures in the code where I need similar functionality.
Is it possible in EF Core to convert the above two queries into one single query that just includes the Documents that relate on a given property (here TenantId)?
try set HasForeignKey and HasPrincipalKey
modelBuilder.Entity<Report>(b => {
b.HasIndex(e => new { e.TenantId });
b.HasMany(r => r.Documents)
.WithOne()
.HasPrincipalKey(d => d.TenantId)
.HasForeignKey(r => r.TenantId);
});

Linq Grouping looses the child entities

I have the following query:
var _customers = (from c in _db.UserProfiles.Include(x=>x.ParentCompanies).Include(x=>x.cProfile).Include(x=>x.cProfile.PhoneNumbers).Include(x=>x.cProfile.Addresses)
where (c.ParentCompanies.Any(pc => pc.CompanyUsers.Any(cu => cu.UserName == userName)) && c.cProfile != null)
group c by c.FirstName.Substring(0, 1).ToUpper() into customerGroup
select new ContactsViewModel
{
FirstLetter = customerGroup.Key,
Customers = customerGroup
}).OrderBy(letter => letter.FirstLetter);
if I take out the group, it works well and includes all the children (parentCompanies, cProfile, ...) as soon as I put the group back in it looses all of the children. How do I solve this issue?
update
I guess I should also include the view model that I'm usign to put the result in.
public class ContactsViewModel
{
public string FirstLetter { get; set; }
public IEnumerable<UserProfile> Customers { get; set; }
}
Include only applies to items in the query results (i.e. the final projection) and cannot contain operations that change the type of the result between Include and the outermost operation (e.g. GroupBy())
http://wildermuth.com/2008/12/28/Caution_when_Eager_Loading_in_the_Entity_Framework
If you want to eager load, do the grouping client-side (i.e. enumerate the query then call the GroupBy method on the results)

Can I query for and retrieve members of a collection property in RavenDB using full text search?

I'm using the term "child documents" loosely relating to objects stored in a collection property. Given those two classes:
public class Foo
{
public int Id { get; set; }
public string Name { get; set; }
public IList<Bar> Bars { get; set; }
}
public class Bar
{
public string Name { get; set; }
public string Description { get; set; }
public string SomeString { get; set; }
public int SomeInt { get; set; }
}
I would like to query for Bars which have the term "roses" in either their Name or Description properties. Please note that Bars are stored within Foo.
This is a two fold question:
Can a query be used over a collection of type Foo to return Bars ("child document")? I want to get a collection of Bars which have "roses" in their Name, other Bars should be skipped. I don't want Foos (aggregate root) returned.
If so, how to do it with full text search?
Regarding #1, I know something like that is impossible in MongoDB, where I would either have to store Foo and Bar separately or query for Foos where any of their Bars has "roses" in the Name and then do something about it on the client. But RavenDB has Live Projections / TransformResults, so I thought it would be possible perhaps.
You can store a copy of each Bar in field storage with the index, so yes - it can be done. But you should make sure you understand the impact of doing this.
Normally when you query from raven, the index is only used to determine which documents get sent back. The documents themselves don't come from the index, but from the document store. The document store has ACID guarantees - meaning you will always get the most current copy of the document regardless of the state of the index. If you project from index entries or index field storage, then the values you get back are as stale as the index itself.
Say you are constantly updating the Bars, and you search before the index has caught up to your last update. You could get back a Bar that has old data. Therefore, you need to weigh in the potential staleness of data into your results, possibly using one of the WaitForNonStaleResultsAsOf... customizations - which will slow the speed your search results return if you have lots of writes going on.
public class Foos_BarsByName
: AbstractIndexCreationTask<Foo, Foos_BarsByName.Result>
{
public class Result
{
public string Name { get; set; }
public Bar Bar { get; set; }
}
public Foos_BarsByName()
{
Map = foos => from foo in foos
from bar in foo.Bars
select new
{
bar.Name,
Bar = bar
};
Index(x => x.Name, FieldIndexing.Analyzed);
Index(x => x.Bar, FieldIndexing.No);
Store(x => x.Bar, FieldStorage.Yes);
}
}
var results = session.Query<Foos_BarsByName.Result, Foos_BarsByName>()
.Customize(x => x.WaitForNonStaleResultsAsOfNow())
.Search(x => x.Name, "roses")
.Select(x => x.Bar);
Another way to handle it might be to let all Foos come back, and then pull out the Bars you are interested in on the client side. At least then, everything comes from the document store:
public class Foos_BarsByName
: AbstractIndexCreationTask<Foo, Foos_BarsByName.Result>
{
public class Result
{
public string Name { get; set; }
}
public Foos_BarsByName()
{
Map = foos => from foo in foos
from bar in foo.Bars
select new
{
bar.Name
};
Index(x => x.Name, FieldIndexing.Analyzed);
}
}
var results = session.Query<Foos_BarsByName.Result, Foos_BarsByName>()
.Search(x => x.Name, "roses")
.As<Foo>()
.AsEnumerable()
.SelectMany(x => x.Bars)
.Where(x => x.Name.IndexOf("roses",
StringComparison.CurrentCultureIgnoreCase)
!= -1)
.AsEnumerable() will force the linq-to-raven query to execute, making everything that follows happen in linq-to-objects on the client side.
Of course, if you are doing a more advanced search than can be expressed with c# string functions, then you won't be able to take this second approach.

Coalesce fields in a .net MVC 4 model without getting "Only initializers, entity members, and entity navigation properties are supported" from LINQ

The answer to this question gave rise to this other question: How to use LINQ expressions as static members of classes in queries when the class is related multiple times to a second class
I have an existing ASP.net MVC 4 site which I need to modify.
The core entity within this site are Items that are for sale, which are created by several different companies and divided into several categories. My task is to allow each company its own optional alias for the global categories. Getting the two categories set up in the database and model was no problem, making the application use the new optional alias when it exists and default to the global otherwise is where I'm struggling to find the optimal approach.
Adding a coalesce statement to every LINQ query will clearly work, but there are several dozen locations where this logic would need to exist and it would be preferable to keep this logic in one place for when the inevitable changes come.
The following code is my attempt to store the coalesce in the model, but this causes the "Only initializers, entity members, and entity navigation properties are supported." error to be thrown when the LINQ query is executed. I'm unsure how I could achieve something similar with a different method that is more LINQ friendly.
Model:
public class Item
{
[StringLength(10)]
[Key]
public String ItemId { get; set; }
public String CompanyId { get; set; }
public Int32 CategoryId { get; set; }
[ForeignKey("CategoryId")]
public virtual GlobalCategory GlobalCategory { get; set; }
[ForeignKey("CompanyId, CategoryId")]
public virtual CompanyCategory CompanyCategory { get; set; }
public String PreferredCategoryName
{
get{
return (CompanyCategory.CategoryAlias == null || CompanyCategory.CategoryAlias == "") ? GlobalCategory.CategoryName : CompanyCategory.CategoryAlias;
}
}
}
Controller LINQ examples:
var categories = (from i in db.Items
where i.CompanyId == siteCompanyId
orderby i.PreferredCategoryName
select i.PreferredCategoryName).Distinct();
var itemsInCategory = (from i in db.Items
where i.CompanyId == siteCompanyId
&& i.PreferredCategoryName == categoryName
select i);
For one you are using a compiled function (getPreferredCategoryName) in the query, unless EF knows how to translate that you are in trouble.
Try the following in item definition:
public static Expression<Func<Item,String>> PreferredCategoryName
{
get
{
return i => (i.CompanyCategory.CategoryAlias == null || i.CompanyCategory.CategoryAlias == "") ?
i.GlobalCategory.CategoryName :
i.CompanyCategory.CategoryAlias;
}
}
Which is used as follows:
var categories = db.Items.Where(i => i.CompanyID == siteCompanyId)
.OrderBy(Item.PreferredCategoryName)
.Select(Item.PreferredCategoryName)
.Distinct();
This should work as you have a generically available uncompiled expression tree that EF can then parse.

Resources