ASP.NET Web API 2 Child entities missing when serialized - asp.net

I am currently building an angular website and using Web Api 2 as my data service.
I have encountered a issue with populating child entities when i call a http get method. The child entities (fields) does not appear in the Json response.
//Fetch records by page size
public ICollection<CompanyStatDomainModel> GetRecordsByPageSize(int page)
{
const int pgeSize = 20;
var result = _companyStatRepo.AllIncluding(c => c.CompanyDomainModel, c => c.RatingDomainModels)
.OrderBy(c => c.CompanyStatId).Skip(page * pgeSize).Take(pgeSize).ToList();
return result;
}
CONTROLLER
public ICollection<CompanyStatDomainModel> GetRecordsByPageSize(int page)
{
var companyStatService = new CompanyStatService();
return companyStatService.GetRecordsByPageSize(page);
}
WebApiConfig.cs
var json = config.Formatters.JsonFormatter;
json.SerializerSettings.PreserveReferencesHandling = Newtonsoft.Json.PreserveReferencesHandling.Objects;
config.Formatters.Remove(config.Formatters.XmlFormatter);

The easiest way to solve this kind of problem is by manually projecting the results of the EF query to the desired structure, something like
var result = _companyStatRepo
.AllIncluding(c => c.CompanyDomainModel, c => c.RatingDomainModels)
.OrderBy(c => c.CompanyStatId).Skip(page * pgeSize)
.Take(pgeSize).Select(csr => new CompanyStatRepo {
prop1 = csr.prop1,
prop2 = csr.prop2,
...
RatingDomainModels = csr.RatingDomainModels.ToList()
}).ToList();
I don't know the real structure of your classes, but this is the basic idea.

Related

How can I display A ViewBag using information from my database and using .include()?

I am new to ASP.NET/EntityFramework and I have created a simple Bank app. The problem I am having is that I can't get the balance to display.
here is the key part that I need to use in my model:
public double Balance
{
get { return Transactions.Sum(transaction => transaction.Amount); }
}
and here is the key part in my controller that I need to use:
[HttpPost("/money")]
public IActionResult Money(Transaction trans)
{
if(ModelState.IsValid)
{
ViewBag.balance = dbContext.Transactions
.Include(t => t.Amount);
dbContext.Transactions.Add(trans);
dbContext.SaveChanges();
return RedirectToAction("Index");
}
var user = loggedInUser;
ViewBag.User = user;
ViewBag.Transactions = dbContext.Transactions
.OrderByDescending(t => t.CreatedAt)
.Where(t => t.UserId == loggedInUser.UserId);
return View("Index");
}
and here is my View where I am trying to get the balance using ViewBag:
<p>Current balance: #ViewBag.User.Balance</p>
Im assuming the problem has something to do with this:
ViewBag.balance = dbContext.Transactions
.Include(t => t.Amount);
First, make sure you can get value from dbContext.Transactions.Include(t => t.Amount);.
Second, you set ViewBag.balance = ..., so in your view. You can get it by <p>Current balance: #ViewBag.balance</p>.
Tips:
If you want get value from User model, you need add #model User, then you can use something like #Model.Balance to get balance.
For more details, you can refer official doc:
Part 4, add a model to an ASP.NET Core MVC app

Odata [EnableQuery] attribute makes EF Core crash on high requests amount

