Web Api, Update DB, connection reset 200ok - c#

I have asp.net web api application. I have the table Companies in the databse which have two fields: id and description. Recently I've updated the database and added a new column called CustomerID. After that when I am trying to call getCompanies
private readonly BackendContext _context;
public CompaniesController(BackendContext context)
{
_context = context;
}
// GET: api/Companies
[HttpGet]
public IEnumerable<Company> GetCompanies()
{
return _context.Companies;
}
I get
I think the controller tries to return the old companies model but can't achieve it because it doesnt exist now but I don't know how to fix this though the controller should return the updated model. Maybe I should somehow rebuild the app to make it use the updated version?
Additional code:
Context
public class BackendContext : Microsoft.AspNetCore.Identity.EntityFrameworkCore.IdentityDbContext<IdentityUser>//DbContext
{
public BackendContext(DbContextOptions<BackendContext> options) : base(options) { }
public DbSet<Company> Companies { get; set; }
public DbSet<CompanyToProduct> CompanyToProducts { get; set; }
public DbSet<Product> Products { get; set; }
public DbSet<Customer> Customers { get; set; }
public DbSet<Vendor> Vendors { get; set; }
public DbSet<VendorToProduct> VendorToProducts { get; set; }
public DbSet<Invoice> Invoices { get; set; }
public DbSet<InvoiceItem> InvoiceItems { get; set; }
}
Model
public class Company
{
public int ID { get; set; }
public string Name { get; set; }
public string Description { get; set; }
public int CustomerID { get; set; }
public virtual Customer Customer { get; set; }
public virtual ICollection<CompanyToProduct> CompaniesToProducts { get; set; }
public virtual ICollection<Invoice> Invoices { get; set; }
}
UPDATE
I've added some values to the table and I got the response of the first company:
[{"id":1,"name":"Google","description":"free food","customerID":6,"customer":null,"companiesToProducts":null,"invoices":null}
BUT I also got the fields which is not specified in the table: customer, companiesToProducts,invoices. Invoices and companiesToProducts are tables in my database and I don't know what is customer referred to. I should also mention that these tables are connected by foreign key.
UPDATE
Error:

Based on the comments on the question above, it sounds like the related tables are all trying to serialize and the overall process is failing likely due to circular references in the object graph. This comment above in particular hints at a solution:
I want to return only the data about companies but the controller also returns another fields like customer, companiesToProducts,invoices
While it's convenient to just return directly from the data context, this has the added side-effect of coupling the API with the database (and with the data access framework, which appears to be the issue here). In API design in general it's always a good idea to explicitly define the "shape" of that API. The fields to return, etc.
Project your result into an explicitly defined shape and return only what you want to return:
var result = _context.Companies
.Select(c => new
{
c.ID,
c.Name,
c.Description,
c.CustomerID
})
.ToList();
This defines specifically what you want to return, fetches only that information from the backing data, materializes it into an in-memory list, and finally then returns it through the API.
There is a potential downside to this, however. Because now we also need to change the return type of your API method. There are a couple options there, such as returning a generic response object or creating a view model which closely approximates your already existing model and starts to feel like duplication.
As with just about anything, it's a balance. Too far in any one direction and that direction starts to become a problem. Personally I often go the route of defining a view model to return:
public class CompanyViewModel
{
public int ID { get; set; }
public string Name { get; set; }
public string Description { get; set; }
public int CustomerID { get; set; }
}
and returning that:
return _context.Companies
.Select(c => new CompanyViewModel
{
ID = c.ID,
Name = c.Name,
Description = c.Description,
CustomID = c.CustomerID
})
.ToList();
But the reason I normally do this is because I normally work in an environment where the web application is just one application attached to a common shared business domain, so the view models don't feel like code duplication. They're in a separate project, often take a different shape than the backing data objects, etc. But if your domain models are already in your web project and that's the only project you have, there's a strong desire to want to return those.
Another option when that's the case could be to universally set your JSON serialization to ignore circular references:
services.AddMvc()
.AddJsonOptions(
options => options.SerializerSettings.ReferenceLoopHandling
= Newtonsoft.Json.ReferenceLoopHandling.Ignore );
But do keep in mind that this still couples your API to your DB models. Maybe that's okay in this project, but if you ever add a column to your DB that you don't want users to see then it becomes an issue. As with anything, you have options.

Related

EF Core NullReferenceException on Related Navigation Property

I have two related models.
public class Offer
{
public long Id { get; set; }
public string OfferCode { get; set; }
public string Description { get; set; }
// more properties
public int ProductId { get; set; }
public virtual Product Product { get; set; }
}
public class Product
{
public long Id { get; set; }
public string Name { get; set; }
// more properties
public virtual ICollection<Offer> Offers { get; set; }
}
I am trying to have an MVC form with a select HTML element where Offers are grouped and products
and have the Product Names serve as optgroups.
To this end, I have a view model that I intend to populate with the grouped Offers and I have a method
to do just that.
private OfferMessageViewModel PrepareViewModel(OfferMessageViewModel viewModel)
{
var offers = _context.Offers.Include(o => o.Product).ToList()
.GroupBy(o => o.Product.Name).ToList();
foreach (var offerGroup in offers)
{
var optionGroup = new SelectListGroup
{
Name = offerGroup.Key
};
foreach (var offer in offerGroup)
{
viewModel.Offers.Add(
new SelectListItem
{
Value = offer.OfferCode,
Text = offer.Description,
Group = optionGroup
}
);
}
}
return viewModel;
}
The code gets tripped up in the GroupBy clause.
o.Product is null even when o.ProductID has a value in it.
The ToList() call right before the GroupBy is not helping.
I have tried removing the virtual modifiers on the related entities
navigation properties but the error persisted.
Installing the NuGet package Microsoft.EntityFrameworkCore.Proxies and
modifying and configuring it as such
services.AddDbContext<ApplicationDbContext>(options =>
options.UseLazyLoadingProxies()
.UseSqlServer(
Configuration.GetConnectionString("DefaultConnection")));
also did not make the error go away.
Is there something else I am missing?
Any help would be greatly appreciated.
EDIT:
It has been suggested that my post might be solved by this SO question. But I get the null reference exception even with lazy loading explicitly turned on.
I have tried the suggested solutions there but still no luck.
I eventually solved it.
Apparently the problem was that the foreign key was an int referencing a primary key of type long.
So I changed
public int ProductId { get; set; }
to
public long ProductId { get; set; }
in the Offer model.
Added the necessary migration, updated the database and now it works.
No more null reference exceptions.
Don't know why I missed that but it's probably a combination of lack of sleep and
a not-so-helpful error message throwing me off in a completely different direction.

Including only Id of related entity in entity framework core

I am currently developing an API with ASP.NET Core and Entity framework core with npgsql as database provider. I have two Entities and they have a one to many relation. The thing is that I only want to include the Id's of the child entity in the JSON result that the "Parent Controller" returns.
These are my entities:
public class Meal {
public int Id { get; set; }
public string Title { get; set; }
public string Description { get; set; }
public string UserId { get; set; }
public User User { get; set; }
public List<Picture> Pictures { get; set; }
public Meal () {
this.Pictures = new List<Pictures>();
}
}
public class Picture {
public int Id { get; set; }
public int MealId { get; set; }
public Meal Meal { get; set; }
public byte[] full { get; set; }
public byte[] small { get; set; }
}
I am however not sure on how to achieve this. Yesterday I came across another SO question which suggested something like this:
public IActionResult Meals () {
var meal = this.context.Meals
.Include(m => m.Pictures.Select(p => p.Id))
.First();
return new JsonResult(meal);
}
This however throws an InvalidOperationException.
My DbContext is very basic, no onModelConfiguring because this code is following convention for as far as I know and it just has two DbSets of the corresponding types. The foreign keys are also correct in the database and callling something like:
var pictures = dbContext.Pictures.Where(p => p.MealId == mealId).ToList();
Works as expected. I have only included the code which I thought was relevant. If more is needed I will include it, but I think this is completely my limited understanding of the queries.
Thank you for your time!
You don't need to change your DB structure, one option is like so:
var db = this.context;
var result = (from meal in db.Meals
where meal.<whatever> == "123"
select new
{
Id = meal.Id,
Title = meal.Title,
Description = meal.Description,
//other required meal properties here.
PictureIds = meal.Pictures.Select(x => x.Id)
}).ToList();
You can do the same thing via lambda as well by using the "Select" method, Linq in such things seem more intuitive to me, however, to each his own... that's your choice.
.Include(m => m.Pictures.Select(p => p.Id)) will not work you have to do
.Include(m => m.Pictures)
And this will get you an array of whole picture model for you (with all properties Id, MealId,..) and in your client side you can choose Id to work with..
There is probably a better answer out there, but this is how I fixed it. Including the entire Picture class was not an option since the binary data would also be included and I did not want to query the server for the data without using it since that is an expensive call to the database.
So what I did is put the binary data in another class called PictureFile (need to think of a better name, but just File was obviously not an option). The class PictureFile just has a reference to the corresponding picture and a byte array with the picture data. That way you can include the Pictures in a Meal without getting the actual files. The client can than later decide which pictures it needs and request them by PictureId.

Navigation property doesn't get loaded - EF

I'm having 3 models Items, Branches and ItemsInBranches defined as follows
public class Items
{
public int Id { get; set; }
public string Barcode { get; set; }
public string Name { get; set; }
public int SizeId { get; set; }
public int Price { get; set; }
public int DiscountId { get; set; }
public int ShortageMargin { get; set; }
[NotMapped]
public double ActualPrice
{
get
{
double amount = ((double)Price * (double)Discount.Amount / 100);
double price = (Price - amount < 0) ? 0 : Price - amount;
return price;
}
}
public Discounts Discount { get; set; }
public ItemSizes Size { get; set; }
public ICollection<ItemsInBranches> ItemsInBrach { get; set; }
}
public class Branches
{
public int Id { get; set; }
public string Name { get; set; }
public string Location { get; set; }
public ICollection<Employees> Employees { get; set; }
public ICollection<TransactionLog> TransacionLogs { get; set; }
public ICollection<ItemsInBranches> ItemsInBranch { get; set; }
}
public class ItemsInBranches
{
public int Id { get; set; }
public int ItemId { get; set; }
public int BranchId { get; set; }
public int Amount { get; set; }
[NotMapped]
public bool IsShort
{
get
{
return Amount < Item.ShortageMargin;
}
}
public Items Item { get; set; }
public Branches Branch { get; set; }
}
whenever I try to load items in branches using the following code I Branch navigation property gets loaded just fine but I the Items is always set to null
public IEnumerable<StorageViewModel> GetStorage(int? BranchId)
{
var storage = Find(x => true).Select(s => new StorageViewModel
{
Amount = s.Amount,
BranchName = s.Branch.Name,
ItemName = s.Item.Name,
SortageMargin = s.Item.ShortageMargin,
IsShort = s.IsShort
});
return storage;
}
public IEnumerable<TEntity> Find(Expression<Func<TEntity, bool>> predicate)
{
return _dataContext.Set<TEntity>().Where(predicate);
}
I made sure the ItemId and BranchId are set to foreign keys for the Items and Branches tables in the database, they don't allow nulls and they enforce foreign key constraints
could anyone tell me why only the Branch get loaded while Item is always set to null
Generally you shouldn't try to wrap frameworks, particularly EF with helper methods like you're doing with your Find method, which I mention because that is why you're running into this problem (rather it's making the solution harder than it would otherwise have to be without the helper).
You may run into this problem you ran into (or where you want to specify AsNoTracking() in your Find one day when you find out about the ChangeTracker and how it works and SaveChanges() starts running slow or you have a large memory footprint due to having too much attached at once. Of course this is problem more with EF because it makes it too easy for people to develop without really understanding important concepts like transactions - the details of the layer it's trying to abstract away...)
You would need to load related entities somehow. I'd recommend reading this link and choosing an option. Here's one option on that link that only touches your GetStorage method, which might "solve the problem easily" but may not be good performance-wise if there are a lot of records - considering your predicate is including everything, though, there may not be a lot. I can't give you a better recommendation without seeing more of your code. If it's loading one and not the other and they're otherwise identical (same non-nullable FK and there's a corresponding record) then I think it's probably configuration somewhere... again read the link to see how to configure loading of navigation properties. Note this will probably generate multiple SELECT statements; I couldn't think of a better way (besides dropping down into Set<>) with the constraints of using Find inside GetStorage to grab the missing records you needed. If you could call the Find from a different class that has a different TEntity specified then you could probably get just the Items records you need in one SELECT statement - but I don't know how you're setting up your services and their lifecycles. That might be a compromise between performance and not having a mass-refactoring of the data access wrappers.
var allEntities = Find(x => true).ToList();
allEntities.ForEach(x => _dataContext.Entry(x).Reference(y => y. Item).Load());
var storage = allEntities.Select(s => new StorageViewModel
// ...
GetStorage seems like it's specific to one TEntity; Your Find method seems like it has TEntity defined as a generic in the containing class - so if I had to guess those methods are in (2) different classes even though you put them back-to-back. Your Find method is then probably on a class that abstracts away EF to give you a "simpler" interface to the database. EF is already that simple interface to the database; you don't need another.
What you could be doing instead is making concrete repositories that take a hard dependency on specific TEntity's and depend on a DbContext and then having either your domain logic taking a dependency on your repositories (which would require you to mock the repository somehow - either with a pseudo database, actual database or a leaky in-memory database to "unit" test your domain logic) or having your repositories totally de-coupled from your domain logic, enabling fast-executing domain logic unit tests. Then your repositories have shielded the rest of the application from EF, so you could change it out to ADO.NET or a lighter weight ORM like Dapper.
A Find method like that is an EF abstraction - you've tied whatever depends on that to EF (and your schema, probably) even if you didn't realize it. What I briefly described is true a database abstraction - which actually would free you up to change your data access (and schema) if you needed to - just change out the repository implementation without changing its API or behavior...

Creating a Blog Comments and Reply section using ASP.NET MVC 4 (nested collections)

I'm building a Blog Comment and Reply section and I have these three classes mapped to my DB. The first class holds a collection of related comments to an article, the second class holds a collection of related remarks to the comments:
public class Article
{
public int ArticleID { get; set; }
public byte[] Image { get; set; }
public string Title { get; set; }
public string Body { get; set; }
public DateTime DatePublished { get; set; }
public string Author { get; set; }
public CategoryTyp Category { get; set; }
public virtual ICollection<Comment> Comments { get; set; }
}
public class Comment
{
public int CommentID { get; set; }
public int ArticleID { get; set; }
public int CategoryID { get; set; }
public int UserID { get; set; }
public string Description { get; set; }
public DateTime CommentDate { get; set; }
public virtual ICollection<Remark> Remarks { get; set; }
}
public class Remark
{
public int RemarkID { get; set; }
public int CommentID { get; set; }
public int ArticleID { get; set; }
public string RemarkDetail { get; set; }
public DateTime RemarkTime { get; set; }
}
And inside my Controller:
public ActionResult GetArticle(int id)
{
var article = db.Articles.Include("Comments").Where(a => a.ArticleID == id).SingleOrDefault();
return View(article);
}
I understand the basis of eager loading but my questions are:
How do you implement it when you're pulling data from multiple related tables?
What is the best practice of populating it to the View? Once I create a View Model how do I stuff the related collections?
1) With multiple related tables you can have two scenarios:
a) Multiple top level relations: you simply add multiple Include statements (I would suggest using lambda expressions instead of strings for this, to avoid typos).
db.Articles
.Include(a=>a.Comments)
.Include(a=>a.SomethingElse)
.FirstOrDefault(a=>ArticleID==id); // Side note: I would suggest this instead of your Where plus SingleOrDefault
For these scenarios I always use a helper method like this one.
b) Multiple nested related entities:
db.Articles
.Include(a=>a.Comments.Select(c=>c.Remarks)
.FirstOrDefault(a=>ArticleID==id);
2) It's a bit up to you how you pass the data to the views. One best practice I can tell you is that you shouldn't let views lazy load any dependant entities or collections. So your use of Include is correct, but I would even suggest to remove the virtual (deactivate lazy loading) to avoid missing an Include by accident.
Regarding the ViewModels you mention, you are actually not using view models, but your data models. This is OK in most cases, unless you need to format the data somehow or add extra information. Then you would need to create a View Model and map it from the data coming from EF.
Another scenario would be if you used WebAPI or an Ajax Action. In that case, I would suggest to use a DTO (equivalent to a ViewModel) to be able to better control the data returned and its serialization.
One last comment about ViewModels is that if you have heavy entities but you only need a few properties, a good choice is to use Projections, to instruct EF to only load the required properties, instead of the full object.
db.Articles
.Include(a=>a.Comments)
.Select(a=>new ArticleDto { Id = a.ArticleID, Title = a.Title })
.ToListAsync();
This will translate to a "SELECT ArticleID, Title FROM Articles", avoiding returning the article bodies and other stuff that you might not need.
You can chain the relationships with Include. For example:
var article = db.Articles.Include("Comments.Remarks").Where(a => a.ArticleID == id).SingleOrDefault();
I'm not sure what you mean by your second question, though. By issuing this query you already have all the comments and all the remarks for those comments. Therefore, you can access them off of the article instance out of the box:
foreach (var comment in article.Comments)
{
...
foreach (var remark in comment.Remarks)
{
...
}
}
How you handle that with your view model is entirely up to you. You could map the comments/remarks to view models of their own, set them directly on the view model, etc. That's all down to what the needs of your application are, and no one but you can speak to that.

How to handle projections in RavenDB

Given domain model...
public class Entity
{
public int Id { get; set; }
public Category Category { get; set; }
}
public class Category
{
public string Title { get; set; }
}
... I want to project results of a select query to this view model:
public class EntityViewModel
{
public int Id { get; set; }
public string CategoryTitle { get; set; }
}
I have tried the following query:
var viewModel = (from entity in _documentSession.Query<Entity>()
select new EntityViewModel
{
Id = entity.Id,
CategoryTitle = entity.Category.Title
}.ToList();
The result of this is only partially correct: the Id is set, the CategoryTitle is not. I understand this behaviour is by design, but I suspect there is an API to handle this scenario.
How should such a projection be handled in RavenDB?
Update: I am using build 1.0.573 in embedded mode.
Updated 2: I have forked RavenDB repository, added a failing test to demonstrate this behaviour and created a pull request (#444). Will post more info as I find out.
Looks like it is actually a bug. See pull request #444 for more information.
I will update this answer when this is fixed in a stable release.
Fixed in the current stable release.

Categories

Resources