Cannot save multiple files to database with Foreign Key - sql-server

In my MVC application I have two tables called Ticket and Attachment and I want to save attachments for per ticket. The problem is that: I need to save multiple attachments with the TicketID when creating a new ticket. So, I think I should create a new ticket in the Ticket table and then get its ID and save all the attachments with this TicketID to the Attachment table in a loop. I have look at many web sites and stackoverflow, but there is not such a kind of problem or solution on that pages. Any idea?
Note: I use Entity Framework Code First, but I can also use Stored Procedure or SQL command for this operation.
Here are these two models:
public class Ticket
{
[Key]
public int ID { get; set; }
public string Description { get; set; }
//... removed for clarifty
}
public class Attachment
{
[Key]
public int ID { get; set; }
//Foreign key for Ticket
public int TicketID { get; set; }
public byte[] FileData { get; set; }
public string FileMimeType { get; set; }
}
And the controller method:
[HttpPost]
[ValidateAntiForgeryToken]
public ActionResult Create([Bind(Exclude = null)] TicketViewModel viewModel
/* contains both model: Ticket and Attachment */, IEnumerable<HttpPostedFileBase> files)
{
if (ModelState.IsValid)
{
//??? First I need to save Ticket
repository.SaveTicket(viewModel.Ticket);
foreach(var f in files)
{
viewModel.Attachment.FileMimeType = f.ContentType;
viewModel.Attachment.FileData = new byte[f.ContentLength];
f.InputStream.Read(viewModel.Attachment.FileData , 0, f.ContentLength);
//??? Then save all attachment. But no idea how to get TicketID
repository.SaveAttachment(viewModel.Attachment);
}
}
return View();
}

The ID property will be automatically filled by EF after a SaveChanges. Your code can then use it. I assume that the viewModel.Ticket object is the actual object saved to the database. If not, please also post the SaveTicket method.
[HttpPost]
[ValidateAntiForgeryToken]
public ActionResult Create([Bind(Exclude = null)] TicketViewModel viewModel
/* contains both model: Ticket and Attachment */, IEnumerable<HttpPostedFileBase> files)
{
if (ModelState.IsValid)
{
// assumes viewModel.Ticket is the actual entity saved, and SaveChanges is called.
repository.SaveTicket(viewModel.Ticket);
foreach(var f in files)
{
viewModel.Attachment.FileMimeType = f.ContentType;
viewModel.Attachment.FileData = new byte[f.ContentLength];
f.InputStream.Read(viewModel.Attachment.FileData , 0, f.ContentLength);
// fill ticket id
viewModel.Attachment.TicketID = viewModel.Ticket.ID;
repository.SaveAttachment(viewModel.Attachment);
}
}
return View();
}
If you want to do everything in one transaction, you can add the childs immediatly, and SaveChanges will save all objects:
[HttpPost]
[ValidateAntiForgeryToken]
public ActionResult Create([Bind(Exclude = null)] TicketViewModel viewModel
/* contains both model: Ticket and Attachment */, IEnumerable<HttpPostedFileBase> files)
{
if (ModelState.IsValid)
{
var ticket = viewModel.Ticket;
foreach(var f in files)
{
var attachment = new Attachment();
attachment.FileMimeType = f.ContentType;
attachment.FileData = new byte[f.ContentLength];
f.InputStream.Read(attachment.FileData , 0, f.ContentLength);
ticket.Attachments.Add(attachment);
}
// this will save the ticket and attachments
repository.SaveTicket(ticket);
}
return View();
}
Your Ticket class will have to look like this:
public class Ticket
{
[Key]
public int ID { get; set; }
public string Description { get; set; }
// EF will now to use the foreign key to the attachment table
public virtual ICollection<Attachment> Attachments { get; set; }
}

Related

Cannot Insert new Data in the Database in .NET Core API error Database operation

