How to use .Include() in EF Core with ViewModel - asp.net-core

I have two models
public class PageGroup
{
public PageGroup()
{
Pages = new HashSet<Page>();
}
public int GroupID { get; set; }
public string GroupTitle { get; set; }
public virtual ICollection<Page> Pages { get; set; }
}
public class Page
{
public Page()
{
}
public int PageID { get; set; }
public int GroupID { get; set; }
public string PageTitle { get; set; }
public string PageText { get; set; }
public virtual PageGroup PageGroup { get; set; }
}
and a ViewModel
public class ShowGroupsViewModel
{
public int GroupID { get; set; }
public string GroupTitle { get; set; }
public int PageCount { get; set; }
}
I filled this ViewModel with this method
public async Task<IEnumerable<ShowGroupsViewModel>> GetListGroupsServiceAsync()
{
return await _context.PageGroups.Include(p => p.Pages.Count).Select(pa => new ShowGroupsViewModel()
{
GroupID = pa.GroupID,
GroupTitle = pa.GroupTitle,
PageCount = pa.Pages.Count
}).ToListAsync();
}
but PageCount is not work. when run the project is has zero value. How can I fill this property?
I use .net core 3.1

Since you are using Ef 3.1 Include should not be used because Include("Pages") will bring all Page instances from the SQl server and count them after this.
In EF Net5 it would be done much more simple, using Include, but since you are using EF 3.1 try this:
public async Task<IEnumerable<ShowGroupsViewModel>> GetListGroupsServiceAsync()
{
return await ( from pg in _context.PageGroups
join p in context.Pages
on pg.GroupId equals p.GroupId
group pg by new { pg.GroupId, pg.GroupTitle} into g
select new ShowGroupsViewModel{
GroupId = g.Key.GroupId,
GroupTitle =g.Key.GroupTitle
PagesCount = g.Count()
}).ToListAsync();
}

Related

Searching method in asp.net core

I'm building an asp.net core searching API which should return a list of videos who has the same searched QR code(the QR code is a ForeignKey)
This is the video model:
public class Video
{
[Key]
public int VideoId { get; set; }
public string Exercice { get; set; }
public string Titre { get; set; }
public int Sexe { get; set; }
public int Categorie { get; set; }
public int Level { get; set; }
public string FilePath { get; set; }
public DateTime DateUpload { get; set; } = DateTime.Now;
[ForeignKey("Machine")]
public string Machine_Qr { get; set; }
public Machine machine { get; set; }
public Coache Coache { get; set; }
}
And this is the search controller:
[HttpGet("{Qr}")]
public async Task<IEnumerable<Video>> Search(string qr)
{
IEnumerable<Video> query = _context.videos.Where(e => e.Machine_Qr == qr);
if ((query != null))
return query;
else
return Enumerable.Empty<Video>().ToList();
}
but I tested it and I got an empty list every time.
Edit: your edit fixed the first logic issue in the original question. But the query also needs to have an await since the function is an async Task.
Since your function returns an IEnumerable, you shouldn't need to call .ToList() ... try this:
[HttpGet("{Qr}")]
public async Task<IEnumerable<Video>> Search(string qr)
{
return await _context.videos.Where(e => e.Machine_Qr == qr) ?? Enumerable.Empty<Video>()
}

Can i add a parent record and a child record using the same _context.SaveChangesAsync()