In my Odata controller I Use [EnableQuery(EnsureStableOrdering = false)]. Stable ordering is because of some custom Order by which I need in my code. Example in the mentioned Issue above
The problem however is with the [EnableQuery] attribute in general. When I have this attribute in the controller on high load, which means more than 150 request in a minute at the same endpoint I get a lot of the following exception:
"System.InvalidOperationException","Message":"A second operation was
started on this context before a previous operation completed. This is
usually caused by different threads concurrently using the same
instance of DbContext."
I have used testing tool which send that kind of request to my endpoind (Result is around 240 requests in a minute with 1s delay between each) and most of the requests succeed, like maybe around 85% - 90% of them, however the failed requests get the exception above.
I needed some time to figure out what is happening, but when I remove the [EnableQuery] attribute everything was fine. No exception on any level of requests amount.
Here some code:
public class OfficeDtoController : ODataController
{
private readonly IOfficesService _officesService;
public OfficeDtoController(IOfficesService officesService)
{
_officesService = officesService;
}
[HttpGet]
[EnableQuery(EnsureStableOrdering = false)]
public IActionResult GetOfficeDto(ODataQueryOptions<OfficeDto> queryOptions, Guid? id = null, string sortColumn = "", string sortOrder = "", int? top = null, int? skip = null)
{
IQueryable<OfficeDto> officeDto = _officesService.GetOffices(id, sortColumn, sortOrder, top, skip);
return Ok(officeDto);
}
}
public IQueryable<OfficeDto> GetOffices(Guid? id, string sortColumn, string sortOrder, int? top, int? skip)
{
IQueryable<Office> query = dbSet;
query = query
.Where(z => !z.Deleted);
var list = query.Select(x =>
new OfficeDto()
{
Id = x.Id,
ZipCode = x.ZipCode,
Active = x..Active,
Employees = x.XrefOfficesEmployees
.OrderBy(y => y.Order)
.Select(y => new IdAndName()
{
Value = y.EmployeeId,
Text = y.Employee.FirstName + " " + y.Employee.LastName,
Order = y.Order
})
});
switch (sortColumn)
{
case "employees":
if (sortOrder == "desc")
{
officeDto = officeDto.OrderByDescending(b => b.Employees.Select(a => a.Text).FirstOrDefault( ));
}
else
{
officeDto = officeDto.OrderBy(b => b.Employees.Select(a => a.Text).FirstOrDefault( ));
}
}
return list;
}
Here is the example URL:
api/OfficeDto?$count=true&$top=20&$skip=0&$select=id,zipcode,employees&$filter=(active
eq true)
Maybe there is problem in the communication between EF Core and Odata.
I use ASP.NET Wen API on NET 5 and the following NuGets:
Microsoft.AspNetCore.OData 7.5.4
Microsoft.Data.OData 5.8.4
Microsoft.OData.Core 7.8.1
Microsoft.EntityFrameworkCore 5.0.2
EDIT:
For ones who asked about DI:
services.AddDbContext<MyContext>( );
services.AddTransient<IOffcesService, OfficesService>( );
However I have tried to initialize everything in the Controller (DB Context and BusinessService) because at first my suggestion was about DI, however it didnt help.

Asp Core, How to use PagingList<T>.CreateAsync() with a viewModel?

