I've been modifying my existing project in mvc-c# and add a new column in one my tables.
Now, I modified my model and add this column polads
[Table("media_order_headers")]
public class OrderHeader
{
[Key]
public int id { get; set; }
/*list of other columns here */
public byte active { get; set; }
public byte with_package { get; set; }
public byte polads { get; set; } //newly added column
So now, i have added this to my Add and Edit controllers
[HttpPost]
public async Task<IActionResult> Add([FromBody]OrderHeaderViewModel model)
{
OrderHeader ord = new OrderHeader
{
/*list of other property value assigning*/
confirmed = model.confirmed,
with_package = model.with_package,
polads = model.polads
};
_context.OrderHeader.Add(ord);
await _context.SaveChangesAsync();
[HttpPost]
public async Task<IActionResult> Edit([FromBody]OrderHeaderViewModel model)
{
var ord = await _context.OrderHeader.Where(f => f.id == model.id).FirstOrDefaultAsync<OrderHeader>();
if (ord != null)
{
ord.with_package = model.with_package;
ord.billing_guide_no = model.bgnumber;
ord.polads = model.polads;
await _context.SaveChangesAsync();
I have not encountered a problem on Add function but in Edit Function.
It doesnt show exception error but it says on the console is internal server error 500. My variable ord is null. When I _context for internal exceptions it shows object not set to an instance of an object.
I have put a breakpoint on this line of script:
var ord = await _context.OrderHeader.Where(f => f.id == model.id).FirstOrDefaultAsync<OrderHeader>();
The view is returning set of values from the form.
my question is that, what might be the reason for this null error if it works with Add controller?
Any idea will be appreciated.
Thanks !
Related
I have an entity framework model as:
public class Campaign
{
public string Name { get; set; } = string.Empty;
public ICollection<CampaignStation> Stations { get; set; } = new List<CampaignStation>();
public ICollection<Order> Orders { get; set; } = new List<Order>();
}
public void Configure(EntityTypeBuilder<Campaign> builder)
{
builder.HasMany(x => x.Stations).WithOne(y => y.Campaign);
builder.HasMany(x => x.Orders).WithOne(y => y.Campaign);
}
So I have a get controller to get results, and I get results as shown in the following picture:
Apparently the service is working correctly
Now in the view, the status shows an error:
But the header show status OK
I have no idea what is wrong. If I try to access a model property like
<p>#Model.Select(x=> x.Name).ToString()</p>
It shows:
System.Linq.Enumerable+SelectListIterator`2[Project.Lib.Models.Campaign,System.String]
Controller:
[HttpGet]
public async Task<IActionResult> Index()
{
var results = await _campaignsService.GetCampaignsAsync();
return View(results);
}
Service:
public async Task<IList<Campaign>> GetCampaignsAsync()
{
return await _db.Campaigns
.Include(a => a.Agency)
.Include(s => s.Stations)
.ThenInclude(cs => cs.Station)
.ToListAsync();
}
UPDATE
Apparently is something with the EF Migration, I removed the collections and created a new migration and now it does not throw any error, it shows the Name property, etc. But now the question is, how can I return the list of my other two tables and not make any conflict? for some reason public ICollection<CampaignStation> Stations { get; set; } = new List<CampaignStation>(); it is not working but as in my image, it is returning the list correctly!
I'm currently learning ASP.NET Core and having some problem. Basically, I have a model like this:
public class WorkRoom
{
[Key]
public int WorkRoomId { get; set; }
[ForeignKey("Room")]
[Required]
public int RoomId { get; set; }
public Room Room { get; set; }
[ForeignKey("Employee")]
[Required]
public string Id { get; set; }
public virtual ApplicationUser Employee { get; set; }
}
and a Dto for this model cause i only want to pass two value is RoomId and Id(using identityUser).
WorkRoomDto.cs
public class WorkRoomDto
{
public int RoomId { get; set; }
public string Id { get; set; }
}
I can write POST, GET and DELETE method normally as i wish, but I have some problems writing PUT.
public async Task<Response> PutWorkRooms(int id, WorkRoomDto workRoom)
{
var respone = new Response();
var workRoomId = _context.WorkRooms.Any(i => i.WorkRoomId == id);
var userId = await userManager.FindByIdAsync(workRoom.Id);
if (workRoomId)
{
if (userId == null)
{
respone.Status = false;
respone.Message = "User not exist";
return respone;
}
var newWorkRoom = mapper.Map<WorkRoomDto, WorkRoom>(workRoom);
_context.Entry(newWorkRoom).State = EntityState.Modified;
await _context.SaveChangesAsync();
respone.Status = true;
respone.Message = "Successfully updated.";
respone.Data = workRoom;
}
else
{
respone.Status = false;
respone.Message = "WorkRoomId no exist.";
}
return respone;
}
But it showed Microsoft.EntityFrameworkCore.DbUpdateConcurrencyException: Database operation expected to affect 1 row(s) but actually affected 0 row(s).
Before the autoMappter I had tried
_context.Entry(workRoom).State = EntityState.Modified;
await _context.SaveChangesAsync();
But it says the Dto is not in the DbContext. How can I fix this?
From your code, WorkRoomId is 0 in newWorkRoom. You need set the value for it. Just change like below:
var newWorkRoom = mapper.Map<WorkRoomDto, WorkRoom>(workRoom);
newWorkRoom.WorkRoomId = id; //add this line...
_context.Entry(newWorkRoom).State = EntityState.Modified;
await _context.SaveChangesAsync();
Automapper give you a brand new object that created locally (not taking back from the database), therefore, not tracked by the dbContext.
So, when you try to update newWorkRoom by forcing the state to modified, the dbContext know it want to see an update operation, but unfortunately, nothing got tracked, then execute nothing.
Try this
var newWorkRoom = mapper.Map<WorkRoomDto, WorkRoom>(workRoom);
if (_context.Entry(newWorkRoom).State == EntityState.Detached)
{
var entityFromDb = await _context.WorkRooms.FindAsync(id);
_context.Entry(entityFromDb).CurrentValues.SetValues(newWorkRoom);
}
I have a web project that everything is working and this below line works for other models except for this one. I'm just needing some info on where to start looking for the solution at.
When I debug it I see that it is getting all the new data that has been edited but it does not assign the new data to EditedtimeEntry. The EditedtimeEntry var has the old data not the new data that was edited. I looked at the timeEntry.Id and it has the new edit its just not being assigned to the EditedtimeEntry. There is no exception or build errors it just does not save the changes, and it looks like the reason it is not save the changes is because the EditedtimeEntry var is not getting the new data assigned to it for some reason. Can anyone point me in the right direction?
TimeEntry EditedtimeEntry = db.TimeEntries.Find(timeEntry.Id);
Here is the Full method with the problem:
public ActionResult Edit( [Bind(Include = "Id,Description,Rate,Paid,Tech,Company")] TimeEntry timeEntry)
{
if (ModelState.IsValid)
{
TimeEntry EditedtimeEntry = db.TimeEntries.Find(timeEntry.Id);
Technician tech = db.Technician.Single(m => m.PhoneNumber == timeEntry.Tech.PhoneNumber);
EditedtimeEntry.Tech = tech;
Company comp = db.Companies.Single(m => m.Name == timeEntry.Company.Name);
EditedtimeEntry.Company = comp;
db.Entry(EditedtimeEntry).State = EntityState.Modified;
db.SaveChanges();
return RedirectToAction("Index");
}
return View(timeEntry);
}
I have other methods for other models that are identical to this one and it works. Here is alsos the model
public class TimeEntry
{
[Key]
public Guid Id { get; set; }
public DateTime StartTime { get; set; }
public DateTime EndTime { get; set; }
public string Description { get; set; }
public Rate Rate { get; set; }
public Technician Tech { get; set; }
public bool Paid { get; set; }
public Company Company { get; set; }
}
public enum Rate { Desktop, Network, Remote, Phone }
Thanks =)
Basically, what you are doing is exactly the contrary of what you want to do :) when you recover the item from the database, you are simply getting the item unchanged that is still in database. What you want is update the database item and then save changes (with EntityState.Modified then SaveChanges())
You simply want to edit timeEntry so that all changes done in the UI are translated into DB :
public ActionResult Edit( [Bind(Include = "Id,Description,Rate,Paid,Tech,Company")] TimeEntry timeEntry)
{
if (ModelState.IsValid)
{
Technician tech = db.Technician.Single(m => m.PhoneNumber == timeEntry.Tech.PhoneNumber);
timeEntry.Tech = tech;
Company comp = db.Companies.Single(m => m.Name == timeEntry.Company.Name);
timeEntry.Company = comp;
db.Entry(timeEntry).State = EntityState.Modified;
db.SaveChanges();
return RedirectToAction("Index");
}
return View(timeEntry);
}
I have an object with a child collection as such:
public class ClaimGroup : BaseModel
{
public int Id { get; set; }
[Required,StringLength(100)]
public string Name { get; set; }
public int Order { get; set; }
public bool Include { get; set; }
public virtual ICollection<ClaimGroupItem> Items { get; set; }
}
The ClaimGroupItem is:
public class ClaimGroupItem : BaseModel
{
[Key,Column(Order = 0),DatabaseGenerated(DatabaseGeneratedOption.None)]
public int ClaimGroupId { get; set; }
[ForeignKey("ClaimGroupId")]
public virtual ClaimGroup ClaimGroup { get; set; }
[Key,Column(Order = 1),DatabaseGenerated(DatabaseGeneratedOption.None)]
public int MenuItemId { get; set; }
[ForeignKey("MenuItemId")]
public virtual MenuItem MenuItem { get; set; }
public string ClaimValue { get; set; }
}
As you can see, it has a composite primary key: MenuItemId and ClaimGroupId.
On updating, it creates duplicates of the ClaimGroupItem objects, with POCO objects being set correctly, but then it creates dynamic proxy items with the same values, hence duplicating the objects.
E.g.:
var items = viewModel.Items.Where(c => !string.IsNullOrEmpty(c.ClaimValue));
The items collection above containts 10 ClaimGroupItemViewModel objects as shown in the image below.
However, when I map the viewModel objects to the Model objects, the collection then has 20 objects, 10 of which are proxy items as seen below:
itemToSave.Items = (from i in items
select new ClaimGroupItem
{
ClaimValue = i.ClaimValue,
MenuItemId = i.MenuItemId,
})
.ToList();
Then when the object goes to save, I get the following error:
_repository.Update<ClaimGroup>(itemToSave);
Violation of PRIMARY KEY constraint 'PK_dbo.ClaimGroupItems'. Cannot
insert duplicate key in object 'dbo.ClaimGroupItems'. The duplicate
key value is (20, 6). The statement has been terminated.
The error makes sense, EF is trying to save 10 duplicate objects.
Why is Entity Framework creating 10 new objects and hence duplicates?
Here is the code on POST that gets the whole list of 79 items in viewModel.Items, then we select just the ones with claimvalue not null. There are NO duplicates at this stage.
[HttpPost, ValidateAntiForgeryToken]
public ActionResult Group([Bind(Include = "Id,Name,Order,Include,Items")] ClaimGroupViewModel viewModel)
{
if (ModelState.IsValid)
{
ClaimGroup itemToSave = _repository.Get<ClaimGroup>(viewModel.Id);
itemToSave.Include = viewModel.Include;
itemToSave.Name = viewModel.Name;
itemToSave.Order = viewModel.Order;
var items = viewModel.Items.Where(c => !string.IsNullOrEmpty(c.ClaimValue));
// There is just 10 items in items variable at this point
itemToSave.Items = (from i in items
select new ClaimGroupItem
{
ClaimValue = i.ClaimValue,
MenuItem = new MenuItem { Id = i.MenuItemId}
})
.ToList();
_repository.Update<ClaimGroup>(itemToSave);
return RedirectToAction("Groups", new { updated = true });
}
return View(viewModel);
}
If you don't need Proxies, during construction of your DBContext set ProxyCreationEnabled = false and check. I have seen cases when we don't even need Proxies and EF by default creates one for all Entities.
I finally got it working.
It was as simple as calling the
itemToSave.Items.Clear()
method to make sure it doesn't load the old proxy objects. What a pain that was to figure out! Here is my working code. Thanks for everyone's help.
[HttpPost, ValidateAntiForgeryToken]
public ActionResult Group([Bind(Include = "Id,Name,Order,Include,Items")] ClaimGroupViewModel viewModel)
{
if (ModelState.IsValid)
{
ClaimGroup itemToSave = _repository.Get<ClaimGroup>(viewModel.Id);
itemToSave.Include = viewModel.Include;
itemToSave.Name = viewModel.Name;
itemToSave.Order = viewModel.Order;
itemToSave.Items.Clear();// This needs to be done, otherwise it will try and load the list from the DB again.
itemToSave.Items = (from i in viewModel.Items
where !string.IsNullOrEmpty(i.ClaimValue)
select new ClaimGroupItem
{
ClaimValue = i.ClaimValue,
MenuItemId = i.MenuItemId,
})
.ToList();
_repository.Update<ClaimGroup>(itemToSave);
return RedirectToAction("Groups", new { updated = true });
}
return View(viewModel);
}
I am developing an C# MVC 4 internet application and I have a question about adding an item to a list.
Here is my class code:
public class MapLocationCompany
{
public MapLocationCompany()
{
MapLocationList = new List<MapLocation>();
}
[Key]
public int id { get; set; }
public List<MapLocation> MapLocationList { get; set; }
}
public class MapLocationCompanyContext : DbContext
{
public DbSet<MapLocationCompany> MapLocationCompanies { get; set; }
}
I am creating a MapLocation object to add to the MapLocationList in the MapLocationCompany class.
Here is my code:
private MapLocationCompanyContext mlcc = new MapLocationCompanyContext();
[HttpPost]
[ValidateAntiForgeryToken]
public ActionResult Create(int id, MapLocation maplocation)
{
if (ModelState.IsValid)
{
MapLocationCompany mapLocationCompany = new MapLocationCompany();
mapLocationCompany = cs.getMapLocationCompany(id);
mapLocationCompany.MapLocationList.Add(maplocation);
mlcc.SaveChanges();
return RedirectToAction("MapLocations", "Company", new { id = id });
}
return View(maplocation);
}
Here is the cs.getMapLocationCompany function that is in a different class
public MapLocationCompany getMapLocationCompany(int id)
{
return db.MapLocationCompanies.Where(c => c.id == id).FirstOrDefault();
}
The db is a MapLocationCompanyContext object.
The maplocation object in the Create function is not being stored in the database.
Can I please have some help with this code?
Thanks in advance
EDIT
Can I please have some help to add the MapLocation object to the list using the Context?
Here is the code I am currently working on:
[HttpPost]
[ValidateAntiForgeryToken]
public ActionResult Create(int id, MapLocation maplocation)
{
if (ModelState.IsValid)
{
MapLocationCompany mapLocationCompany = new MapLocationCompany();
mapLocationCompany = cs.getMapLocationCompany(id);
mlcc.MapLocationCompanies.Where(c => c.id == id).FirstOrDefault().Add(maplocation);
mlcc.SaveChanges();
return RedirectToAction("MapLocations", "Company", new { id = id });
}
return View(maplocation);
}
This is the error I am getting:
'CanFindLocation.Classes.MapLocationCompany' does not contain a definition for 'Add' and no extension method 'Add' accepting a first argument of type 'CanFindLocation.Classes.MapLocationCompany' could be found
How can I add an object to the list, knowing the id of the MapLocationCompany?
You are trying to add a MapLocation directly to the MapLocationCompany object here:
mlcc.MapLocationCompanies.Where(c => c.id == id).FirstOrDefault().Add(maplocation);
That is incorrect. You want to add it to the MapLocationList property of a MapLocationCompany object. This might make it clearer:
var company = mlcc.MapLocationCompanies.Where(c => c.id == id).FirstOrDefault();
if (company != null) {
company.MapLocationList.Add(mapLocation);
}