I have 2 classes:
public class Category
{
public int Id { get; set; }
[Required(ErrorMessage = "Pole nie może być puste")]
public string Name { get; set; }
public List<Subcategory> Subcategories {get;set;}
}
public class Subcategory
{
public int Id { get; set; }
[Required(ErrorMessage = "Pole nie może być puste")]
[DisplayName("Nazwa podkategorii")]
public string Name { get; set; }
public int CategoryId { get; set; }
public Category Category { get; set; }
}
And I have got class where I am trying get my object "Category"
public IEnumerable<Category> GetAllCategories()
{
return context.Categories.Include(c => c.Subcategories);
}
public Category GetCategory(int Id)
{
return context.Categories.Find(Id);
}
Method GetAllCategories works fine, but when I use GetCategory I get only Category data without related object "Subcategory" list. Someone can help me with my problem? How can I get whole object?
You need to include relations that you need:
public Category GetCategory(int Id)
{
return context.Categories.Include(e => e.Subcategories).Single(e => e.Id == Id);
}
You can see more examples here: https://learn.microsoft.com/en-us/ef/core/querying/related-data
Related
I want to show the books belonging to the author on the author edit page, but all the books are displayed on the page. I want to select and show only the books belonging to that author.
Admin Controller Page :
[HttpGet]
public IActionResult AuthorEdit(int? id)
{
if(id==null)
{
return NotFound();
}
var entity = _authorService.GetByIdWithBooks((int)id);
if(entity==null)
{
return NotFound();
}
var model = new AuthorModel()
{
Books = _bookService.GetAll(),
AuthorId = entity.AuthorId,
NameLastName = entity.NameLastName,
Description = entity.Description,
};
return View(model);
}
GetByIdWithBooks
public Author GetByIdWithBooks(int authorId)
{
return BookContext.Authors
.Where(i=>i.AuthorId==authorId)
.FirstOrDefault();
}
Book Model :
public class Book
{
public int BookId { get; set; }
public string Name { get; set; }
public string Url { get; set; }
public double? Price { get; set; }
public string Description { get; set; }
public string ImageUrl { get; set; }
public string BarcodeNumber { get; set; }
public int PageCount { get; set; }
public string FirstPrintDate { get; set; }
public bool IsApproved { get; set; }
public bool IsHome { get; set; }
public DateTime DateAdded { get; set; }
public List<BookCategory> BookCategories { get; set; }
public int AuthorId { get; set; }
public Author Author { get; set; }
public int PublisherId { get; set; }
public Publisher Publisher { get; set; }
}
Author Model :
public class Author
{
public int AuthorId { get; set; }
public string NameLastName { get; set; }
public string ImageUrl { get; set; }
public string Description { get; set; }
public List<Book> Books { get; set; }
}
If you're using EF you could just change GetByIdWithBooks() method to do it, and it would make sense.
public Author GetByIdWithBooks(int authorId)
{
return BookContext.Authors
.Include(c => c.Books)
.Where(i=>i.AuthorId==authorId)
.FirstOrDefault();
}
Since you have a FK between Books -> Author the "Include" would make the necessary joins to bring back the related books.
Or, if you want to keep the _booksService.GetAll() call, which in my opinion, may not make a lot of sense:
_bookService.GetAll().Where(c => c.AuthorId == id)
Which should probably be a different method inside your service.
Is that what you were trying to achieve?
I create a forum and I have a problem: how can I add the number of posts for each category? I would like to use the viewmodel
public class Category
{
public int Id { get; set; }
public string Name { get; set; }
public string Description { get; set; }
public virtual List<Post> Posts { get; set; }
}
public IActionResult Index()
{
var model = categoryService.GetAll();
return View(model);
}
You can add a property to your ViewModel that will retrieve the number of Post's for that Category.
It could look something like this:
public class Category
{
public int Id { get; set; }
public string Name { get; set; }
public string Description { get; set; }
public virtual List<Post> Posts { get; set; }
public int PostCount
{
get
{
return Posts != null ? Posts.Count : 0;
}
}
}
I have two classes:
public class Category
{
public int Id { get; set; }
[Required]
[MaxLength(255)]
public string Name { get; set; }
public int? CategoryId { get; set; }
public double Weight { get; set; }
public ICollection<Article> Articles { get; set; }
public bool Hidden { get; set; }
}
public class Article
{
public int Id { get; set; }
[StringLength(255)]
public string Title { get; set; }
public string Body { get; set; }
public double Weight { get; set; }
public Category Category { get; set; }
public int? CategoryId { get; set; }
}
I would like to select some Categories including Articles, but without Article.Body. Method syntax is more preferred.
Something like:
IEnumerable<Category> categories = _context
.Categories
.Where(c => c.Hidden == false)
.Include(c => c.Articles)
.OrderBy(c => c.Weight);
Not sure how to specify which columns exactly to select (eagerly) on the included Articles.
Include doesn't allow projections, you can only include complete entities.
But there is a way out.
This is a typical case that you should solve by table splitting. By table splitting you "split" a table over two (or more) entities, so it's easier to filter e.g. light data from heavy data or public data from secure data.
In your case the class model (for Article) would look like this:
public class Article
{
public int Id { get; set; }
[StringLength(255)]
public string Title { get; set; }
public double Weight { get; set; }
public Category Category { get; set; }
public int? CategoryId { get; set; }
public virtual ArticleBody ArticleBody { get; set; }
}
public class ArticleBody
{
public int Id { get; set; }
public string Text { get; set; }
}
And the mappings:
modelBuilder.Entity<Article>()
.HasRequired(a => a.ArticleBody)
.WithRequiredPrincipal();
modelBuilder.Entity<Client>().ToTable("Article");
modelBuilder.Entity<ArticleBody>().ToTable("Article");
Now if you do...
_context.Categories
.Where(c => !c.Hidden)
.Include(c => c.Articles)
...you'll see that only Articles without body texts will be selected in the generated SQL.
If you want the body as well, you do
_context.Categories
.Where(c => !c.Hidden)
.Include(c => c.Articles.Select(a => a.ArticleBody))
Sorry if i did not understand your question, but I think you can specify what columns you want in your select statement.
Simple example:
var query = from c in Categories
select c.Name, c.CategoryId;
I'm having trouble querying RavenDB with even the simplest of queries, probably I'm doing something wrong, but after a few hours I just can't see it anymore. I've googled almost anything I can think of..
I have these entities:
public class User
{
public string Id { get; set; }
public string DisplayName { get; set; }
public string RealName { get; set; }
public string Email { get; set; }
public string PictureUri { get; set; }
public List<Comment> Comments { get; set; }
public List<Role> Roles { get; set; }
}
public class NewsItem
{
public string Id { get; set; }
public string Title { get; set; }
public string Text { get; set; }
public DateTime Created { get; set; }
public string UserId { get; set; }
public List<Tag> Tags { get; set; }
public List<Comment> Comments { get; set; }
public List<WebImage> Images { get; set; }
}
I want to query these so I get a list of newsItems, but with the user information alongside it. So I read the docs and tried the LoadDocument feature, the index:
public class NewsItemIndexWithComments : AbstractIndexCreationTask<NewsItem, NewsItemIndexWithComments.Result>
{
public class Result
{
public string AuthorName { get; set; }
}
public NewsItemIndexWithComments()
{
Map = newsItems => from newsItem in newsItems
select new
{
AuthorName = LoadDocument<User>(newsItem.UserId).DisplayName
};
}
}
Which I try to use like:
var result = _documentSession.Query<NewsItemIndexWithComments.Result, NewsItemIndexWithComments>().AsProjection<NewsItemIndexWithComments.Result>().ToList();
Now I get the number of documents in my list, but the AuthorName is always null. If I don't use the AsProjection method, I won't get any results. Can anyone show me a proper example on which I can experiment further?
Thanks.
_ edit:
That helped a lot, thanks :) Now for step two, I'm sorry if I'm being a bit newbish, but you'll have to start somewhere. In the newsitems there are comments, in these comments there is another reference to the userid. You can probably guess what I want to do: I want the user info for the comments with the comments as well.
new Index:
public class NewsItemIndexWithComments : AbstractIndexCreationTask<NewsItem, NewsItemIndexWithComments.Result>
{
public class Result : NewsItem
{
public string AuthorName { get; set; }
public string AuthorId { get; set; }
}
public NewsItemIndexWithComments()
{
Map = newsItems => from newsItem in newsItems
let user = LoadDocument<User>(newsItem.UserId)
select new
{
AuthorName = user.DisplayName,
AuthorId = user.Id,
};
Store(x => x.AuthorName, FieldStorage.Yes);
Store(x => x.AuthorId, FieldStorage.Yes);
}
}
Comment class:
public class Comment
{
public string Id { get; set; }
public string Text { get; set; }
public string UserId { get; set; }
public string User { get; set; }
public DateTime Created { get; set; }
}
How can I query the comments and expand the results for that? Or is it better to create a new index just for the comments and get the user info analog to the solution above?
You're almost there, you just need to store the field you are projecting. Add this to the index constructor, after the map.
Store(x=> x.AuthorName, FieldStorage.Yes);
This is because you want it returned and available for AsProjection to find. If you just wanted to use the author name in a where or orderby, you wouldn't need it.
If you just want to include the comments in your AsProjection, you can simply index the entire object along.
Note that indexing a custom object will mean that you're not able to query on it using a .Where(). RavenDB can only query on flattened results (ints, decimals, strings, dates).
In order to, for instance, query on the title, you will need to create a seperate Property public string Title { get; set; } and map it with Title = newsItem.Title.
public class NewsItemIndexWithComments : AbstractIndexCreationTask<NewsItem, NewsItemIndexWithComments.Result>
{
public class Result : NewsItem
{
public string AuthorName { get; set; }
public string AuthorId { get; set; }
public List<Comment> Comments { get; set; }
}
public NewsItemIndexWithComments()
{
Map = newsItems => from newsItem in newsItems
let user = LoadDocument<User>(newsItem.UserId)
select new
{
AuthorName = user.DisplayName,
AuthorId = user.Id,
Comments = newsItem.Comments.
};
Store(x => x.AuthorName, FieldStorage.Yes);
Store(x => x.AuthorId, FieldStorage.Yes);
Store(x => x.Comments, FieldStorage.Yes);
}
}
I'm trying to get my head around treeviews and am able to do two levels, but I'm stumped with adding the 3rd level. I assume you make a 3rd Hierarchical Template - but I'm not sure how this is loaded. I'm using the AdventureWorks database as sample data.
I'm using the Product, ProductCategory, and ProductSubCategory tables.
My Metadata looks like. (just the interesting bits)
public partial class Product
{
public string Name { get; set; }
public int ProductID { get; set; }
public string ProductNumber { get;set; }
public ProductSubcategory ProductSubcategory { get; set; }
public Nullable<int> ProductSubcategoryID { get; set; }
}
public partial class ProductCategory
{
public string Name { get; set; }
public int ProductCategoryID { get; set; }
[Include]
[Composition]
public EntityCollection<ProductSubcategory> ProductSubcategory { get; set; }
}
public partial class ProductSubcategory
{
public string Name { get; set; }
[Include]
[Composition]
public EntityCollection<Product> Product { get; set; }
public ProductCategory ProductCategory { get; set; }
public int ProductCategoryID { get; set; }
public int ProductSubcategoryID { get; set; }
My Queries look like :
public IQueryable<Product> GetProduct()
{
return this.ObjectContext.Product;
}
public IQueryable<ProductCategory> GetProductCategory()
{
return this.ObjectContext.ProductCategory.Include("ProductSubcategory");
}
public IQueryable<ProductSubcategory> GetProductSubcategory()
{
return this.ObjectContext.ProductSubcategory.Include("Product");
}
My Code behind (which is where I'm having the problem understanding how to load two queries). I want to return Products under ProductSubCategory under ProductCategory.
public partial class Tree : Page
{
public Tree()
{
InitializeComponent();
AdventureWorksContext ads = new AdventureWorksContext();
trvTree.ItemsSource = ads.ProductCategories;
ads.Load(ads.GetProductCategoryQuery());
}
}
Try modifying your GetProductCategory query as such:
{
return this.ObjectContext.ProductCategory.Include("ProductSubcategory").Include("ProductSubcategory.Product");
}
I don't know if it'll work in your case, where you want to build a tree, but it should include the data as needed.