I want to get and Save ASP NET MVC Membership provider UserProfile table UserId field into a separate table?
Here is my model
public class Product
{
public int ProductId { get; set; }
public int UserId { get; set; }
public string ProductName { get; set; }
}
I don't want to add UserId field into my Model anymore.
and here is my controller:
private UsersContext db = new UsersContext();
// GET: /ProCont/
int UserID = (int)Membership.GetUser().ProviderUserKey;
public ActionResult Index()
{
return View(db.Products.Where(p => p.UserId == UserID).ToList());
}
What to do?
Regards
Dostdar
Disclaimer: It will be too long as a edit in already existing answer and hence posted in separate answer.
To answer your question on how will you add the UserID field in your model object below is a detailed description with code segment.
Considering that you have already added Products entity using ADO.NET Entity Data Model. Add a separate class file to your Models folder with any name you like and have the below code. Below code segment decorate your model object.
using System.ComponentModel.DataAnnotations;
using System.ComponentModel;
namespace MvcApplication1.Models
{
[MetadataType(typeof(productMetadata))]
public partial class Product
{
}
public class productMetadata
{
[HiddenInput(DisplayValue = false)]
public int ProductId { get; set; }
// to make the field hidden and will not display in browser
[HiddenInput(DisplayValue = false)]
//directing that don't scaffold this column since it will be added manually
[ScaffoldColumn(false)]
public int UserId { get; set; }
}
}
You can directly modify the auto-generated Product class in EF but if you re-generate the EF model again then the changes will be gone. So, above shown approach is best using MetadataType attribute.
Controller Action Method(s):
namespace MvcApplication1.Controllers
{
public class HomeController : Controller
{
//
// GET: /Home/
public ActionResult Index()
{
return View();
}
public ActionResult Create()
{
return View();
}
[HttpPost]
public ActionResult Create([Bind(Include = "ProductName, ProductId")]Product product)
{
SampleContext db = new SampleContext();
if (ModelState.IsValid)
{
product.UserId = (int)Membership.GetUser().ProviderUserKey;
db.Products.Add(product);
db.SaveChanges();
return RedirectToAction("Index");
}
return View(product);
}
public ActionResult List()
{
SampleContext db = new SampleContext();
IEnumerable<Product> products = db.Products.ToList();
return View(products);
}
}
}
View (Specifically the Create view):
#model MvcApplication1.Models.Product
#{
ViewBag.Title = "Create";
}
<h2>Create</h2>
#using (Html.BeginForm()) {
#Html.ValidationSummary(true)
<fieldset>
<legend>Product</legend>
<div class="editor-label">
#Html.LabelFor(model => model.ProductName)
</div>
<div class="editor-field">
#Html.EditorFor(model => model.ProductName)
#Html.ValidationMessageFor(model => model.ProductName)
</div>
<p>
<input type="submit" value="Create" />
</p>
</fieldset>
}
<div>
#Html.ActionLink("Back to List", "Index")
</div>
Notice that the view has editable controls for only productname as input field since ProductID is an identity column and UserID getting value in code and not from user.
It's really not clear what you are asking here? But are you saying that you want to store the UserID into your Products table in database. Then you can designate your model class so like below
[System.ComponentModel.DataAnotations.Table("Products")]
public class Product
{
public int ProductId { get; set; }
public int UserId { get; set; }
public string ProductName { get; set; }
}
Either you can create the table Products manually or you can use EF code first Migrations to create a snapshot and update the database in order to add the newly created table.
Then you can add data to your table like
Product p1 = new Product
{
UserID = (int)Membership.GetUser().ProviderUserKey,
ProductName = "XXX";
};
db.Products.Add(p1);
db.SaveChanges();
Per your comment:
What you have currently is the right way to do it else there is no way you can relate a product(s) with a specific user. This is what called as Referential Integrity Constraint. Here UserID in your model class is acting as FOREIGN KEY. You just can't do it automatically.
Related
Hi everyone so I am trying to create an application using asp.net mvc with a code first database that allows the users to be able to create a blog post with as many images as they wish.I am currently trying to have the image path in one table and the heading,body text in the other table along with a foreign key to the image path.So that I can create one post with multiple images. This is my first time using multiple tables and currently I am getting an error when it reaches this line context.SaveChanges(); in the save method when I am trying to create a post and save it to the db. Thank you for any help with this issue.
An exception of type 'System.Data.Entity.Infrastructure.DbUpdateException' occurred in EntityFramework.dll but was not handled in user code
Additional information: An error occurred while updating the entries. See the inner exception for details
I was able to get the program to work when I was using one table but it had this issue : https://imgur.com/a/lQQ3Q
Here is the database Diagram :http://imgur.com/a/iJZGx
Query that I tried to make but am not sure where to use in my code.
var query = db.PostModel.Where(x => x.PostID == PostId).Select(x => new
{
PostID = x.PostID,
ImageId = x.ImageModel.ImageId,
ImagePath = x.ImageModel.ImagePath,
Heading = x.PostModel.Heading,
PostBody = x.PostModel.PostBody
}).FirstOrDefault();
My program
View to Create posts
#model Crud.Models.PostModel
....
#using (Html.BeginForm("Create", "Home", null, FormMethod.Post, new { enctype = "multipart/form-data" }))
{
#Html.AntiForgeryToken()
#Html.ValidationSummary(true)
<form action="" method="post" enctype="multipart/form-data">
#Html.LabelFor(model => model.ImageModel.ImagePath)
<input id="ImagePath" title="Upload a product image" multiple="multiple" type="file" name="files" />
#Html.LabelFor(model => model.Heading)
<input id="Heading" title="Heading" name="Heading" />
#Html.LabelFor(model => model.PostBody)
<input id="PostBody" title="PostBody" name="PostBody" />
<p><input type="submit" value="Create" /></p>
</form>
}
View to display posts
#model IEnumerable<Crud.Models.PostModel>
....
#foreach (var item in Model)
{
<div>#Html.DisplayFor(modelItem => item.Heading)</div>
<div>#Html.DisplayFor(modelItem => item.PostBody)</div>
<div><img class="img-thumbnail" width="150" height="150" src="/Img/#item.ImageModel.ImagePath" /></div>
}
Models
public partial class PostModel
{
[Key]
[HiddenInput(DisplayValue = false)]
public int PostID { get; set; }
public string Heading { get; set; }
public string PostBody { get; set; }
[ForeignKey("ImageModel")]
public int ImageId { get; set; }
public virtual ImageModel ImageModel { get; set; }
}
public class ImageModel
{
[Key]
public int ImageId { get; set; }
public string ImagePath { get; set; }
public string PostID { get; set; }
}
DBcontext
public class EFDbContext : DbContext
{
public DbSet<SchoolNewsModel> SchoolNews { get; set; }
public DbSet<PostModel> Posts { get; set; }
public DbSet<ImageModel> Images { get; set; }
}
Controller
public ViewResult Display()
{
return View(repository.Posts);
}
public ViewResult Create()
{
return View("Create", new PostModel());
}
[HttpPost]
public ActionResult Create(PostModel Image, IEnumerable<HttpPostedFileBase> files)
{
if (ModelState.IsValid)
{
foreach (var file in files)
{
PostModel post = new PostModel();
if (file.ContentLength > 0)
{
file.SaveAs(HttpContext.Server.MapPath("~/Img/") + file.FileName);
// post.ImagePath = file.FileName;
post.PostBody = post.PostBody;
post.Heading = post.Heading;
}
repository.Save(post);
}
}
return RedirectToAction("display");
}
public ViewResult PublicPostDisplay()
{
return View(repository.Posts);
}
Repository
public IEnumerable<PostModel> Posts
{
get { return context.Posts; }
}
public void Save(PostModel Image)
{
if (Image.PostID == 0)
{
context.Posts.Add(Image);
}
else
{
PostModel dbEntry = context.Posts.Find(Image.PostID);
if (dbEntry != null)
{
dbEntry.ImageModel.ImagePath = Image.ImageModel.ImagePath;
}
}
context.SaveChanges();
}
You need to include the full details of the error. Its the See the inner exception for details that will give you the relevant information. However that will probably not matter since your models and relationships are incorrect.
You want a PostModel to have multiple ImageModel so you need a one-many relationship and your PostModel needs have the following property
public virtual ICollection<ImageModel> Images { get; set; }
and delete the int ImageId and ImageModel ImageModel properties. In addition the ImageModel should contain public virtual PostModel Post { get; set; }
Your POST method to create a new PostModel then becomes
[HttpPost]
public ActionResult Create(PostModel post, IEnumerable<HttpPostedFileBase> files)
{
if (!ModelState.IsValid)
{
return View(post);
}
foreach (var file in files)
{
if (file.ContentLength > 0)
{
file.SaveAs(HttpContext.Server.MapPath("~/Img/") + file.FileName);
// Initialize a new ImageModel, set its properties and add it to the PostModel
ImageModel image = new ImageModel()
{
ImagePath = file.FileName
};
post.Images.Add(image);
}
}
repository.Save(post);
return RedirectToAction("display");
}
There are however multiple other issues with your code that you should address.
First, your view has nested forms which is invalid html and not
supported. You need to remove the inner <form> tag
Your editing data, so always use a view model (refer What is
ViewModel in
MVC?)
and the PostVM will include a property
IEnumerable<HttpPostedFileBase> Images and in the view, bind to it
using #Html.TextBoxFor(m => m.Images, new { type = "file", multiple
= "multiple" })
You have no validation at all, and your properties should include
validation attributes, for example, a [Required] attribute on
Heading and Body. The you need to include
#Html.ValidationMessageFor() for each property in the view.
You manual html for the inputs will not give you 2-way model binding
and prevent any client side validation. Always use the HtmlHelper
methods to generate form controls, e.g. #Html.TextBoxFor(..)
Do not save the image with just the file name (multiple users may
upload files with the same name and overwrite existing files. One
option is to use a Guid for the file name, and include a
additional property string DisplayName in ImageModel. Refer
this
answer
for an example of that approach.
I am new to MVC. I work on an auction application. On the auction site, there should be a form for making a bid. I have a problem passing the auction parameter to the controller
My models:
public class Auctions
{
[Key]
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public int ID { get; set; }
public string title { get; set; }
(..) some other fields
public List<Bid> bids = new List<Bid>();
}
public class BiddingViewModel
{
public Auctions auctionToSend { get; set; }
public double bid { get; set; }
}
My view:
#model BiddingViewModel
#using(Html.BeginForm("CreateBid", "Auction", FormMethod.Post, new { enctype = "multipart/form-data" }))
{
#Html.HiddenFor(model=>model.auctionToSend)
#Html.EditorFor(model => model.bid)
<input type="submit" value="Make it work" />
}
and my controller:
[AllowAnonymous]
public ActionResult AuctionPage(int id)
{
var tmp = _context.Auctions.FirstOrDefault(i => i.ID == id);
BiddingViewModel bvm = new BiddingViewModel
{
auctionToSend = tmp,
bid = -1
};
return View(bvm);
}
[Authorize]
[HttpPost]
public async Task<ActionResult> CreateBid(BiddingViewModel bvm)
{
//After filling the form from view, the bvm.auctionToSend is null, whereas the bvm.bid value is visible
return RedirectToAction("AuctionList", "Auction");
}
My problem is that the auction data (perfectly visible in the view) is not sent back to the controller. I checked the internet and it showed me some naming-conflicts' solutions, so I made sure the naming is different, but this didn't fix my problem.
auctionToSend is a complex object and your use of #Html.HiddenFor(model=>model.auctionToSend) is generating
<input type="hidden" name="auctionToSend" value="yourAssembly.Auctions" ... />
If you just need the ID of the Auctions, then use
#Html.HiddenFor(m => m.auctionToSend.ID)
otherwise you need to generate a hidden input for each property of Auctions but that would be inefficient, particularly as Auctions contains a property which is a collection, so if you need the Auctions object in the POST method, better to just get it again based on the ID value your submitting.
As a side note, you really should be using a view model with just properties for the double Bid and int AuctionID
I'm using ASP.NET Core 1.0 and EF Core 1.0 and have the following code-first class in my SQL database.
namespace GigHub.Models
{
public class Genre
{
public byte Id { get; set; }
[Required]
[StringLength(255)]
public string Name { get; set; }
}
}
I also have the following ViewModel class I use in a Razor view form:
namespace GigHub.ViewModels
{
public class GigFormViewModel
{
public string Venue { get; set; }
public string Date { get; set; }
public string Time { get; set; }
public List<Genre> Genres { get; set; }
}
}
I also have this controller:
using GigHub.Data;
using GigHub.ViewModels;
using Microsoft.AspNetCore.Mvc;
namespace GigHub.Controllers
{
public class GigsController : Controller
{
private readonly ApplicationDbContext _context;
public GigsController(ApplicationDbContext context)
{
_context = context;
}
public IActionResult Create()
{
var vm = new GigFormViewModel();
// Need to get my Genre list from the DbSet<Genre> in my database context injected above
// into the GigFormViewModel for the Select taghelper to consume
return View(vm);
}
}
}
I have my Razor view set up to use the ViewModel fine but I'm unsure how the Select taghelper code below should be set up to access the Genre property.
<div class="form-group">
<label asp-for="????" class="col-md-2 control-label"></label>
<div class="col-md-10">
<select asp-for="????" asp-items="????" class="form-control"></select>
<span asp-validation-for="????" class="text-danger" />
</div>
</div>
I'm basically having trouble grokking how to get my list of genres from my database into the ViewModel property in a form that the Select taghelper asp-items= can consume. The many trial & error contortions I've gone through generally result in conversion issues going from generic List<> type to MVCs SelectListItem type. I suspect my ViewModel Genre class needs adjusting but my research thus far has only resulted in articles covering previous versions of ASP.NET and Entity Framework and I struggle to map them to ASP.NET core 1.0 RC2 and EF Core 1.0.
You can use asp-for to specify the model property name for the select element and asp-items to specify the option elements.
<select asp-for="SomeFiles" asp-items="Model.SomeOptions"></select>
You can also use ViewBag.SomeOptions if you don't want to add the SomeOptions filed to the mode.
For more information take a look at The Select Tag Helper documentation.
Example
View
<select asp-for="Country" asp-items="Model.Countries"></select>
Model
using Microsoft.AspNetCore.Mvc.Rendering;
using System.Collections.Generic;
namespace FormsTagHelper.ViewModels
{
public class CountryViewModel
{
public string Country { get; set; }
public List<SelectListItem> Countries { get; set; }
}
}
Controller
The Index method initializes the CountryViewModel, sets the selected country and list of countries and passes the model to the Index view.
public IActionResult Index()
{
var model = new CountryViewModel();
model.Country = "CA";
model.Countries = db.Countries
.Select(x => new SelectListItem { Value = x.Id, Text = x.Name })
.ToList();
return View(model);
}
I am learning LINQ and was wondering how can I INSERT into two different tables with no relationship on one click using LINQ. Is it even possible?
I am adding into one table like this. How can I add into the second table as well?
Second Table,
GenreId
Name
Description
Code,
[HttpPost]
public ActionResult Create(Artist artist)
{
if (ModelState.IsValid)
{
_db.Artists.Add(artist);
_db.Genres.Add(new Genre { GenreId = 1, Name = "Some Genre", Description = "Trying to add second table" }); // how can i add the genre object here
_db.SaveChanges();
return RedirectToAction("Index", new { id = artist.ArtistId });
}
return View(artist);
}
Note that my View is strongly typed to Artist class.
My View,
#model EntityFrameWorkDBFirst.Models.Artist
#Html.TextBoxFor(model => model.Name)
#Html.TextBoxFor(model =>model.Genres) // how to get this working
My Model Class,
public partial class Artist
{
public Artist()
{
this.Albums = new HashSet<Album>();
this.Genres = new HashSet<Genre>();
}
public int ArtistId { get; set; }
public string Name { get; set; }
public virtual ICollection<Album> Albums { get; set; }
public virtual ICollection<Genre> Genres { get; set; }
}
Tuple related suggestion didn't work. Maybe I am missing something.
#model Tuple<EntityFrameWorkDBFirst.Models.Artist, EntityFrameWorkDBFirst.Models.Genre>
<legend>Create a Artist</legend>
#Html.TextBoxFor(model => model.Item1.Name)
<h2>Genre</h2>
#Html.TextBoxFor(model => model.Item2.Name)
#Html.TextBoxFor(model => model.Item2.Name)
<p>
<input type="submit" value="Create" />
</p>
Controller Code:
[HttpPost]
public ActionResult Create(Artist artist, Genre genre)
{
if (ModelState.IsValid)
{
_db.Artists.Add(artist);
_db.Genres.Add(genre);
_db.SaveChanges();
return RedirectToAction("Index", new { id = artist.ArtistId });
}
return View(artist);
}
You mixed domain model and view model.
It's big mistake, you should work only with viewmodel on view.
You should create view model:
public class CreateArtistViewModel
{
public string ArtistName { get; set; }
public int? Genres { get; set; } // if you provide chooser for user
public string GenresName { get; set; } // if user can enter new genre
public string GenresDecription { get; set; } // if user can enter new genre
public IList<Genre> Genres { get; set; }
}
in the post action you should check view model and create artist and genre if user create new gener.
I am trying to figure out how to insert multiple rows of data using a viewmodel with a single postback. I am using EF and code first approach. The model I an having trouble with has one to many relationship. What I would like to do is that whenever a Person is created, multiple Tasks should be saved to the database along with a Person (different tables).
So far, I am only able to save the very first Task and not all.
My models are:
public class Person
{
public Person()
{
this.Tasks = new List<Task>();
}
[Key]
public int Id { get; set; }
public string Name { get; set; }
public virtual ICollection<Task> Tasks { get; set; }
}
public class Task
{
[key]
public int ID { get; set; }
public int PersonID { get; set; }
public string Task { get; set; }
public virtual Person Person { get; set; }
}
ViewModel:
Public class PersonData
{
public Person Person { get; set; }
public Task Task { get; set; }
}
View:
#model Project.ViewModels.PersonData
#using (Html.BeginForm()) {
#Html.ValidationSummary(true)
<fieldset>
<legend>Event</legend>
<div>
#Html.TextBoxFor(model => model.Event.Name)
</div>
<div id="taskdiv">
</div>
#*dynamically generated textboxes here*#
<div>
#Html.ActionLink("Back to List", "Index")
</div>
</fieldset>
<script type="text/javascript">
// took few codes out here, but whats happening here is based on int value textboxes for Task appears
for (var i = 1; i <= $count; i++) {
$('#taskdiv').append('<div><label>Task #' + i + ' </label>#Html.TextBoxFor(model => model.Task.Task)</div>');
}
Controller:
public class PersonController : Controller
{
private Context db = new DBContext();
public ActionResult Create()
{
return View();
}
[HttpPost]
public ActionResult Create(ViewModels.PersonData personData)
{
if (ModelState.IsValid)
{
db.Person.Add(eventData.Person);
db.Tasks.Add(eventData.Task);
db.SaveChanges();
return RedirectToAction("Index");
}
return View();
}
At least I know that tasks are being passed. I tried few things like
foreach (char c in personData.Task.Task.AsEnumerable)
{ count += 1}
and received the correct amount of number. I thought using ViewModels are relatively common, but I didn't see what I would like to do in any tutorials or forums....
My goal is to be able to save multiple tasks per person with one postback. Any advice will be appreciated!
In your ViewModel you only have one Task, should be a list of tasks right?
When I do stuff like this I usually save the Person object first then add tasks to it using ajax.
The Method could look something like this.
public void SaveTasks(List<Task> tasks){
//important that tasks already have personId --set on client side
foreach(var task in Tasks){
//do some validation
db.Tasks.Add(task);
}
db.SaveChanges();
}