I am working on an API and when I started adding new data. I received this error. It was working when I manually add the ID every input but now I got this error and after adding some solutions from here its still not working.
Error:
Microsoft.EntityFrameworkCore.DbUpdateConcurrencyException: Database operation expected to affect 1 row(s) but actually affected 0 row(s).
Data may have been modified or deleted since entities were loaded. See http://go.microsoft.com/fwlink/?LinkId=527962 for information on understanding and handling optimistic concurrency exceptions.
Code for insert:
public bool Insert(string UserName, SendInventoryModel sendInventoryModel)
{
using (DatabaseContext context = new DatabaseContext())
{
bool flag = false;
// Create new
InventoryEntity inventoryEntity = new InventoryEntity
{
UserName = sendInventoryModel.UserName,
Item = sendInventoryModel.Item ,
};
context.Table.Add(inventoryEntity);
context.SaveChanges();
// Check
var model = CheckUserNameID(UserName, sendInventoryModel.Item);
var data = context.Table.Find(model.Id);
if (null != data)
{
flag = true;
}
return flag;
}
}
SendInventoryModel:
public class SendSiteMailModel
{
[Required]
public string UserName { get; set; }
[Required]
public string Item{ get; set; }
}
InventoryController:
[HttpPost("{username}")]
[Authorize]
public JObject Post([Required] string UserName, [FromBody] SendInventoryModel sendInventoryModel)
{
ResponseModel x = new ResponseModel();
try
{
InventoryRepository InventoryRepository = new InventoryRepository();
bool isSuccess = InventoryRepository.Insert(UserName, sendInventoryModel);
}
catch (Exception error)
{
// if not successful
}
return Json(x);
}
I already added [DatabaseGenerated(DatabaseGeneratedOption.Identity)] in my InventoryEntity and InventoryModel.
InventoryEntity:
[Key]
DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public int Id { get; set; }
InventoryModel:
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public int Id { get; set; }
I also added the below code in my DBContext.cs:
public virtual DbSet<OtherTableEntity> Table{ get; set; }
public virtual DbSet<InventoryEntity> Table{ get; set; }
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.Entity<OtherTableEntity>();
modelBuilder.Entity<InventoryEntity>().Property(x => x.Id).ValueGeneratedOnAdd();
base.OnModelCreating(modelBuilder);
}
partial void OnModelCreatingPartial(ModelBuilder modelBuilder);
Add finally my table design: Inventory ID:
(Is Identity) = Yes
Identity Increment = 1
Identity Seed = 1
Note that there is no Primary Key in the Inventory table. And its an old table with existing data. The current database was migrated from membership to identity.
After all the things that I have added the context.SaveChanges(); in the insert method still does not work. Any ideas or suggestion on how to fix this problem?
Note: I've changed the table entity names and models per Asherguru suggestion since its kinda confusing and generic.
Are your TableEntity and Table in database same table names?
Why different names - TableEntity and Table?
Try to add [Table("YourTableNameInDatabase")] in TableEntity class. Then EF can find actual table in database and insert into this table.
[Table("YourTableNameInDatabase")]
public partial class TableEntity
{
public int Id { get; set; }
}
It would be less confusing if you show actual table names with some necessary screenshots.

EmployeeDataContext class not pulling data from Database