I have the following 2 Parent/Child objects:-
public Submission()
{
SubmissionQuestionSubmission = new HashSet<SubmissionQuestionSubmission>();
}
public int Id { get; set; }
public string FirstName { get; set; }
public string LastName { get; set; }
public string Npi { get; set; }
public bool Independent { get; set; }
public string Comment { get; set; }
public virtual ICollection<SubmissionQuestionSubmission> SubmissionQuestionSubmission { get; set; }
}
public partial class SubmissionQuestionSubmission
{
public int SubmissionQuestionId { get; set; }
public int SubmissionId { get; set; }
public string Answer { get; set; }
public virtual Submission Submission { get; set; }
}
and i created the following view model:-
public class SubmissionCreate
{
public Submission Submission {set; get;}
public IList<SubmissionQuestion> SubmissionQuestion { set; get; }
public IList<SubmissionQuestionSubmission> SubmissionQuestionSubmission { set; get; }
}
where i have the following action method to add a parent record (submission) and a child record (SubmissionQuestionSubmission ), but to do so, i have to issue 2 save requests to the database, one to save the parent and get its ID, while the other to save the child record and assign it the parent ID, as follow:-
public async Task<IActionResult> Create(SubmissionCreate sc)//Bind("Id,FirstName,LastName,Npi,Independent,Comment")]
{
if (ModelState.IsValid)
{
var newsubmission = _context.Submission.Add(sc.Submission);
await _context.SaveChangesAsync();
foreach (var v in sc.SubmissionQuestionSubmission)
{
v.SubmissionId = sc.Submission.Id;
_context.SubmissionQuestionSubmission.Add(v);
}
await _context.SaveChangesAsync();
TempData["message"] = "Thank You.. Your request has been submitted...";
return View("Confirmation");
}
return View(sc);
}
so my question is if i can do the above job, using one save statement instead of 2?
You don't need use two SaveChanges. You can assign newsubmission into Submission property instead of v.SubmissionId = sc.Submission.Id;.
In this case Id and ForeignKey created automatically by EF Core
var newsubmission = _context.Submission.Add(sc.Submission);
foreach (var v in sc.SubmissionQuestionSubmission)
{
v.Submission = newsubmission;
_context.SubmissionQuestionSubmission.Add(v);
}
await _context.SaveChangesAsync();
Another way
sc.Submission.SubmissionQuestionSubmission = new List<SubmissionQuestionSubmission>();
foreach (var v in sc.SubmissionQuestionSubmission)
{
sc.Submission.SubmissionQuestionSubmission.Add(v)
}
_context.Submission.Add(sc.Submission);
await _context.SaveChangesAsync();

EF Core 2.2 runs SELECT * FROM [TableName] with no SELECT query in my code

I have the following pieces of code:
public class BaseEntity
{
public Guid Id { get; set; }
public DateTimeOffset CreatedDate { get; set; }
[Timestamp]
public byte[] RowVersion { get; set; }
}
public class StateHistory : BaseEntity
{
public string RegistrationFlowTitle { get; set; }
public string RegistrationStateTitle { get; set; }
public Guid? PartyId { get; set; }
}
public class Party : BaseEntity {
public string FirstName { get; set; }
public string LastName { get; set; }
}
//StateHistoryRepository.cs
public class StateHistoryRepository : BaseRepository<StateHistory>, IStateHistoryRepository
{
public StateHistoryRepository(DbContext context) : base(context)
{ }
public void Add(StateHistory history)
{
Context.StateHistories.Add(history);
}
public async Task AddAsync(StateHistory history)
{
await Context.StateHistories.AddAsync(history);
}
}
//PartyService.cs
public async Task<ServiceResponse> UpdatePartyAsync(Party model)
{
var existingParty = await _partyRepository.GetParty(model.Id, asNoTracking:false);
if (existingParty == null)
{
return ServiceResponse<ServiceResponse>.Failed(message: "");
}
existingParty.FirstName = model.FirstName;
existingParty.LastName = model.LastName;
await _stateHistoryRepository.AddAsync(new StateHistory{RegistrationFlowTitle = "RegistrationFlowTitle", RegistrationStateTitle = "RegistrationStateTitle",
PartyId = model.Id});
await _partyRepository.CommitAsync();
return ServiceResponse.Successful();
}
All my project is designed as above. But in some scenarios that I have no idea about, when running the SQL Server Profiler, I see the following query:
SELECT [s].[Id], [s].[CreatedDate], [s].[PartyId], [s].[RegistrationFlowTitle], [s].[RegistrationStateTitle], [s].[RowVersion] FROM [StateHistories] AS [s]
How is it possible when I have not SELECT query on StateHistorie table, but it runs in the background? Is it a bug from EF Core? How can I track it? Since the table has over 5 million records, it creates deadlocks in database!

How to show and search a list of products in asp.net core?

