I have an action and 2 SQL tables I know how to enter a record into 1 table but for the second table I want to get the lastly inserted ID from table 1 and insert that into the 2nd table for one of the columns. I have everything setup but my database save doesn’t work without an exception. Also Im not sure how to use scope_identity() or get the last ID and I am using EF for everything.
public ActionResult AddTimeSheet()
{
ViewBag.ProjectsSelect = new SelectList(
context.Projects.ToList(), "ProjectId", "ProjectName");
var uid = User.Identity.GetUserId();
ViewBag.CurrentId = uid;
return View();
}
[HttpPost]
[ValidateAntiForgeryToken]
public ActionResult AddTimeSheet(TimeSheetProjectsModel timeSheetModel)
{
try
{
if(timeSheetModel == null)
{
return new HttpStatusCodeResult(HttpStatusCode.BadRequest);
}
//if (ModelState.IsValid)
//{
TimeSheetMaster masterModel = new TimeSheetMaster();
masterModel.FromDate = timeSheetModel.Proj1;
masterModel.ToDate = timeSheetModel.Proj1.AddDays(7);
/* NEED HRS 4 WEEK/END MONTH/NEW MONTH */
masterModel.TotalHours = timeSheetModel.ProjTotal1;
masterModel.UserId = User.Identity.GetUserId();
masterModel.DateCreated = DateTime.Now;
/* MONTH SUBMITTED */
masterModel.TimeSheetStatus = 1;
masterModel.Comment = timeSheetModel.ProjDesc1;
context.TimeSheetMaster.Add(masterModel);
var detailsModel = CreateTimeSheetDetails(masterModel, timeSheetModel);
context.TimeSheetDetails.Add(detailsModel);
context.SaveChanges();
TempData["SuccessMaster"] = "TimeSheetMaster Created Successfully";
TempData["SuccessDetails"] = "TimeSheetDetails Created Successfully";
return RedirectToAction("TimeSheetList", "TimeSheet");
//}
// TempData["Error"] = "TimeSheet Create Was Unsuccessful";
// return RedirectToAction("TimeSheetList", "TimeSheet");
}
catch (Exception ex)
{
Console.Write(ex.Message);
TempData["Error"] = "TimeSheet Create Was Unsuccessful";
return RedirectToAction("TimeSheetList", "TimeSheet");
}
}
If the two entities are associated with the same DbContext and they are directly related, (PK/FK) then the best solution is to ensure that the entities have a navigation property set up for the relation and let EF manage the keys and linking.
For instance:
TimeSheetMaster masterModel = new TimeSheetMaster();
masterModel.FromDate = timeSheetModel.Proj1;
masterModel.ToDate = timeSheetModel.Proj1.AddDays(7);
masterModel.TotalHours = timeSheetModel.ProjTotal1;
masterModel.UserId = User.Identity.GetUserId();
masterModel.DateCreated = DateTime.Now;
masterModel.TimeSheetStatus = 1;
masterModel.Comment = timeSheetModel.ProjDesc1;
var detailsModel = CreateTimeSheetDetails(masterModel, timeSheetModel);
// either here, or inside CreateTimeSheetDetails...
// if Master has a collection of Details: (1-to-Many)
masterModel.Details.Add(details);
// or if Master has a 1-to-1 relationship to details.
// masterModel.Details = detailsModel;
context.TimeSheetMaster.Add(masterModel);
context.SaveChanges();
Ideally EF should manage your relationships so that your code simply works with the related entity structure and hands off the key/relationship management to Entity Framework.
If you need IDs cross-context, or to return after performing an insert then you can access the entity's new PK directly after calling SaveChanges:
context.TimeSheetMaster.Add(masterModel);
context.SaveChanges();
var newId = masterModel.TimeSheetMasterId; // Populated automatically after SaveChanges.
Related
Normally, with MVC I use db.savechanges() method after I do some processes. But check the below code when I use N-Tier Architecture in everyloop its gonna insert in this way but I dont want it. I have to check all the items first. If there is no problem then I have to insert it all together.
foreach (var item in mOrderList)
{
MOrder mOrder = new MOrder();
mOrder.StatusAdmin = false;
mOrder.Date = DateTime.Now;
mOrder.StatusMVendor = "Sipariş alındı.";
mOrder.HowMany = item.HowMany;
mOrder.MBasketId = item.MBasketId;
mOrder.MProductId = item.MProductId;
mOrder.MVendorId = item.MVendorId;
mOrder.WInvestorId = item.WInvestorId;
MProduct mprostock = _imProductService.GetMProductById(item.MProductId);
if (mprostock.Stock<=0)
{
return ReturnErrorAndSuccess(HttpStatusCode.NotFound, "MProduct", mprostock.Name + " ürününde stok kalmadığı için işlem tamamlanamadı.");
}
_imOrderService.InsertMOrder(mOrder);
}
all you have to do is:
first you should define a method that get list of mProductId and then return list of MProduct.
after that you should check if there is any record with Stock<=0 then return your error.
-also for your insert you should define a method that get list of MOrder and return appropriate datatype for example Boolean.
public List<MProduct> GetMProductByIds(List<MProductId> mProductId)
{
//getting record code
}
public bool AddMOrder(List<MOrder> mOrder)
{
//inserting record code
}
i have two tables orders and order detail and i have order foriegn key in order detail. and i am inserting ordertotal into order table and all other order info in order details but i am getting this error .please help me out and please tell me hw to use entity state to add values to foriegn key table.
and here is my saveorder controller function
[HttpPost]
public ActionResult SaveOrder(FormCollection fc)
{
ResturantContext context = new ResturantContext();
ShoppingCart myCart = (ShoppingCart)Session[WebUtil.CART];
User u = (User)Session[WebUtil.USER];
Order order = new Order();
order.TotalAmount = myCart.TotalAmount;
new OrderHandler().AddOrder(order);
foreach (var m in myCart.Items)
{
OrderDetails od = new OrderDetails();
od.ID = m.Id;
od.Price =m.Price;
od.Product_name = m.Name;
od.Quantity = m.Quantity;
od.Address = fc["Address"];
od.City = fc["City"];
od.DateOfOrder = DateTime.Now;
od.UserName = u.FullName;
od.Email = u.Email;
od.ContactNo = fc["ContactNo"];
od.Country = fc["Country"];
new OrderHandler().AddOrderDetails(od);
}
Session.Remove(WebUtil.CART);
return RedirectToAction("Index","Home");
}
od.Order will always be Null since you are not setting it anywhere in your code. Try at least set it like od.Order = order inside your loop before calling your OrderHandler().AddOrderDetails(od);
I've visited every search result I could find but I'm stuck.
I'm working on a "dvd store" website using ASP.Net MVC 5, and I'm having some difficulty with inserting MultiSelectList values into the database. The database structure has a many-to-many table which stores the movie Id, genre Id, and a primary key. There's also a movie table with fields like movie title, cost, image path, director, rating, etc.
My insert logic works for putting data into the movies table, but I have a multiselectlist in my Create view which is populated from a list of movie genres in the database. When I select one list item, the ID inserts into the moviegenre table just fine. When I select more than one, only one ID is inserted. I'd like to insert a new row for each selection, with the movie ID and the genre ID (i.e. if 3 genres are selected, create 3 new rows with the same movie id but different genre id's for each row).
How can I iterate through the posted MultiSelectList data and insert a new row for each value?
This is the code in my View:
#Html.ListBoxFor(r => r.CMovie.GenreId, new MultiSelectList(Model.CGenreList, "Id", "Description"), new { #class = "form-control" })
My Controller:
[HttpPost]
public ActionResult Create(MovieGenresDirectorsRatings mgdr) // The ViewModel
{
try
{
mgdr.CMovie.Insert();
return RedirectToAction("Index");
}
catch (Exception ex)
{
throw ex;
return View(mgdr);
}
}
The ViewModel:
public class MovieGenresDirectorsRatings
{
public IEnumerable<int> GenreId { get; set; }
public CGenreList CGenreList { get; set; }
public CDirectorList CDirectorList{ get; set; }
public CFormatList CFormatList { get; set; }
public CRatingList CRatingList { get; set; }
public CGenre CGenre { get; set; }
public CMovie CMovie { get; set; }
}
And my Insert logic in the Model:
public void Insert()
{
using (myEntities dc = new myEntities())
{
try
{
tblMovie movie = new tblMovie();
// Add movie to tblMovie
movie.Id = 1;
if (dc.tblMovies.Any())
movie.Id = dc.tblMovies.Max(p => p.Id) + 1;
this.Id = movie.Id;
movie.Title = this.Title;
movie.Description = this.Description;
movie.ImagePath = this.ImagePath;
movie.Cost = this.Cost;
movie.RatingId = this.RatingId;
movie.FormatId = this.FormatId;
movie.DirectorId = this.DirectorId;
try
{
tblMovieGenre genre = new tblMovieGenre();
genre.Id = 1;
if (dc.tblMovieGenres.Any())
genre.Id = dc.tblMovieGenres.Max(p => p.Id) + 1;
// THIS IS THE PART that I'm struggling with.
// I think the data is there, I'm just not sure how to access it
foreach (var GenreId in GenreId) // This line is probably wrong, but I'm not sure how to access the data
{
genre.GenreId = this.GenreId.FirstOrDefault();
genre.MovieId = movie.Id;
dc.tblMovieGenres.Add(genre);
}
}
catch (Exception ex)
{
throw ex;
}
dc.tblMovies.Add(movie);
// Commit changes
dc.SaveChanges();
}
catch (Exception ex)
{
throw ex;
}
}
}
I've tried foreach loops and for loops and I can't get it to work. What am I doing wrong?
Edit #1: After making a few changes, here is my (current and non-working) complete Insert logic in the CMovie class. When I only choose one "genre" from the MultiSelectList, it works fine and inserts into both tables properly. However, when I select two or more "genres" from the MultiSelectList, I get a "Value cannot be null, parameter name: items" error.
public void Insert()
{
using (dbEntities2 oDc = new dbEntities2())
{
try
{
tblMovie movie = new tblMovie();
// Add movie to tblMovie
movie.Id = 1;
if (oDc.tblMovies.Any()) // If table is not empty
movie.Id = oDc.tblMovies.Max(p => p.Id) + 1;
this.Id = movie.Id;
movie.Title = this.Title;
movie.Description = this.Description;
movie.ImagePath = this.ImagePath;
movie.Cost = this.Cost;
movie.RatingId = this.RatingId;
movie.FormatId = this.FormatId;
movie.DirectorId = this.DirectorId;
try
{
foreach (var GenreId in GenreIds)
{
tblMovieGenre genre = new tblMovieGenre();
genre.Id = 1;
if (oDc.tblMovieGenres.Any())
{
genre.Id = oDc.tblMovieGenres.Max(p => p.Id) + 1; // genre.Id is set to the highest id in the table, +1
}
genre.Id = this.Id;
genre.GenreId = GenreId;
genre.MovieId = movie.Id;
oDc.tblMovieGenres.Add(genre);
}
}
catch (Exception ex)
{
throw ex;
}
oDc.tblMovies.Add(movie);
// Commit changes
oDc.SaveChanges();
}
catch (Exception ex)
{
throw ex;
}
}
}
}`
Edit 2: I've found a solution to the problem. Hopefully this helps someone else having the same issue. I changed the create to use a dropdownlist instead of a multiselectlist, and modified the edit method to allow for updating of multiple genres.
Inside the CMovie Model, I created two new methods, AddGenre and DeleteGenre. In the controller, I added four new IEnumerable<int> variables: oldGenreIds, newGenreIds, adds, and deletes.
I then made a list from the IEnumerable deletes and adds:
IEnumerable<int> deletes = oldGenreIds.Except(newGenreIds);
IEnumerable<int> adds = newGenreIds.Except(oldGenreIds);
deletes.ToList().Foreach(a => mgdr.CMovie.DeleteGenre(id, a));
adds.ToList().Foreach(a => mgdr.CMovie.AddGenre(id, a));
The update method is then called, which sets the changed values (including the movie title, description, image path, etc):
mgdr.CMovie.Update();
By moving the ForEach logic into the controller, I was able to call the AddGenre method several times - which I wasn't able to do when calling it directly inside the Insert method.
Your post method should accept array rather than single object.
[HttpPost]
public ActionResult Create(MovieGenresDirectorsRatings[] mgdr) // The ViewModel
{
foreach(var genr in mgdr){
try
{
genr.CMovie.Insert(); //inserting each object received from view.
return RedirectToAction("Index");
}
catch (Exception ex)
{
throw ex;
return View(mgdr);
}
}
}
The idea is receive all objects from view. Even if your view is posting all the items from multiselect list, you need array like structure in your controller to get data from view. Once you have that data in controller, you loop through all of them inserting one by one.
Here is the problem (an overlook actually):
tblMovieGenre genre = new tblMovieGenre();
// code...
foreach (var GenreId in GenreId)
{
genre.GenreId = this.GenreId.FirstOrDefault();
// code
dc.tblMovieGenres.Add(genre);
}
So see in the above code you create one tblMovieGenre and then in your loop you keep adding the same instance of tblMovieGenres over and over again. So essentially you add a single tblMovieGenres with values from the last iteration in your loop.
Fix
To fix the issue move the instantiation inside the loop:
foreach (var GenreId in GenreId)
{
tblMovieGenre genre = new tblMovieGenre();
// code...
dc.tblMovieGenres.Add(genre);
}
Other Suggestions
1
Hungarian notation is not encouraged in .NET so prefixing your database tables with tbl is not only a notation problem but makes your code harder to read, especially when you use an ORM. Therefore, if you remove the tbl from table names, your code will be:
MovieGenere instead of tblMovieGenre.
2
Also, if I look at one line of code and I can figure out the type of the object, I always use var instead. Like this:
tblMovieGenre genre = new tblMovieGenre();
var genre = new tblMovieGenre();
That is a personal preference (less typing).
But if I cannot figure the type from reading the single line, then I do not use var:
tblMovieGenre genre = GetMovie();
3
If you make your table primary key columns identity columns starting at 1, then you will not need such code:
movie.Id = 1;
if (dc.tblMovies.Any())
movie.Id = dc.tblMovies.Max(p => p.Id) + 1;
Whenever you create a new object in code, it will have an ID of 0 and when you add it to the db, EF will treat it like a new record and generate a new identity for it. This takes the responsibility of managing IDs away from you, which means less coding.
How I can do just this ( a.myFavorits.Add()) without pulling the all object to var a , because a has a lot of data, and I don't want to pull all a object, but I can't find a way do do it.
I want to do the lambada and the linq without return something but linq is always return something
public static void addFavorits(long f,long idUser)
{
using (var db = dataBase())
{
// here i pull object user from users table
var a = db.users.Where(c => c.id == idUser).SingleOrDefault();
// here i adding to the object field myFavorits new value
//myFavorits is also a table of entitys that connected to user object
a.myFavorits.Add(new BE.FavoritsUsersLong { myLong = f });
db.SaveChanges();
}
}
I thought to do something like this but i dont know how to set the field users_TableId that is the key that connect the 2 tables
public static void addFavorits(long favoritId,long idUser)
{
using (var db = dataBase())
{
db.favoritsUsersLong.Add(new BE.FavoritsUsersLong {myLong = favoritId}
/*,users_TableId =idUser*/);
db.SaveChanges();
}
}
Here's a concrete example that does what you want. In this example, only the Name of a Company is modified and saved. Or an item is added to one of its collections.
var cmp = new Company{ CmpId = 1, Name = "Cmp1" }; // CmpId is the primary key
db.Companies.Attach(cmp);
db.Entry(cmp).Property(c => c.Name).IsModified = true;
// Or add an entity to a collection:
cmp.Users = new[] {new User { Name = "a1", PassWord = "a1" } };
try
{
db.Configuration.ValidateOnSaveEnabled = false;
db.SaveChanges();
}
finally
{
db.Configuration.ValidateOnSaveEnabled = true;
}
Result in SQL:
DECLARE #0 VarChar(30) = 'Cmp1'
DECLARE #1 Int = 1
UPDATE [dbo].[Company]
SET [Name] = #0
WHERE ([CmpId] = #1)
There are a few things to note here:
Obviously you need to know the Id of the entity you want to modify.
The object you create is called a stub entity, which is an incomplete entity. When you try to save such an entity, EF is very likely to complain about null values in required properties. That's why almost certain you'd have to disable validation (temporarily, or, better, dispose the context immediately).
If you want to add an item to a collection, you should leave validation enabled, because you'd want to know for sure that the new entity is valid. So you shouldn't mix these two ways to use a stub entity.
If you often need roughly the same small part of your entity you may consider table splitting.
I'm guessing this is what you want? I don't see you 'editting' I only see you adding.
using (var db = dataBase())
{
var a = new user();
....
//set properties etc..
...
a.myFavorits.Add(new BE.FavoritsUsersLong { myLong = f });
db.users.Add(a);
db.SaveChanges();
}
Hey. Nooby question but new with Entity.
I am trying to create a new user object and some details in some additional tables which have a foreign key of the newly created user id.
I've attempted to do this in one round trip. Do I have to add the user to the database first and then go back, set the userid's on the other objects and add them. Here is the Entity Model and code to elaborate:
using (var context = new ServicesEntities())
{
newUser = new UsersTable();
newUser.Address = user.UserDetails.Address;
newUser.City = user.UserDetails.City;
newUser.Country = user.UserDetails.Country;
newUser.Email = user.Email.EmailString;
newUser.FirstName = user.UserDetails.FirstName;
newUser.LastName = user.UserDetails.LastName;
newUser.State = user.UserDetails.State;
newUser.Zip = user.UserDetails.Zip;
context.UsersTables.AddObject(newUser);
context.SaveChanges();
}
using (var context = new ServicesEntities())
{
var referralDetails = new UserReferrals();
referralDetails.CreatedThruServiceId = 1; // todo don't make this an absolute 1
referralDetails.ReferralEmail = user.ReferralDetails.ReferralEmail;
referralDetails.TwoPlusTwoHandle = user.ReferralDetails.TwoPlusTwoHandle;
referralDetails.UserId = newUser.UserId;
context.UserReferrals.AddObject(referralDetails);
context.SaveChanges(); // THIS WORKS FINE!
}
using (var context = new ServicesEntities())
{
var credentials = new UserCredentials();
credentials.CreatedOn = DateTime.Now;
credentials.EmailValidated = false;
//credentials.EmailValidatedOn = null;
credentials.FailedLoginAttempts = 0;
credentials.IsLockedOut = false;
//credentials.LastValidLogin = null;
credentials.Password = user.Password.PasswordString;
credentials.PermissionId = 1; // todo don't make this an absolute 1 = user
credentials.SuccessfulLoginAttempts = 0;
credentials.UserId = newUser.UserId; ;
context.UserCredentials.AddObject(credentials);
context.SaveChanges(); // THIS ONE CRAPS OUT!
}
When I run this I get the following Exception when I run SaveChanges():
{"A dependent property in a
ReferentialConstraint is mapped to a
store-generated column. Column:
'UserId'."}
Note: Updated this with some slightly different code based on an example in a book.
Note2: I've narrowed down the problem to be in the adding of credentials.
Note3: Fixed this, I accidentally had AUTO-INCREMENT set on my UserCredentials userid. If anyone ares here is working code:
public POCO.User AddNewUserToDb(User user)
{
if (IsDuplicateUser(user.Email.EmailString))
{
throw new DuplicateNameException("This email is already taken.");
}
UsersTable newUser;
using (var context = new ServicesEntities())
{
newUser = new UsersTable();
newUser.Address = user.UserDetails.Address;
newUser.City = user.UserDetails.City;
newUser.Country = user.UserDetails.Country;
newUser.Email = user.Email.EmailString;
newUser.FirstName = user.UserDetails.FirstName;
newUser.LastName = user.UserDetails.LastName;
newUser.State = user.UserDetails.State;
newUser.Zip = user.UserDetails.Zip;
var referralDetails = new UserReferrals();
referralDetails.CreatedThruServiceId = 1; // todo don't make this an absolute 1
referralDetails.ReferralEmail = user.ReferralDetails.ReferralEmail;
referralDetails.TwoPlusTwoHandle = user.ReferralDetails.TwoPlusTwoHandle;
//referralDetails.UserId = newUser.UserId;
var credentials = new UserCredentials();
credentials.CreatedOn = DateTime.Now;
credentials.EmailValidated = false;
//credentials.EmailValidatedOn = null;
credentials.FailedLoginAttempts = 0;
credentials.IsLockedOut = false;
//credentials.LastValidLogin = null;
credentials.Password = user.Password.PasswordString;
credentials.PermissionId = 1; // todo don't make this an absolute 1 = user
credentials.SuccessfulLoginAttempts = 0;
//credentials.UserId = newUser.UserId; ;
newUser.Credentials = credentials;
newUser.ReferralDetails = referralDetails;
context.UsersTables.AddObject(newUser);
context.SaveChanges();
}
user.UserId = newUser.UserId;
return user;
Try to add related records directly to UserTable record:
newUser.Credentials.Add(credentials);
newUser.ReferralDetails.Add(referralDetails);
Do not set any Id. It will be set during saving automatically.
Edit: Btw. make sure that UserId column in UserCredentials table is not set as auto generated in database.
Take a look at these links:
Using the entity framework to add existing entities to a collection on a newly created entity.
How to create foreign key relationships with the Entity Framework?
Entity Framework - insert new object with a collection of existing objects
The key links for EF4:
Fortunately, in EF4 we can directly
update the relationship due to the
Foreign Key Association which allows
us to keep the foreign key property
inside the entity classes. For
detail, please see
http://blogs.msdn.com/adonet/archive/2009/11/06/foreign-key-relationships-in-the-entity-framework.aspx.
Besides, we have another great feature
Self Tracing Entities, which makes the
n-tier pattern much easier in EF,
http://blogs.msdn.com/adonet/archive/2009/11/15/updated-feature-ctp-walkthrough-self-tracking-entities-for-the-entity-framework.aspx.
EF4 will allow you to include the foreign key values as scalar properties of your entities. Make sure the "Include foreign keys" checkbox is checked when you create your EDM.
Looking at your model, it looks like you've already done that. Just set the foreign key values explicitly, no need to do a round trip.