I am working on a asp.net core 1.1 project and i want to create paging in my some views. I studied microsoft documents about paging in asp core but it is very simple mode with 1 table. In my view i use multi table and use a viewmodel to initialize it. I want to use PagingList<T>.CreateAsync() method to create paging but get error:
can not convert from system.linq.Iqueryable<> to system.linq.IorderedQueryable<>
my action:
[HttpGet]
public async Task<IActionResult> Index(int? page)
{
List<BookListViewModel> model = new List<BookListViewModel>();
var query = (from b in _context.books
join a in _context.authors on b.AuthorID equals a.AuthorId
join bg in _context.bookgroups on b.BookGroupID equals bg.BookGroupId
select new
{
b.BookId,
b.BookName,
b.BookPageCount,
b.BookImage,
b.AuthorID,
b.BookGroupID,
a.AuthorName,
bg.BookGroupName
});
foreach (var item in query)
{
BookListViewModel objmodel = new BookListViewModel();
objmodel.BookId = item.BookId;
objmodel.BookName = item.BookName;
objmodel.BookImage = item.BookImage;
objmodel.BookPageCount = item.BookPageCount;
objmodel.AuthorId = item.AuthorID;
objmodel.BookGroupId = item.BookGroupID;
objmodel.AuthorName = item.AuthorName;
objmodel.BookGroupName = item.BookGroupName;
model.Add(objmodel);
}
ViewBag.RootPath = "/upload/thumbnailimage/";
int pageSize = 3;
int pageNumber = (page ?? 1);
return View(await PagingList<BookListViewModel>.CreateAsync(model.AsQueryable() , pageNumber, pageSize));
}
I have not yet written anything about paging in index view and it is a simple list of viewmodel
Well can't really be sure from the code you posted. But the exception says the the CreateAsync method needs IOrderedQueryable, but you're giving it an IQueryable.
Try changing it to pass in the query object (which I guess should implement the IOrderedQueryable, if you're using Entity framework).
The idea behind the PagingList (presumably) is to use it to do the paging in the database.
What you're doing is bringing the filterted set into memory (when for-eaching through the result), and then doing doing the paging.
The code might look something like this:
[HttpGet]
public async Task<IActionResult> Index(int page = 1)
{
var query = (from b in _context.books
join a in _context.authors on b.AuthorID equals a.AuthorId
join bg in _context.bookgroups on b.BookGroupID equals bg.BookGroupId
select new BookListViewModel()
{
BookId = b.BookId,
BookName = b.BookName,
BookPageCount = b.BookPageCount,
BookImage = b.BookImage,
AuthorId = b.AuthorID,
BookGroupId = b.BookGroupID,
AuthorName = a.AuthorName,
BookGroupName = bg.BookGroupName
}).AsNoTracking().OrderBy(u => u.BookId);
ViewBag.RootPath = "/upload/thumbnailimage/";
var pagedResult = await PagingList<BookListViewModel>.CreateAsync(query, 10, page);
return View(pagedResult);
}
Hope it helps.

Select All Rows Using Entity Framework

I'm trying to select all the rows out of a database using entity framework for manipulation before they're sent to the form
var ptx = [modelname].[tablename]();
ptx.[tablename].Select(????)
what goes in the ????
I used the entitydatasource and it provide everything I needed for what I wanted to do.
_repository.[tablename].ToList();
Entity Framework has one beautiful thing for it, like :
var users = context.Users;
This will select all rows in Table User, then you can use your .ToList() etc.
For newbies to Entity Framework, it is like :
PortalEntities context = new PortalEntities();
var users = context.Users;
This will select all rows in Table User
How about:
using (ModelName context = new ModelName())
{
var ptx = (from r in context.TableName select r);
}
ModelName is the class auto-generated by the designer, which inherits from ObjectContext.
You can use this code to select all rows :
C# :
var allStudents = [modelname].[tablename].Select(x => x).ToList();
You can simply iterate through the DbSet context.tablename
foreach(var row in context.tablename)
Console.WriteLn(row.field);
or to evaluate immediately into your own list
var allRows = context.tablename.ToList();
If it's under a async method then use ToListAsync()
public async Task<List<DocumentTypes>> GetAllDocumentTypes()
{
var documentTypes = await _context.DocumentTypes.ToListAsync();
return documentTypes;
}
Old post I know, but using Select(x => x) can be useful to split the EF Core (or even just Linq) expression up into a query builder.
This is handy for adding dynamic conditions.
For example:
public async Task<User> GetUser(Guid userId, string userGroup, bool noTracking = false)
{
IQueryable<User> queryable = _context.Users.Select(x => x);
if(!string.IsNullOrEmpty(userGroup))
queryable = queryable.Where(x => x.UserGroup == userGroup);
if(noTracking)
queryable = queryable.AsNoTracking();
return await queryable.FirstOrDefaultAsync(x => x.userId == userId);
}
Here is a few ways to do it (Just assume I'm using Dependency Injection for the DbConext)
public class Example
{
private readonly DbContext Context;
public Example(DbContext context)
{
Context = context;
}
public DbSetSampleOne[] DbSamples { get; set; }
public void ExampleMethod DoSomething()
{
// Example 1: This will select everything from the entity you want to select
DbSamples = Context.DbSetSampleOne.ToArray();
// Example 2: If you want to apply some filtering use the following example
DbSamples = Context.DbSetSampleOne.ToArray().Where(p => p.Field.Equals("some filter"))
}
You can use:
ptx.[tablename].Select( o => true)

MVC3 C#4.0 / Passing variables between views

new to C# and MVC. What I would like to achieve is passing a variable as ViewData from one view to another view without using ID in the ActionResult because this view generates it own variable. I am sure there are better ways to do that, but here what I thought might work.
First I made a model:
public class EventToShow
{
public Int64? ID { get; set; }
public Int64? EventID { get; set; }
}
Then I passed the variable EventID from the first View (Telerik MVC GRID) using the following:
columns.Template(item => Html.Raw(string.Format("{1}", Url.Action("tableread", "Home", new { id = (long)item.Event_ID }), "EventID"))).Width(20);
It worked using the following in my controller:
[AcceptVerbs(HttpVerbs.Post)]
public ActionResult tableread1(long? id)
{
ViewData["EID"] = id;
EventToShow ctx = new EventToShow();
ctx.ID = 1;
ctx.EventID = (long)ViewData["EID"];
return RedirectToAction("EditServerSide");
}
To pass the variable to the other view I tried to use the following (I think it is very wrong):
public ActionResult EditServerSide()
{
EventToShow ctx = new EventToShow();
var model1 = ctx.(x => x.ID == 1); **// The error here is (Identifier** expected)
ViewData["EID"] = ctx.EventID;
var model = from o in new Main().OffLinePayments
select new EditOffLinePayment
{
ID = o.ID,
Amount = o.Amount,
Details = o.Details
};
return View(model, ViewData["EID"]) **(this must be wrong)**
}
I thought maybe I should make the variable like this:
private string GetFullName()
{
EventToShow ctx = new EventToShow();
var name = EventToShow().Where(x => x.ID == 1);
ViewData["EID"] = ctx.EventID;
return name;
}
First I got an error: ‘GridEdit_OfflinePayment.Models.EventToShow' is a 'type' but is used like a 'variable'
I also did not know how to incorporate returned [name] in the EditServerSide Action.
My question, is there a better way to achieve what I am trying to do, and if this approach is correct, I would appreciate any help to fix these errors
From what I understand of the question is that you would like to pass data between several Actions? Like some sort of wizard steps process where you can pass data between multiple Actions?
If that's the case then here are some related questions and their answers:
How do I pass data across ActionResults in MVC 3?
multi-step registration process issues in asp.net mvc (splitted viewmodels, single model)

Resources