I would like to build these functionalities to a project using Asp.Net Core MVC.
Could someone please guide me through, How I can approach these steps:
View a list of product types for a given product category or for all categories.
I have created an ASP.NET Core MVC project with Identity authentication, where the user could register and log in.
I also have these Models created.
namespace Company.Models
{
public class ProductType
{
public ProductType()
{
Products = new List<Product>();
}
public long ProductTypeId { get; set; }
public string ProductName { get; set; }
public string ProductInfo { get; set; }
public string Location { get; set; }
public ProductTypeStatus Status { get; set; }
public string ImageUrl { get; set; }
public string Manufacturer { get; set; }
public string AdminComment { get; set; }
public Category Categories { get; set; }
public ICollection<Product> Products { get; protected set; }
}
public enum ProductTypeStatus
{
Available,
ReservedAdmin
}
public enum ProductStatus
{
Available,
ReservedLoaner,
ReservedAdmin,
Loaned,
Defect,
Trashed,
Lost,
NeverReturned
}
namespace Company.Models
{
public class Product
{
public long ProductId { get; set; }
public long ProductTypeId { get; set; }
public int ProductNumber { get; set; }
public string SerialNo { get; set; }
public ProductStatus Status { get; set; }
public string AdminComment { get; set; }
public string UserComment { get; set; }
public long? CurrentLoanInformationId { get; set; }
}
}
namespace Company.Models
{
public class Category
{
public Category()
{
ProductTypes = new List<ProductType>();
}
public int CategoryId { get; set; }
public string Name { get; set; }
public ICollection<ProductType> ProductTypes
{
get; protected set;
}
}
I have recently turned to Asp.Net Core MVC. So this is a new envirnoment for me to get startd. Though, I did follow the msdn tutorials on asp.net mvc.
I APPRECIATE any help!
I saw your model design I think you missing 1 small thing that is relationship between Product and Category.
1 Product will be in 1 Category
So to add 1 to 1 relationship you need to adjust your model like this. You can view more here
namespace Company.Models
{
public class Product
{
public long ProductId { get; set; }
public long ProductTypeId { get; set; }
public int ProductNumber { get; set; }
public string SerialNo { get; set; }
public ProductStatus Status { get; set; }
public string AdminComment { get; set; }
public string UserComment { get; set; }
public long? CurrentLoanInformationId { get; set; }
public Category Category { get;set; }
}
}
namespace Company.Models
{
public class Category
{
public Category()
{
ProductTypes = new List<ProductType>();
}
public int CategoryId { get; set; }
public string Name { get; set; }
public ICollection<ProductType> ProductTypes
{
get; protected set;
}
}
}
So when you update your model you will need to run ef migration to apply change to db. Detail can be found here
And finally you need to write the code to query some thing like
var query = _db.Product.Where(x => x.Category == "Book");
You can read how to write ef query in c# here

How to disable changestate tracking for a sub-entity in boilerplate AppService

I'm using aspnet core & ef core with boilerplate and would like to disable the changestate tracking for a sub-entity. How do I this this within AppService (ie AsyncCrudAppService).
For example:
Entity:
[Table("tblCategory")]
public class Category : FullAuditedEntity<int>, IMustHaveTenant
{
public const int MaxCodeLength = 128;
public const int MaxNameLength = 2048;
public virtual int TenantId { get; set; }
[Required]
[StringLength(MaxCodeLength)]
public virtual string Code { get; set; }
[Required]
[StringLength(MaxNameLength)]
public virtual string Name { get; set; }
[ForeignKey("GroupId")]
public virtual Group Group { get; set; }
[Required]
public virtual int GroupId { get; set; }
}
Dto:
[AutoMapFrom(typeof(Category))]
public class CategoryDto : FullAuditedEntityDto<int>
{
[Required]
[StringLength(Category.MaxCodeLength)]
public string Code { get; set; }
[Required]
[StringLength(Category.MaxNameLength)]
public string Name { get; set; }
[Required]
public int GroupId { get; set; }
[DisableValidation]
public GroupDto Group{ get; set; }
}
AppService Update Method:
public override async Task<CategoryDto> Update(CategoryDto input)
{
var cat = await _categoryManager.GetA(input.Id);
MapToEntity(input, cat);
//I'd like to disable the tracking of cat.Group here ?
await _categoryManager.UpdateA(cat);
return await Get(input);
}
I'd like to disable the change detection for cat.Group, how do I do this ?
Thanks in advance.
Using AsNoTracking when loading resolves the issue
Tracking can be skipped by adding .AsNoTracking() to the call.
For example:
var cat = await _yourDbContext.AsNoTracking().FirstAsync(m => m.Id == input.Id);
Which is fine for results that will not be edited during it's lifetime.