When I add a column at Database, how to renew my DB.Context.
it is my Context.cs
modelBuilder.Entity<SensorModbusRtusetting>(entity =>
{
entity.ToTable("SensorModbusRTUSetting");
entity.Property(e => e.Id).HasColumnName("id");
entity.Property(e => e.Baudrate).HasColumnName("baudrate");
entity.Property(e => e.Channel)
.IsRequired()
.HasColumnType("jsonb")
.HasColumnName("channel");
entity.Property(e => e.Comport)
.IsRequired()
.HasColumnName("comport");
entity.Property(e => e.Pid).HasColumnName("pid");
entity.Property(e => e.Slaveid).HasColumnName("slaveid");
entity.Property(e => e.Nane).HasColumnName("nane");
It is my model
namespace Sensormanager2V2
{
public partial class SensorModbusRtusetting
{
public string Comport { get; set; }
public int Baudrate { get; set; }
public string Channel { get; set; }
public int Slaveid { get; set; }
public int Pid { get; set; }
public long Id { get; set; }
public string Nane { get; set; }
}
}
it is my controller
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.Rendering;
using Microsoft.EntityFrameworkCore;
using Sensormanager2V2;
namespace Sensormanager2V2.Controllers
{
public class SensorModbusRtusettingsController : Controller
{
private readonly postgresContext _context;
public SensorModbusRtusettingsController(postgresContext context)
{
_context = context;
}
// GET: SensorModbusRtusettings
public async Task<IActionResult> Index()
{
return View(await _context.SensorModbusRtusettings.ToListAsync());
}
// GET: SensorModbusRtusettings/Details/5
public async Task<IActionResult> Details(long? id)
{
if (id == null)
{
return NotFound();
}
var sensorModbusRtusetting = await _context.SensorModbusRtusettings
.FirstOrDefaultAsync(m => m.Id == id);
if (sensorModbusRtusetting == null)
{
return NotFound();
}
return View(sensorModbusRtusetting);
}
// GET: SensorModbusRtusettings/Create
public IActionResult Create()
{
return View();
}
// POST: SensorModbusRtusettings/Create
// To protect from overposting attacks, enable the specific properties you want to bind to, for
// more details, see http://go.microsoft.com/fwlink/?LinkId=317598.
[HttpPost]
[ValidateAntiForgeryToken]
public async Task<IActionResult> Create([Bind("Comport,Baudrate,Channel,Slaveid,Pid,Id,Name")] SensorModbusRtusetting sensorModbusRtusetting)
{
if (ModelState.IsValid)
{
_context.Add(sensorModbusRtusetting);
await _context.SaveChangesAsync();
return RedirectToAction(nameof(Index));
}
return View(sensorModbusRtusetting);
}
// GET: SensorModbusRtusettings/Edit/5
public async Task<IActionResult> Edit(long? id)
{
if (id == null)
{
return NotFound();
}
var sensorModbusRtusetting = await _context.SensorModbusRtusettings.FindAsync(id);
if (sensorModbusRtusetting == null)
{
return NotFound();
}
return View(sensorModbusRtusetting);
}
// POST: SensorModbusRtusettings/Edit/5
// To protect from overposting attacks, enable the specific properties you want to bind to, for
// more details, see http://go.microsoft.com/fwlink/?LinkId=317598.
[HttpPost]
[ValidateAntiForgeryToken]
public async Task<IActionResult> Edit(long id, [Bind("Comport,Baudrate,Channel,Slaveid,Pid,Id,Name")] SensorModbusRtusetting sensorModbusRtusetting)
{
if (id != sensorModbusRtusetting.Id)
{
return NotFound();
}
if (ModelState.IsValid)
{
try
{
_context.Update(sensorModbusRtusetting);
await _context.SaveChangesAsync();
}
catch (DbUpdateConcurrencyException)
{
if (!SensorModbusRtusettingExists(sensorModbusRtusetting.Id))
{
return NotFound();
}
else
{
throw;
}
}
return RedirectToAction(nameof(Index));
}
return View(sensorModbusRtusetting);
}
// GET: SensorModbusRtusettings/Delete/5
public async Task<IActionResult> Delete(long? id)
{
if (id == null)
{
return NotFound();
}
var sensorModbusRtusetting = await _context.SensorModbusRtusettings
.FirstOrDefaultAsync(m => m.Id == id);
if (sensorModbusRtusetting == null)
{
return NotFound();
}
return View(sensorModbusRtusetting);
}
// POST: SensorModbusRtusettings/Delete/5
[HttpPost, ActionName("Delete")]
[ValidateAntiForgeryToken]
public async Task<IActionResult> DeleteConfirmed(long id)
{
var sensorModbusRtusetting = await _context.SensorModbusRtusettings.FindAsync(id);
_context.SensorModbusRtusettings.Remove(sensorModbusRtusetting);
await _context.SaveChangesAsync();
return RedirectToAction(nameof(Index));
}
private bool SensorModbusRtusettingExists(long id)
{
return _context.SensorModbusRtusettings.Any(e => e.Id == id);
}
}
}
If you update the structure of a table in your database, you need to ensure that your change is propagated to the data model, views, and controller.
For this tutorial, you will add a new column to the Student table to record the middle name of the student. To add this column, open the database project, and open the Student.sql file. Through either the designer or the T-SQL code, add a column named MiddleName that is an NVARCHAR(50) and allows NULL values.
Deploy this change to your local database by starting your database project (or F5). The new field is added to the table. If you do not see it in the SQL Server Object Explorer, click the Refresh button in the pane.
The new column exists in the database table, but it does not currently exist in the data model class. You must update the model to include your new column. In the Models folder, open the ContosoModel.edmx file to display the model diagram. Notice that the Student model does not contain the MiddleName property. Right-click anywhere on the design surface, and select Update Model from Database.
In the Update Wizard, select the Refresh tab and then select Tables > dbo > Student. Click Finish.
After the update process is finished, the database diagram includes the new MiddleName property. Save the ContosoModel.edmx file. You must save this file for the new property to be propagated to the Student.cs class. You have now updated the database and the model.
Build the solution.
Related
I'm strugglish with adding feature for my controller. While adding new item, receving the error like: "An error occurred while updating the entries. See the inner exception for details."
I debugged it, and understood ProductDetailIs is null and here is the issue. But, can not figure out how to mend the problem.
Here is the DTO models:
public class WishlistItemDto
{
public int Id { get; set; }
public string CustomerId { get; set; }
public ProductDetailsDtoWithPrimaryImage ProductDetails { get; set; }
public int Quantity { get; set; }
}
public class WishListItemCreationDto
{
public string CustomerId { get; set; }
public int ProductDetailId { get; set; }
public int Quantity { get; set; }
}
Controller:
[HttpPost]
public async Task<IActionResult> Add(WishListItemCreationDto wishListItemDto)
{
var itemAdd = _mapper.Map<WishlistItemDto>(wishListItemDto);
var itemCreated = await _wishListItemService.AddAsync(itemAdd);
return CreatedAtAction(nameof(GetId), new { id = itemCreated.Id }, wishListItemDto);
}
Service:
public async Task<WishlistItemDto> AddAsync(WishlistItemDto item)
{
var entity = _mapper.Map<WishlistItem>(item);
await _wishListItemRepository.AddAsync(entity);
return _mapper.Map<WishlistItemDto>(entity);
}
Repository:
public async Task<WishlistItem> AddAsync(WishlistItem item)
{
await _context.Set<WishlistItem>().AddAsync(item);
await _context.SaveChangesAsync();
return item;
}
This line here:
var itemAdd = _mapper.Map<WishlistItemDto>(wishListItemDto);
your "wishListItemDto" is passed in as a 'WishListItemCreationDto' which contains only a ProductDetailsId. Automapper will have no way of knowing how to convert that into a ProductDetailsDtoWithPrimaryImage.
Typically for something like this where you pass an reference ID you would compose your entity by either populating a FK or loading the referenced entity. Your existing service and repository patterns will complicate your final solution. From what I can see from your example I'd look at creating an AddAsync method that accepts the WishListItemCreationDto:
public async Task<WishlistItemCreationDto> AddAsync(WishlistItemCreationDto item)
{
var entity = _mapper.Map<WishlistItem>(item);
var productDetails = _productDetailsRepository.GetById(item.ProductDetailsId);
entity.ProductDetails = productDetails;
await _wishListItemRepository.AddAsync(entity);
return _mapper.Map<WishlistItemDto>(entity);
}
Without the added abstraction complexity of the Service and Repository the add operation can be a whole lot simpler:
[HttpPost]
public async Task<IActionResult> Add(WishListItemCreationDto wishListItemDto)
{
// or better, use an injected dependency to the Context...
// TODO: add applicable exception handling.
using(var context = new AppDbContext())
{
var item = _mapper.Map<WishlistItem>(wishListItemDto);
var productDetails = context.ProductDetails.Single(x => x.ProductDetailsId == wishListItemDto.ProductDetailsId);
item.ProductDetails = productDetails;
context.SaveChanges();
return CreatedAtAction(nameof(GetId), new { id = itemCreated.Id }, wishListItemDto);
}
}
I am getting changed entity from fronted, mapping it on backend side and simply want to update it in database.
Update is performing like this:
[HttpPut("{id}")]
public IActionResult Update(string id, [FromBody]Worker worker)
{
using (var dbContext= new MyDbContext())
{
dbContext.ChangeTracker.QueryTrackingBehavior = QueryTrackingBehavior.NoTracking;
var entity = dbContext.Workers.FirstOrDefault(r => r.Id == worker.Id);
if (entity == null) return BadRequest();
dbContext.Workers.Update(worker);
dbContext.SaveChanges();
return Ok();
}}
Before this action, i am getting the list of users and sending it to frontend.
Although I set QueryTrackingBehavior to NoTracking, i am getting exception:
System.InvalidOperationException: 'The instance of entity type 'Contract' cannot be tracked because another instance with the key value 'Id:4' is already being tracked. When attaching existing entities, ensure that only one entity instance with a given key value is attached.'
Where Contract is related entity for Worker which is updated...
Any idea what i am doing wrong here?
UPDATE:
Worker - Contract relation:
public class Worker: IId
{
public int Id { get; set; }
public ICollection<Contract> Contracts{ get; set; }
}
public class Contract: IId
{
public int Id { get; set; }
public int WorkerId { get; set; }
public Worker Worker { get; set; }
}
Okay! got the problem in your code. You didn't map the updated entity to the existing entity that you pulled from the database. You have to map the updated entity to the existing entity. To do so you can use AutoMapper or explicit mapping as follows:
You can solve the problem as follows:
[HttpPut("{id}")]
public IActionResult Update(string id, [FromBody]Worker worker)
{
using (var dbContext= new MyDbContext())
{
var entityToBeUpdated = dbContext.Workers.AsNoTracking().FirstOrDefault(r => r.Id == worker.Id);
if (entity == null) return BadRequest();
entityToBeUpdated.Property1 = worker.Property1;
entityToBeUpdated.Property2 = worker.Property2;
// Do the same for the other changed properties as well
dbContext.Workers.Update(entityToBeUpdated);
dbContext.SaveChanges();
return Ok();
}
}
Alternatively you can try as follows:
[HttpPut("{id}")]
public IActionResult Update(string id, [FromBody]Worker worker)
{
using (var dbContext= new MyDbContext())
{
var entityToBeUpdated = dbContext.Workers.FirstOrDefault(r => r.Id == worker.Id);
if (entity == null) return BadRequest();
entityToBeUpdated.Property1 = worker.Property1;
entityToBeUpdated.Property2 = worker.Property2;
// Do the same for the other changed properties as well.
dbContext.SaveChanges();
return Ok();
}
}
I'm building some REST API server in .NET Core and using Postman software to test it. I have a problem with POST method which doesn't return me any value ("Could not get any response") when I try to perform second Add operation on my DBContext class inside CreateUser method. My code :
UsersController :
[Produces("application/json")]
[Route("api/[controller]")]
public class UsersController : Controller
{
private readonly DBContext _context;
#region CONSTRUCTOR
public UsersController(DBContext context)
{
_context = context;
}
#endregion
#region HTTP GET
// GET: api/users || api/users?cardnr=xxx
[HttpGet]
public async Task<IActionResult> GetUsers(string cardNr)
{
if (String.IsNullOrEmpty(cardNr))
{
try
{
var users = await _context.Users.ToListAsync();
if (users.Any())
{
return Json(users);
}
else
{
return NotFound();
}
}
catch (Exception ex)
{
Helpers.ExceptionLogger.LogException(ex);
return StatusCode(500);
}
}
else
{
try
{
var user = await _context.Users.FirstOrDefaultAsync(u => u.Cards.Any(c => c.CardNumber.Equals(cardNr)));
if (user == null)
{
return NotFound();
}
else
{
return new ObjectResult(user);
}
}
catch (Exception ex)
{
Helpers.ExceptionLogger.LogException(ex);
return StatusCode(500);
}
}
}
//GET: api/users/1
[HttpGet("{id}", Name = "GetUserByID")]
public async Task<IActionResult> GetUserByID(Int32 id)
{
try
{
var user = await _context.Users.FirstOrDefaultAsync(u => u.IDUser == id);
if (user == null)
{
return NotFound();
}
else
{
return new ObjectResult(user);
}
}
catch (Exception ex)
{
Helpers.ExceptionLogger.LogException(ex);
return StatusCode(500);
}
}
#endregion
#region HTTP POST
[HttpPost]
public async Task<IActionResult> CreateUser([FromBody] Models.User userToCreate, string userGroupID)
{
if (userToCreate == null)
{
return BadRequest();
}
else
{
try
{
_context.Users.Add(userToCreate);
int parsingResult;
// if user passed userGroupID
if (userGroupID != null)
{
// parsing if userGroupID is a number
if (!int.TryParse(userGroupID, out parsingResult))
{
return BadRequest();
}
else
{
// if client want to assign a new user to some group
if (parsingResult > 0)
{
// creating new record in UserGroup table - assigning a user to group
var userGroup = new Models.UserGroup();
_context.Entry(userGroup).Property("IDGroup").CurrentValue = parsingResult;
_context.Entry(userGroup).Property("IDUser").CurrentValue = userToCreate.IDUser;
_context.UserGroups.Add(userGroup); // NOTE HERE
}
}
}
await _context.SaveChangesAsync();
return CreatedAtRoute("GetUserByID", new { id = userToCreate.IDUser }, userToCreate);
}
catch (Exception ex)
{
Helpers.ExceptionLogger.LogException(ex);
return StatusCode(500);
}
}
}
#endregion
}
User model :
public class User
{
[Key]
public int IDUser { get; set; }
[Required]
public string Name { get; set; }
public List<UserGroup> UsersGroups { get; set; }
}
UserGroup model :
public class UserGroup
{
public Group Group { get; set; }
public User User { get; set; }
}
DBContext class :
public class DBContext : DbContext
{
public DBContext(DbContextOptions<DBContext> options)
: base(options)
{
}
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
// shadow property - foreign key
modelBuilder.Entity<UserGroup>()
.Property<int>("IDUser");
// shadow property - foreign key
modelBuilder.Entity<UserGroup>()
.Property<int>("IDGroup");
modelBuilder.Entity<UserGroup>()
.HasKey( new string[]{ "IDUser", "IDGroup" });
modelBuilder.Entity<UserGroup>()
.HasOne(ug => ug.Group)
.WithMany(g => g.UsersGroups)
.HasForeignKey("IDGroup");
modelBuilder.Entity<UserGroup>()
.HasOne(ug => ug.User)
.WithMany(u => u.UsersGroups)
.HasForeignKey("IDUser");
base.OnModelCreating(modelBuilder);
}
public DbSet<Group> Groups { get; set; }
public DbSet<User> Users { get; set; }
public DbSet<UserGroup> UserGroups { get; set; }
}
The problem lies in HttpPost method in UsersController.
When I do "normal" POST and pass JSON object which contain a user to add without assigning it to group (empty userGroupID parameter) everything is ok - user gets added to the DataBase and Postman returns me a user with its ID.
screen :
and when I try to add a new user but with adding it to specific group I always get an error :
screen :
Even despite that error new user gets properly added to DB and associated with its group (record gets added to UserGroup table; UserGroup is join table between Users and Groups table). So I have proper data in my DB but I always get this error and I can't return new added user to client who called API and can't get his ID. Am I doing something wrong in my CreateUser method ?
UPDATE :
I have added a comment line in "NOTE HERE" in CreateUser method in UsersController. If I comment whole this line I don't get an error from Postman but obviously I don't get my User associated with its group (I don't get new record added to UserGroup join table). So it seems like another Add method on context object causing an error ... Does it make sense ?
Did you try to debug it?
Set a breakpoint on the row:
if (userToCreate == null)
Send again the request with Postman and debug your app. There you can see what and where it goes wrong.
Please let me know how it is going so I know how can I help you:)
I have a many-to-many relationship (Users to skills), and when I try to associate an existing skill to a user, it always creates a new one.
User:
public class ApplicationUser : IdentityUser
{
public async Task<ClaimsIdentity> GenerateUserIdentityAsync(UserManager<ApplicationUser> manager)
{
// Note the authenticationType must match the one defined in CookieAuthenticationOptions.AuthenticationType
var userIdentity = await manager.CreateIdentityAsync(this, DefaultAuthenticationTypes.ApplicationCookie);
// Add custom user claims here
return userIdentity;
}
public virtual ICollection<Skill> Skills { get; set; }
}
Skill:
public class Skill
{
public int Id { get; set; }
public string Name { get; set; }
}
OnModelCreating:
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
base.OnModelCreating(modelBuilder);
modelBuilder.Entity<ApplicationUser>()
.HasMany<Skill>(user => user.Skills)
.WithMany();
}
Creating the association:
public ActionResult Skills(SkillsViewModel viewModel)
{
var user = UserManager.FindById(User.Identity.GetUserId());
if(!string.IsNullOrWhiteSpace(viewModel.NewSkill)
&& !user.Skills
.Where(sk => sk.Name == viewModel.NewSkill)
.Any())
{
var foundSkill = this.db.Skills
.Where(sk => sk.Name == viewModel.NewSkill)
.FirstOrDefault();
if(foundSkill != null)
{
user.Skills.Add(foundSkill);
}
else
{
user.Skills.Add(new Skill()
{
Name = viewModel.NewSkill
});
}
}
if(viewModel.SelectedSkillId > 0)
{
var foundSkill = this.db.Skills.Find(viewModel.SelectedSkillId);
user.Skills.Add(foundSkill);
}
this.UserManager.Update(user);
return RedirectToAction("skills");
}
I've stepped through, and verified that I do indeed get a 'foundSkill' from the database, but after I add it to the user, and save the user, the skill associated to the user is not the one I found, but a new one with the same name and different ID.
I figured it out. The UserManager was being loaded with one DbContext, and I was trying to associate a Skill loaded from a different DbContext.
Quick hack to test and fix this was to load the user from the same DbContext as the Skills, update the user, save the DbContext.
Longer term solution would be to ensure that everything uses the same DbContext per request.
I'm using EF6 code first with MVC 5. I have two objects, Movie and User, with each object having a collection of the other (many-to-many). Using an existing User, I'm trying to associate that User to a Movie but no rows are being inserted into the database. The Movie could be existing or new, but either way the association is not being created.
Movie is just a simple POCO
User inherits from IdentityUser
public class User : IdentityUser {
public virtual ICollection<Movie> Movies { get; set; }
public User() {
Movies = new Collection<Movie>();
}
public async Task<ClaimsIdentity> GenerateUserIdentityAsync(UserManager<User> manager) {
var userIdentity = await manager.CreateIdentityAsync(this, DefaultAuthenticationTypes.ApplicationCookie);
return userIdentity;
}
}
My Controller Action:
public async Task<HttpResponseMessage> Post(Movie rawMovie) {
try {
var movie = _store.Movies.Get(m => m.Id == rawMovie.Id).FirstOrDefault();
if (movie == null) {
movie = rawMovie;
_store.Movies.Insert(movie);
movie.Cast.Where(n => _store.Cast.Get(e => e.Id == n.Id).Select(e => e.Id).Contains(n.Id))
.ToList()
.ForEach(c => _store.Context.Entry(c).State = EntityState.Unchanged);
}
var user = await UserManager.FindByIdAsync(User.Identity.GetUserId());
if(user == null) return Request.CreateErrorResponse(HttpStatusCode.Unauthorized, "Invalid User");
user.Movies.Add(movie);
return Request.CreateResponse(_store.SaveChanges());
} catch (Exception e) {
return Request.CreateErrorResponse(HttpStatusCode.InternalServerError, e.Message);
}
}
I use the new IdentityDbContext as my single context, so it's used for both authentication and my POCO models - meaning that both Movie and User : IdentityUser share the same the context.
public class ApplicationContext : IdentityDbContext<User> {
public DbSet<Movie> Movies { get; set; }
public DbSet<Character> Cast { get; set; }
public ApplicationContext()
: base("MoovyConnection", throwIfV1Schema: false) { }
public static ApplicationContext Create() {
return new ApplicationContext();
}
protected override void OnModelCreating(DbModelBuilder modelBuilder) {
base.OnModelCreating(modelBuilder);
modelBuilder.Entity<Movie>().HasMany(m => m.Cast).WithMany(c => c.Movies)
.Map(t => t.MapLeftKey("Movid_Id").MapRightKey("Character_Id").ToTable("MovieCharacters"));
}
}
I've found this example but user.Movies does not have an attach method as it is only an ICollection.
What is the proper way to associate two objects to each other in a many-to-many relationship in EF6?