below is my code
I am trying to pull data from database using entityframework.
EmployeeDataContext class -
namespace _09032020_1.Models
{
public class EmployeeDataContext : DbContext
{
public DbSet<Employee> Employees { get; set; }
}
}
Employee model -
namespace _09032020_1.Models
{
[Table("TbleEmployee")]
public class Employee
{
public int employeeId { get; set; }
public string employeeName { get; set; }
public string employeeCity { get; set; }
public string employeeGender { get; set; }
public int departmentId { get; set; }
}
}
below are the table columns.
here is the controller code
namespace _09032020_1.Controllers
{
public class EmployeeController : Controller
{
public ActionResult Index()
{
EmployeeDataContext employeeDataContext = new EmployeeDataContext();
Employee employee = new Employee();
List<Employee> employees1 = new List<Employee>();
employees1 = employeeDataContext.Employees.ToList();
return View(employees1);
}
}
}
I am not getting data inside employeeDataContext
Please let me know if more info regarding config file requires.
Create the table w data. The first column is primary key and identity.
In an MVC project (you can use another type, also I am showing database first, and you can use another type, right click on the Models folder and add ADO.NET Entity Data Model named EmployeeDataContext. Choose EF Designer from database. New connection, and choose your db. Save connection as EmployeeDataContext and choose your table.
Put this in your code:
using (EmployeeDataContext context = new EmployeeDataContext())
{
var emps = context.Employees.ToList();
}
I got my answer,
I have written wrong table name as model attribute [Table("TbleEmployee")]
My table name is TblEmplyee. So it should be [Table("TblEmployee")]
As I changed this I abled to proceed forward.

EF Core wrong join statement

I have the following model classes in EF Core 2.2
public class User
{
[Key]
public long Id { get; set; }
[ForeignKey("Post")]
public long? PostId { get; set; }
public virtual Post Post { get; set; }
public virtual ICollection<Post> Posts { get; set; }
}
public class Post
{
[Key]
public long Id { get; set; }
[ForeignKey("User")]
public long UserId { get; set; }
public virtual User User { get; set; }
}
I have checked the relations with SSMS and they are fine.
But when I use
dbContext.Posts.Include(p => p.User);
EF Core generates the following join statement
FROM Posts [p]
LEFT JOIN Users [p.Users] ON [p].[Id] = [p.Users].[PostId]
I'm including User from Post and expect it to be as below
FROM Posts [p]
LEFT JOIN Users [p.Users] ON [p].[UserId] = [p.Users].[Id]
What is wrong with models?
Assume that I want to save last PostId in User model.
Is there an attribute to tell ef core which property to use when joining models?
From the discussion on the other response it looks like you want a User to contain Posts, but then also have the User track a reference to the Latest post. EF can map this, however you will probably need to be a bit explicit about the relationships.
For instance:
[Table("Users")]
public class User
{
[Key]
public int UserId { get; set; }
public string Name { get; set; }
public virtual ICollection<Post> Posts { get; set; } = new List<Post>();
public virtual Post LatestPost { get; set; }
}
[Table("Posts")]
public class Post
{
[Key]
public int PostId { get; set; }
public string PostText { get; set; }
public DateTime PostedAt { get; set; }
public virtual User User { get; set; }
}
then a Configuration to ensure EF wires up the relationship between user and posts correctly:
// EF6
public class UserConfiguration : EntityTypeConfiguration<User>
{
public UserConfiguration()
{
HasMany(x => x.Posts)
.WithRequired(x => x.User)
.Map(x=>x.MapKey("UserId"));
HasOptional(x => x.LatestPost)
.WithMany()
.Map(x=>x.MapKey("LatestPostId"));
}
}
// EFCore
public class UserConfiguration : IEntityTypeConfiguration<User>
{
public void Configure(EntityTypeBuilder<User> builder)
{
builder.HasMany(x => x.Posts)
.WithOne(x => x.User)
.HasForeignKey("UserId");
HasOne(x => x.LatestPost)
.WithMany()
.IsRequired(false)
.HasForeignKey("LatestPostId");
}
}
You can accomplish this in the OnModelCreating event with the modelBuilder reference as well. Note here I am not declaring FK properties in my entities. This too is an option, but I generally recommend not declaring FKs to avoid reference vs. FK update issues. I've named the LatestPost FK as LatestPostId just to reveal a bit more accurately what it is for. It could be mapped to a "PostId" if you so choose.
Now lets say I go to add a new post and I want to associate it to the user, and assign it as the LatestPost for that user:
using (var context = new SomethingDbContext())
{
var user = context.Users.Include(x => x.Posts).Include(x => x.LatestPost)
.Single(x => x.UserId == 1);
var newPost = new Post { PostText = "Test", User = user };
user.Posts.Add(newPost);
user.LatestPost = newPost;
context.SaveChanges();
}
You can update the "latest" post reference by loading the user and setting the LatestPost reference to the desired post.
There is a risk with this structure however that you should consider. The issue is that there is no way to reliably enforce (at a data level) that the LatestPost reference in a User actually references a post associated to that user. For instance, if I have a latest post pointing to a particular post, then I delete that post reference from the user's Posts collection, that can result in FK constraint errors, or simply disassociate the post from the user, but the user latest post still points at that record. I can also assign another user's post to this user's latest post reference. I.e.
using (var context = new SomethingDbContext())
{
var user1 = context.Users.Include(x => x.Posts).Include(x => x.LatestPost)
.Single(x => x.UserId == 1);
var user1 = context.Users.Include(x => x.Posts).Include(x => x.LatestPost)
.Single(x => x.UserId == 2);
var newPost = new Post { PostText = "Test", User = user1 };
user1.Posts.Add(newPost);
user1.LatestPost = newPost;
user2.LatestPost = newPost;
context.SaveChanges();
}
And that would be perfectly fine. User 2's "LatestPostId" would be set to this new post, even though this post's UserId only refers to User1.
A better solution when dealing with something like a Latest post is to not denormalize the schema to accommodate it. Instead, use unmapped properties in the entity for the latest post, or better, rely on projection to retrieve this data when it's needed. In both cases you would remove the LatestPostId from the User table
Unmapped property:
[Table("Users")]
public class User
{
[Key]
public int UserId { get; set; }
public string Name { get; set; }
public virtual ICollection<Post> Posts { get; set; } = new List<Post>();
[NotMapped]
public Post LatestPost
{
get { return Posts.OrderByDescending(x => x.PostedAt).FirstOrDefault(); }
}
}
The caveat of the unmapped property approach is that you need to remember to eager-load Posts on the User if you want to access this property, otherwise you will trip a lazy load. You also cannot use this property in Linq expressions that get sent to EF (EF6) though they may work with EFCore, but risk performance issues if the expression gets translated to in-memory early. EF will not be able to translated LatestPost to SQL since there would be no key in the schema.
Projection:
[Table("Users")]
public class User
{
[Key]
public int UserId { get; set; }
public string Name { get; set; }
public virtual ICollection<Post> Posts { get; set; } = new List<Post>();
}
Then if you want to retrieve a user and it's latest post:
var userAndPost = context.Users.Where(x => x.UserId == userId)
.Select(x => new { User = x, LatestPost = x.Posts.OrderByDescending(PostedAt).FirstOrDefault()} ).Single();
Projection with Select can retrieve entities of interest, or better, simply return the fields from those entities into a flattened view model or DTO to send to UI or such. This results in more efficient queries against the database. Using Select to retrieve the details you don't need to worry about eager-loading via Include, and when done correctly, will avoid pitfalls with lazy loading.
The relationship between User and Post is wrong. This should be your model:
public class User
{
[Key]
public long Id { get; set; }
public virtual ICollection<Post> Posts { get; set; }
}
public class Post
{
[Key]
public long Id { get; set; }
[ForeignKey("User")]
public long UserId { get; set; }
public virtual User User { get; set; }
}
That is one-to-many relationship: one user can have many posts and a post can have just one user.
You had enough of answers on this question to how to create the model , I will just highlight what's wrong in your class
You have craete postid in user model as FK and therefore it will always consider it as relationship between user and post , you have to remove that and design it something similar to what is said by steve

Angular Web API $http post exclude list

I have a Web API (2) project which has departments and employees. An employee has a department, and a department has a list of employees.
Now in the frontend, when creating or editing an employee, the user must select a department. When posting this to the API, the department contains the list of employees (which causes an invalid modelstate), how can I prevent this?
This is my relevant setup:
Models:
public class Employee : IEntity, ICreatedOn, IModifiedOn, IMappable
{
[Key]
public virtual int Id { get; set; }
public virtual Department Department { get; set; }
// .. other properties
}
public class Department : IEntity, IMappable
{
[Key]
public virtual int Id { get; set; }
public virtual ICollection<Employee> Employees { get; set; }
// .. other properties
}
Web API controller:
public class EmployeesController : ApiController
{
private readonly IEmployeeService _employeeService;
public EmployeesController(IEmployeeService employeeService)
{
this._employeeService = employeeService;
}
// .. GET, POST, DELETE etc.
// PUT: api/Employees/5
[ResponseType(typeof(void))]
public IHttpActionResult PutEmployee(int id, EmployeeVM employee)
{
// This is always invalid, because the employee has a department, which in turn has a list of employees which can be invalid
// What to do to exclude the list of employees from validation, or even better prevent from being sent to the API
if (!ModelState.IsValid)
{
return BadRequest(ModelState);
}
// Update etc..
return StatusCode(HttpStatusCode.NoContent);
}
Angular (DataService.js):
app.factory('DataService',
["$http",
function ($http) {
return {
// other functions
updateEmployee: _updateEmployee
}
function _updateEmployee(employee) {
// Maybe exclude the list of employees in employee.department in here??
return $http.put(employeesUrl + "/" + employee.id, employee);
}
// .. other functions
}]);
Notes:
It happens in both the Put and the Post (updating and creating)
I'm using AutoMapper for mapping to the ViewModels, which look the same as the entities
I'm using Entity Framework for the ORM
What I've tried:
[JsonIgnore] attribute on the Employees collection; this causes the Employees also not being loaded when loading the Department
[Bind(Exclude = "Employees")] attribute in the controller action parameters, this did not have any effect
[Bind(Exclude = "Department.Employees")] same
What works, but I'm sure there must be a better solution:
function _updateEmployee(employee) {
var newEmp = angular.copy(employee);
delete newEmp.department.employees;
return $http.put(employeesUrl, newEmp);
}
Create a new request for updating your employee. Like:
public class UpdateEmployeeRequest{
public int EmployeeId {get;set;}
public int DepartmentId {get;set;}
//and so on
}
For this request you can specify concrete validation.
And declare Entities with explicit ID.:
public class Employee : IEntity, ICreatedOn, IModifiedOn, IMappable
{
[Key]
public int Id { get; set; }
[ForeignKey( "Department" )]
public Guid DepartmentId { get; set; }
[Required]
public virtual Department Department { get; set; }
// .. other properties
}
I would modify the Employee entity like this
public class Employee : IEntity, ICreatedOn, IModifiedOn, IMappable
{
[Key]
public virtual int Id { get; set; }
//Add DepartmentId
public Guid? DepartmentId { get; set; }
public virtual Department Department { get; set; }
// .. other properties
}
Then you can only set DepartmentId and that's it. EF will take care of the rest

Update model in database using entity framework gives conflict with foreign key

I know this has been asked millions of times and I've had it myself hundreds of times, but for some reason I can't fix this one.
I get the well known error:
The UPDATE statement conflicted with the FOREIGN KEY constraint ...
All my tables in my database are cascaded when an insert or delete is done.
Now on to the error:
I want to update an admins table (administrator accounts) that is linked to a cultures table (for languages).
Everything is filled in correctly. and thus we get to the following code:
[HttpPost]
public ActionResult Edit(Admins admins)
{
if (!ModelState.IsValid)
{
return View(admins);
}
admins.cultures_id = admins.Cultures.id;
_unitOfWork.AdminsRepository.Update(admins);
_unitOfWork.Save();
return RedirectToAction("Index", "Overview", new { area = "Admin" });
}
I first set the cultures id of my admin object/entity equal to that of the id in the cultures table that is linked:
admins.cultures_id = admins.Cultures.id;
I then fill update the table:
_unitOfWork.AdminsRepository.Update(admins);
The method update holds this code:
public virtual void Update(TEntity entityToUpdate)
{
DbSet.Attach(entityToUpdate);
ArtWebShopEntity.Entry(entityToUpdate).State = EntityState.Modified;
}
So far so good, but then, when I actually want to save the admin:
_unitOfWork.Save();
That save method holds this code:
public void Save() {
try
{
_artWebshopEntity.SaveChanges();
}
catch (DbEntityValidationException dbEx)
{
foreach (var validationErrors in dbEx.EntityValidationErrors)
{
Console.WriteLine("Entity of type \"{0}\" in state \"{1}\" has the following validation errors:", validationErrors.Entry.Entity.GetType().Name, validationErrors.Entry.State);
foreach (var validationError in validationErrors.ValidationErrors)
{
Console.WriteLine("- Property: \"{0}\", Error: \"{1}\"", validationError.PropertyName, validationError.ErrorMessage);
}
}
throw; // Will do something here later on...
}
}
And at the SaveCHanges method I get the error. I know what it means but I can't seem to fix it. I've tried all the things I know that could cause it.
Edit
I only want to update the admin values, so I don't want to update the culture values.
This is the query:
update [dbo].[Admins]
set [login] = 'Herve' /* #0 */,
[password] = null,
[salt] = null,
[email] = 'xxxxx.xxx#glevin.be' /* #1 */,
[permissions] = 'administrator' /* #2 */,
[attempts] = 4 /* #3 */,
[locked] = 0 /* #4 */,
[cultures_id] = 0 /* #5 */
where ([id] = 1 /* #6 */)
So, the cultures_id is the issue. I've now did the following:
var updateAdmin = new Admins
{
attempts = admins.attempts,
cultures_id = admins.cultures_id,
email = admins.email,
locked = admins.locked,
login = admins.login,
id = admins.id,
password = admins.password,
permissions = admins.permissions,
salt = admins.salt,
};
And that works, but the moment I add the Cultures object to the mix, it crashes and gives me the reference error. So it boils down to, how the frack do I update a table with a foreign key to another table to also needs to be updated?
Edit II
My admin and cultures entity (database first), also image of database in sql management studio:
Admin class:
public partial class Admins
{
public int id { get; set; }
public string login { get; set; }
public string password { get; set; }
public string salt { get; set; }
public string email { get; set; }
public string permissions { get; set; }
public Nullable<int> attempts { get; set; }
public Nullable<bool> locked { get; set; }
public Nullable<int> cultures_id { get; set; }
public virtual Cultures Cultures { get; set; }
}
Cultures class:
public partial class Cultures
{
public Cultures()
{
this.Categories_local = new HashSet<Categories_local>();
this.Menu_items_local = new HashSet<Menu_items_local>();
this.Products_local = new HashSet<Products_local>();
this.Subcategories_local = new HashSet<Subcategories_local>();
this.Webpages_local = new HashSet<Webpages_local>();
this.Admins = new HashSet<Admins>();
}
public int id { get; set; }
public string name { get; set; }
public string display_name { get; set; }
public virtual ICollection<Categories_local> Categories_local { get; set; }
public virtual ICollection<Menu_items_local> Menu_items_local { get; set; }
public virtual ICollection<Products_local> Products_local { get; set; }
public virtual ICollection<Subcategories_local> Subcategories_local { get; set; }
public virtual ICollection<Webpages_local> Webpages_local { get; set; }
public virtual ICollection<Admins> Admins { get; set; }
}
I've gotten it to work!
The problem was that in the edit page the final field was the field that showed the name of the culture that corresponded with the id of the admin.
In other words I did the following:
#Html.EditorFor(model => model.Cultures.name)
But this wasn't the correct way.
In order to show the name of the culture but in the code pass the culture id, I used a #Html.DropDownListFor()-element.
The problem with this however was that my original model, Admins, didn't have a IEnumerable object that I could pass to the dropdownlist element in my view. I had to create a new model which I named CreateAdminModel, The new model looks like this:
public class CreateAdminModel
{
public CreateAdminModel() { }
public CreateAdminModel(IEnumerable<SelectListItem> cultures) { Cultures = cultures; }
public CreateAdminModel(Admins admin) { Admin = admin; }
public CreateAdminModel(IEnumerable<SelectListItem> cultures, Admins admin)
{
Cultures = cultures;
Admin = admin;
}
public IEnumerable<SelectListItem> Cultures { get; set; }
public Admins Admin { get; internal set; }
}
It has an Admin object created by the entity framework (database first).
With that new model I created the following method:
private CreateAdminModel CreateAdminWithcultureDetails(Admins admin = null)
{
var cultureItems = (_unitOfWork.CulturesRepository.Get()).ToArray();
var cultureList = new List<SelectListItem>();
for (int i = 0; i < cultureItems.Count(); i++) cultureList.Add(new SelectListItem { Text = cultureItems[i].name, Value = cultureItems[i].id.ToString() });
return admin != null ? new CreateAdminModel(cultureList, admin) : new CreateAdminModel(cultureList);
}
This fills the dropdown list with the cultures and depending on whether or not an admin object was passed also adds an admin object.
Now I can use this model in the view and correctly fill both the dropdown list and the admin if necessary.
I'm going to do the same for the other things that have to use CRUD.

Resources