I'm having an issue with a problem that was supposed to be corrected in EFCore 2.1. I know this was not capable in 2.0. I have my project as a MVC Core 2.1 app with EFCore being at 2.1 also.
I have searched the web for several different ways of wording the problem and I get pieces of information that do not complete the entire code process required to make the code work correctly.
I have a model Mother and a mother has a sub model of Mailing Address and Physical Address. I am using a stored procedure with FromSql in order to give me the capability of having the multiple joins in my procedure. The stored procedure is almost impossible to write in Linq.
I will include everything I have coded that meets the examples I have already read. PLEASE NOTE: I am using Areas for my code because I am writing a system with multiple areas of functionality.
Models - Mother.cs
namespace Birth.Models
{
public class Mother
{
[Key]
public int MomId { get; set; }
public string MomFirst { get; set; }
public string MomLast { get; set; }
//[ForeignKey("MomId")]
public MotherAddress Physical { get; set; }
// public MotherAddress Mailing { get; set; }
}
//[Owned]
public class MotherAddress
{
public string pType { get; set; }
public string Street { get; set; }
public string PreDir { get; set; }
[Key]
public int MomId { get; set; }
public Mother Mother { get; set; }
}
}
Interface - IbirthRepository.cs
public interface IBirthRepository
{
IQueryable<Mother> Mothers { get; }
IQueryable<MotherAddress> MotherAddresses { get; }
}
Repository - BirthRepository.cs
public class BirthRepository : IBirthRepository
{
private BirthDbContext context;
public BirthRepository(BirthDbContext ctx)
{
context = ctx;
}
public IQueryable<Mother> Mothers => context.Mothers;
public IQueryable<MotherAddress> MotherAddresses => context.MotherAddresses;
}
Controller - GetMotherController.cs
namespace VSMaintenance.Areas.Birth.Controllers
{
[Area("Birth")]
[Route("Birth/GetMother")]
public class GetMotherController : Controller
{
private BirthDbContext context;
private IBirthRepository repository;
public GetMotherController(BirthDbContext ctx, IBirthRepository repo)
{
repository = repo;
context = ctx;
}
public IActionResult Load()
{
return View("Index");
}
[HttpPost]
public async Task<ActionResult> Retrieve(Mother mother)
{
var gbd = new GetBirthData(repository, context);
var mom = new Mother();
mom = await gbd.GetMotherData(mother.MomId);
return View("Mother", mom);
}
}
}
Main View - Mother.cshtml
#model Birth.Models.Mother
#{
ViewData["Title"] = "Mother";
}
<h2>Mother</h2>
#Html.DisplayNameFor(model => model.MomId)
#Html.DisplayFor(model => model.MomId)
<br />
#Html.DisplayNameFor(model => model.MomFirst)
#Html.DisplayFor(model => model.MomFirst)
<br />
#Html.DisplayNameFor(model => model.MomLast)
#Html.DisplayFor(model => model.MomLast)
<br /><br /><br />
#Html.RenderPartialAsync("Birth/_MotherAddress", Model.Physical)
#*#Html.RenderPartialAsync("Birth/_MotherAddress", Model.Mailing)*#
Partial View - MotherAddress.cshtml
#model Birth.Models.MotherAddress
<div class="container">
<div class="row">
<div class="col-sm-3">
#Html.DisplayNameFor(model => model.pType)
</div>
<div class="col-sm-3">
#Html.DisplayNameFor(model => model.Street)
</div>
<div class="col-sm-4">
#Html.DisplayNameFor(model => model.PreDir)
</div>
<div class="col-sm-2"> </div>
</div>
<div class="row">
<div class="col-sm-3">
#Html.TextBoxFor(model => model.pType, new { Style = "width:100%" })
</div>
<div class="col-sm-3">
#Html.TextBoxFor(model => model.Street, new { Style = "width:100%" })
</div>
<div class="col-sm-4">
#Html.TextBoxFor(model => model.PreDir, new { Style = "width:80%" })
</div>
<div class="col-sm-2"> </div>
</div>
</div>
Models.Data - GetBirthData.cs
namespace Birth.Models.Data
{
public class GetBirthData
{
private IBirthRepository repo;
pivate BirthDbContext _ctx;
public GetBirthData(IBirthRepository repository, BirthDbContext context)
{
repo = repository;
_ctx = context;
}
public async Task<Mother> GetMotherData(int MomId)
{
Mother list = new Mother();
try
{
string query = "exec MAINTGetMotherAddresses {0}";
list = await repo.Mothers
.FromSql(query, MomId)
.AsNoTracking()
.FirstOrDefaultAsync();
}
catch (Exception ex)
{
var msg = ex.Message;
}
return list;
}
}
}
DbContext - BirthDbContext.cs
namespace Birth.Models.Data
{
public class BirthDbContext : DbContext
{
public BirthDbContext(DbContextOptions<BirthDbContext> options)
:base(options) {
}
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.Entity<Mother>(b =>
{
b.HasKey(e => e.MomId);
b.Property(e => e.MomId).ValueGeneratedNever();
//b.OwnsOne<MotherAddress>(e => e.Physical);
b.HasOne<MotherAddress>(e => e.Physical)
.WithOne(e => e.Mother)
.HasForeignKey<MotherAddress>(e => e.MomId);
b.ToTable("Table1");
});
}
public DbSet<Mother> Mothers { get; set; }
public DbSet<MotherAddress> MotherAddresses { get; set; }
}
}
SQLServer Stored Procedure - MAINTGetMotherAddresses
ALTER PROCEDURE MAINTGetMotherAddresses
#MotherId NUMERIC(18,0)
AS
BEGIN
SELECT
CAST(md.MOTHER_DETAIL_ID AS INT) AS MomId,
md.FIRST_NAME AS MomFirst,
md.LAST_NAME AS MomLast,
'Physical' AS pType,
maP.STREET_ADDRESS AS Street,--PhysicalStreet,
maP.PRE_DIRECTION AS PreDir --PhysicalPreDir--,
--'Mailing' AS MailingType,
--maM.STREET_ADDRESS AS MailingStreet,
--maM.PRE_DIRECTION AS MailingPreDir
,CAST(#MotherId AS INT) AS PhysicalMomId
--,CAST(#MotherId AS INT) AS MailingMomId
--,CAST(#MotherId AS INT) AS MotherAddressMomId
FROM dbo.MOTHER_DETAIL md
JOIN dbo.MOTHER_ADDRESS maP
ON maP.MOTHER_DETAIL_ID = md.MOTHER_DETAIL_ID
JOIN dbo.MOTHER_ADDRESS maM
ON maM.MOTHER_DETAIL_ID = md.MOTHER_DETAIL_ID
WHERE md.MOTHER_DETAIL_ID = #MotherId
AND maP.ADDRESS_TYPE in (133, 176)
AND maM.ADDRESS_TYPE IN (132, 175)
END
The Mother_Detail and Mother_Address tables have a lot more fields than I have listed here, so I'm not going to show the full table structure. If you create the tables and add an addresses to the Mother_Address table, being Address_Type of 176, then you will be able to see the result set I am trying to work with currently. My desire is to join the address a second time as seen in the stored procedure and return the mailing and physical in one resultset and have the system populate the MotherAddress model appropriately with both addresses.
Please help! This is very frustrating.
Randy
your stored procedure will get all cross joined data for 1 mom, here is correct sql:
SELECT m.Whatever, physical.Whatever, mailing.Whatever
FROM Mother m
CROSS APPLY
(
SELECT TOP 1 a.Name, a.Whatever
FROM Address a
WHERE a.momId= m.Id and a.pType in (1,2,3)
) physical
CROSS APPLY
(
SELECT TOP 1 b.Name, b.Whatever
FROM Address b
WHERE b.momId= m.Id and b.pType in (4,5,6)
) mailing
The reason your SP cannot map result to your models is: your SP returns a view which does not exist in your context. Current EF & Core do not give a good way to execute Query SP, but for inserting,updating,deleting only.
//call extension like this:
_context.QueryStoredProcedureAsync<YourViewModel>("YourProcedureName", ("a", 1), ("b", 2), ("c", 3));
//here is extension
public static class ContextExtensions
{
public static async Task<List<T>> QueryStoredProcedureAsync<T>(
this DbContext context,
string storedProcName,
params (string Key, object Value)[] args)//<==c# 7 new feature
{
using (var command = context.Database.GetDbConnection().CreateCommand())
{
command.CommandText = storedProcName;
command.CommandType = System.Data.CommandType.StoredProcedure;
foreach (var arg in args)
{
var param = command.CreateParameter();
param.ParameterName = arg.Key;
param.Value = arg.Value;
command.Parameters.Add(param);
}
using (var reader = await command.ExecuteReaderAsync())
{
return reader.MapToList<T>();
}
}
}
private static List<T> MapToList<T>(this DbDataReader dr)
{
var result = new List<T>();
var props = typeof(T).GetRuntimeProperties();
var colMapping = dr.GetColumnSchema()
.Where(x => props.Any(y => y.Name.ToLower() == x.ColumnName.ToLower()))
.ToDictionary(key => key.ColumnName.ToLower());
if (dr.HasRows)
{
while (dr.Read())
{
T obj = Activator.CreateInstance<T>();
foreach (var prop in props)
{
var val =
dr.GetValue(colMapping[prop.Name.ToLower()].ColumnOrdinal.Value);
prop.SetValue(obj, val == DBNull.Value ? null : val);
}
result.Add(obj);
}
}
return result;
}
}
you SP is just a query, I provide you 2 options to replace it:
use compile query: https://learn.microsoft.com/en-us/dotnet/framework/data/adonet/ef/language-reference/compiled-queries-linq-to-entities
use my simple sultion to remove SP, SP is not designed for query data, but for process data.
public partial class Mother
{
public Mother()
{
MotherAddresses = new HashSet<MotherAddress>();
}
[Key, Column("ID")]
public int Id { get; set; }
[StringLength(50)]
public string Name { get; set; }
[InverseProperty("Mother")]
public ICollection<MotherAddress> MotherAddresses { get; set; }
}
public partial class MotherAddress
{
[Key, Column(Order = 0)]
public int MotherId { get; set; }
[Key, Column(Order = 1)]
public int AddressType { get; set; }
[StringLength(50)]
public string Address { get; set; }
[ForeignKey("MotherId")]
[InverseProperty("MotherAddress")]
public Mother Mother { get; set; }
}
public enum AddressType
{
Physical,
Mailing,
}
public static class MotherExtension
{
public static MotherAddress MailingAddress(this Mother mom)
{
return mom.Address(AddressType.Mailing);
}
public static MotherAddress PhysicalAddress(this Mother mom)
{
return mom.Address(AddressType.Physical);
}
public static MotherAddress Address(this Mother mom, AddressType addressType)
{
return mom.MotherAddresses.FirstOrDefault(x => x.AddressType == addressType);
}
}
// here is in your DbContext
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.Entity<MotherAddress>(entity =>
{
entity.HasKey(e => new { e.MotherId, e.AddressType });
entity.HasOne(d => d.Mother)
.WithMany(p => p.MotherAddress)
.HasForeignKey(d => d.MotherId)
.OnDelete(DeleteBehavior.ClientSetNull)
.HasConstraintName("FK_MotherAddress_Mother");
});
}
then in your html:
#Html.RenderPartialAsync("Birth/_MotherAddress", Model.PhysicalAddress())
#*#Html.RenderPartialAsync("Birth/_MotherAddress", Model.WhateverAddress())*#
Related
This is where I struggle, for example, you have a setup up 2 x many-to-many relationship with Student in the center.
You have ClassID and HobbyID, how do you list all the students (+ all hobbies and classes in the collection inside student object) that have those using Entity Framework Core, and not have to access the database multiple times or to have to queue everything.
Thanks :)
Edit :
This is an example of what I want to achieve with minimum times accessing the database.
After you chose options on dropdowns (available information is ClassID and HobbyID) and click Filter button you should get students that have those. The students should be displayed with the rest of the classes and hobbies they have.
It is not possible to queue it all from single access to DB, how would you do it otherwise to be as effective as you can be.
Sorry if I was unclear. I am trying as hard as I can to explain this. Thank you for being patient! :)
Example design =>
<span>Pick a Hoby</span>
<select>
<option value="1">Climbing</option>
<option value="2">Running</option>
<option value="3">Painting</option>
</select>
<span style="margin-left:20px">Pick a Class</span>
<select >
<option value="1">Art</option>
<option value="2">Mathematics</option>
<option value="3">Geography</option>
</select>
<button>Filter</button>
<br>
<hr>
List of students :
<p>- <b>Mark</b> has <b>Hobbies : </b> Climbing, Painting / <b>Attending Classes : </b>: <b>Art</b>, Geography</p>
<p>- <b>Jhon</b> has <b>Hobbies : </b> Climbing / <b>Attending Classes : </b> Art</p>
<p>- <b>Steven</b> has <b>Hobbies : </b> Climbing, Running / <b>Attending Classes : </b> Art, Geography, Mathematics</p>
The following assumes EF Core 5, which now automatically creates many-to-many relationships without you having to add an entity class for the join table (as was necessary in previous versions of EF Core).
// Entity classes
public class Student
{
public int StudentId { get; set; }
public string StudentName { get; set; }
public ICollection<Hobby> Hobbies { get; set; }
public ICollection<Class> Classes { get; set; }
}
public class Class
{
public int ClassId { get; set; }
public string ClassName { get; set; }
public ICollection<Student> Students { get; set; }
}
public class Hobby
{
public int HobbyId { get; set; }
public string HobbyName { get; set; }
public ICollection<Student> Students { get; set; }
}
public class AppDbContext : DbContext
{
public AppDbContext(DbContextOptions<AppDbContext> options)
: base(options)
{
}
public DbSet<Student> Students { get; set; }
public DbSet<Class> Classes { get; set; }
public DbSet<Hobby> Hobbies { get; set; }
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
base.OnModelCreating(modelBuilder);
modelBuilder.Entity<Student>().HasKey(c => new { c.StudentId });
modelBuilder.Entity<Class>().HasKey(c => new { c.ClassId });
modelBuilder.Entity<Hobby>().HasKey(h => new { h.HobbyId });
}
}
Here's a simple controller action that uses a single EF Core 5 query statement in the get action to query for students along with their hobbies and classes included:
private readonly AppDbContext _dbContext;
public HomeController(AppDbContext dbContext, ILogger<HomeController> logger)
{
_dbContext = dbContext;
_logger = logger;
}
[HttpGet]
public IActionResult Index()
{
var students = _dbContext.Students
.Include(s => s.Hobbies)
.Include(s => s.Classes)
.ToList();
var filterableStudentListing = new FilterableStudentsListing
{
Students = students,
StudentClasses = students.SelectMany(s => s.Classes).Select(c => new SelectListItem { Text = c.ClassName, Value = c.ClassId.ToString() }).Distinct().ToList(),
StudentHobbies = students.SelectMany(s => s.Hobbies).Select(c => new SelectListItem { Text = c.HobbyName, Value = c.HobbyId.ToString() }).Distinct().ToList()
};
return View(filterableStudentListing);
}
[HttpPost]
public IActionResult Index(FilterableStudentsListing model)
{
// Demonstrates the new EF Core 5 filtered include, but you actually would not want to do this in a real app.
var students = _dbContext.Students
.Include(s => s.Hobbies.Where(h => h.HobbyId == model.SelectedHobbyId))
.Include(s => s.Classes.Where(c => c.ClassId == model.SelectedClassId))
.ToList();
var filterableStudentListing = new FilterableStudentsListing
{
Students = students,
StudentClasses = students.SelectMany(s => s.Classes).Select(c => new SelectListItem { Text = c.ClassName, Value = c.ClassId.ToString() }).Distinct().ToList(),
StudentHobbies = students.SelectMany(s => s.Hobbies).Select(c => new SelectListItem { Text = c.HobbyName, Value = c.HobbyId.ToString() }).Distinct().ToList()
};
return View(filterableStudentListing);
}
Clarifying on my code comment in the post action, the reason why it wouldn't be a good idea to use the EF Core Filtered Include in any real app, is you'd be stripping out possible select options for the user to choose each time you select a filter option to filter the data. In a real app, you would just filter the students in the view model after using the exact same query as in the get action. I just wanted to demo filtered include since its the new hotness in EF Core.
Here's a simple razor view that shows the students with their classes and hobbies:
#model FilterableStudentsListing
#{
ViewData["Title"] = "Home Page";
}
<div class="text-center">
<h1 class="display-4">Example</h1>
</div>
<div>
<form id="form1" method="post">
<select asp-for="SelectedClassId" asp-items="#Model.StudentClasses"> </select>
<select asp-for="SelectedHobbyId" asp-items="#Model.StudentHobbies"></select>
<button type="submit">Filter</button>
</form>
</div>
<div>
#foreach (var student in Model.Students)
{
<h4>#student.StudentName</h4>
<h5>Classes</h5>
<ul>
#foreach (var c in student.Classes)
{
<li>#c.ClassName</li>
}
</ul>
<h5>Hobbies</h5>
<ul>
#foreach (var h in student.Hobbies)
{
<li>#h.HobbyName</li>
}
</ul>
}
Here is the simple model class for the razor view:
public class FilterableStudentsListing
{
public List<Student> Students { get; set; }
public List<SelectListItem> StudentHobbies { get; set; }
public List<SelectListItem> StudentClasses { get; set; }
public int SelectedClassId { get; set; }
public int SelectedHobbyId { get; set; }
}
I have a database with 2 tables (clients and cars).
The table cars have a column named client_id.
In my Razor Pages project I have the models created for the 2 tables, but I need do a INNER JOIN to return the result to view side and do a foreach loop.
So until now I used the IList and do this in .cshtml:
#foreach (var item in Model.clients)
{
<p>#Html.DisplayFor(x=> item.name)</p>
<p>#Html.DisplayFor(x=> item.mail)</p>
}
And in code cshtm.cs
public IList<establishments> establishments;
IQueryable<establishments> establishments_filter;
establishments_filter = (from x in db.establishments where x.category == category select x);
establishments = establishments_filter.ToList();
Now the problem is that I cant do the same with a Inner Join or I donĀ“t know how (most probably).
I see in others posts that I can use a variable to receive the values like this:
var filter = (from x in db.cars join y in db.clients on x.id_client == y.id select new {
mark = x.mark,
model = x.model,
name = y.name,
mail = y.name
}.ToList();
But now, my real question... How I can do a foreach if the var filter is not acessible in cshtml?
Thanks
I think you can define a viewModel that contains the field in the filter object. Then you can foreach the viewModel in your page. A simple demo like below:
Model:
public class Car
{
public int id { get; set; }
public string mark { get; set; }
public string model { get; set; }
[ForeignKey("Client")]
public int client_id { get; set; }
}
public class Client
{
public int id { get; set; }
public string name { get; set; }
public string mail { get; set; }
}
ViewModel:
public class ViewModel
{
public string Mark { get; set; }
public string Model { get; set; }
public string Name { get; set; }
public string Mail { get; set; }
}
Index.cshtml:
#page
#model RazorPageApp.Pages.IndexModel
#{
ViewData["Title"] = "Index";
}
<table>
<thead>
<tr>
<th>Mark</th>
<th>Model</th>
<th>Name</th>
<th>Mail</th>
</tr>
</thead>
#foreach (var item in Model.filter)
{
<tr>
<td>#Html.DisplayFor(x => item.Mark)</td>
<td>#Html.DisplayFor(x => item.Model)</td>
<td>#Html.DisplayFor(x => item.Name)</td>
<td>#Html.DisplayFor(x => item.Mail)</td>
</tr>
}
</table>
Index.cshtml.cs:
public class IndexModel : PageModel
{
private readonly ILogger<IndexModel> _logger;
private readonly MyDbContext _db;
public IndexModel(ILogger<IndexModel> logger, MyDbContext db)
{
_db = db;
_logger = logger;
}
public List<ViewModel> filter { get; set; }
public async Task OnGetAsync()
{
filter = await (from x in _db.cars
join y in _db.clients on x.client_id equals y.id
select new ViewModel
{
Mark = x.mark,
Model = x.model,
Name = y.name,
Mail = y.mail
}).ToListAsync();
}
DataSource:
Result:
I have a form which has a place where a user can insert multiple tags separated by a comma into the database. I got it to insert, but I'm having trouble retrieving it to show on my edit form.
This is my Edit Action:
public IActionResult Edit(int id)
{
var gallery = _ctx.GalleryImages.SingleOrDefault(m => m.Id == id);
if (gallery == null)
return NotFound();
var categories = _ctx.Categories.ToList();
var model = new GalleryFormViewModel(gallery)
{
Tags = gallery.Tags,
Category = categories,
};
return View("Views/Image/UploadForm.cshtml", model);
}
Here is my ViewModel:
public class GalleryFormViewModel
{
public int? Id { get; set; }
public string Title { get; set; }
public IEnumerable<ImageTag> Tags { get; set; }
public IEnumerable<Category> Category { get; set; }
[Required]
public int CategoryId { get; set; }
public IFormFile ImageUplaod { get; set; }
public GalleryFormViewModel()
{
Id = 0;
}
public GalleryFormViewModel(GalleryImage galleryImage)
{
Id = galleryImage.Id;
Title = galleryImage.Title;
Tags = galleryImage.Tags;
CategoryId = galleryImage.CategoryId;
}
}
And here is the Form input: (I'm using this form for creating and editing the gallery)
<div class="form-group">
#Html.LabelFor(m => m.Tags)
#Html.TextBoxFor(m => m.Tags, new { #class = "form-control" })
#Html.ValidationMessageFor(m => m.Tags)
</div>
Here is the Tag Model:
namespace SimpleImageGallery.Data.Models
{
public class ImageTag
{
public int Id { get; set; }
public string Description { get; set; }
}
}
Here is the Gallery Model:
public class GalleryImage
{
public virtual IEnumerable<ImageTag> Tags { get; set; }
// ....
}
This is how the tags table looks in the database:
It seems like I'm not getting any errors, maybe something is wrong in the actual input field?
There are some mistakes :
First, you have to Include the Tags to retrieve them from DB (if using Entity Framework):
var gallery = _ctx.GalleryImages.Include(m=>m.Tags).SingleOrDefault(m => m.Id == id);
Secondly, you are doing the same this twice :
var model = new GalleryFormViewModel(gallery)
{
Tags = gallery.Tags,
Category = categories,
};
and
public GalleryFormViewModel(GalleryImage galleryImage)
{
Id = galleryImage.Id;
Title = galleryImage.Title;
Tags = galleryImage.Tags;
CategoryId = galleryImage.CategoryId;
}
Thirdly, you cannot do this : #Html.TextBoxFor(m => m.Tags, new { #class = "form-control" }) for a enumerable, you have to reconstruct the string.
I have these models
public class employee
{
public int Empid {get;set;}
public string name {get;set;}
public string fname{get;set;}
}
public class empLanguage
{
public string language { get; set; }
public string speaking{ get; set; }
}
public class EmpInsertion
{
public string name { get; set; }
public string fname { get; set; }
public List<ViewModels.Employee.empLanguage> EmpLangs { get; set; }
}
and i have this controller
[HttpPost]
[ValidateAntiForgeryToken]
public ActionResult Insert(EmpInsertion empins)
{
if (ModelState.IsValid)
{
Employee Emp = new Employee();
Emp.name= empins.name;
Emp.fname= empins.fname;
var MaxID = (from emp in db.Employees select emp.EmployeeID).Max();
EmpLanguage objlng = new EmpLanguage();
objlng.EmployeeID = MaxID;
foreach (var emplang in Emp.EmpLanguages.ToList())
{
empLanguage lng = new empLanguage();
emplang.Language = lng.language;
emplang.Speaking = lng.speaking;
empins.EmpLangs.Add(lng);
}
}
}
I have two tables Employee(id, name, fname) Language(id, language, speaking, empid) it has one to many relationship, each employee can speak multiple language at same time.
I want to add data in both tables from one view, how can I add to one employee multiple language
I think you can do something like this.
public Employee CreatePartnerWithUser(Employee employee, List<Language> language)
{
using (DataContext context = new DataContext())
{
using (var trans = context.Database.BeginTransaction())
{
var insertEmployee = context.Employee.Add(employee);
context.SaveChanges();
var insertLanguage = context.Language.AddRange(language);
context.SaveChanges();
trans.Commit();
}
}
}
If use DbContext and EF change your model like this:
public class employee
{
public int id {get; set;}
public string name {get; set;}
public string fname {get; set;}
public virtual ICollection<language> language { get; set; }
}
public class language
{
public int id {get; set;}
public string languageName {get; set;}
public virtual ICollection<employee> employee { get; set; }
}
then in DbContext class add this modelBuilder to have many to many relations
modelBuilder.Entity<employee>()
.HasMany(e => e.language)
.WithMany(e => e.employee)
.Map(m => m.ToTable("employeeLanguage").MapLeftKey("employeeID").MapRightKey("languageID"));
after that, you can insert employee in a model like this:
[HttpPost]
[ValidateAntiForgeryToken]
public ActionResult Insert(employee emp, string langs)
{
if (ModelState.IsValid)
{
using(DbContext db = new DbContext())
{
if(langs != null)
{
string[] strLangs = langs.Split('-');
foreach (var l in strLangs)
{
string lan = l.Trim();
while (lan.Contains(" "))
lan.Replace(" ", " ");
emp.language.Add(new language
{
languageName = lan
});
}
}
db.employee.Add(emp);
db.SaveChanges();
return View("YourReturnPage");
}
}
return View(Insert);
}
and in Insert.cshtml file adds this piece of code:
<div class="form-group">
<label for="Title" class="control-label col-md-2">Languages</label>
<div class="col-md-10">
<textarea type="text" name="langs" class="form-control" value="#ViewBag.langs"></textarea>
<p class="help-block text-danger">Separate language keywords with - </p>
</div>
</div>
I am making an auction application. Currently I am working on the bidding system. My Auction model consists of:
public class Auctions
{
[Key]
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public int ID { get; set; }
(...) some more fields like title, description etc(...)
public List<Bid> bids = new List<Bid>(); // a list of bids from the users
}
public class Bid
{
public string bidAuthor { get; set; }
public decimal bid { get; set; }
public DateTime bidDate { get; set; }
}
In the view, I have a form for sending a bid:
#model BiddingViewModel
(...)info stuff about the auction(...)
#using (Html.BeginForm("CreateBid", "Auction", FormMethod.Post, new { enctype = "multipart/form-data" }))
{
#Html.AntiForgeryToken()
#Html.HiddenFor(model=>model.auctionToSend.ID)
#Html.EditorFor(model => model.bid)
<input type="submit" value="Send a bid" />
}
Then, my contoller:
[Authorize]
[HttpPost]
[ValidateAntiForgeryToken]
public async Task<ActionResult> CreateBid(BiddingViewModel bvm)
{
var user = await _userManager.FindByIdAsync(HttpContext.User.GetUserId());
var tmp = _context.Auctions.FirstOrDefault(i => i.ID == bvm.auctionToSend.ID);
Bid newBid = new Bid()
{
bid = decimal.Parse(bvm.bid.ToString()),
bidAuthor = user.Email,
bidDate = DateTime.Now
};
tmp.bids.Add(newBid);
_context.Entry(tmp).State = Microsoft.Data.Entity.EntityState.Modified;
_context.SaveChanges();
return RedirectToAction("AuctionList", "Auction");
}
Unfortunately, this doesn't seem to update my bids column in my database (which is of type VARBINARY(MAX)). What am I doing wrong?
I'll try to explain this as good as I can for the future users: the problem was that I tried to add the List as a column of the table. What I should have done was to add a new table for the Bids, linked in ApplicationDbContext:
public DbSet<Auctions> Auctions { get; set; }
public DbSet<Bid> Bids { get; set; }
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.Entity<Auctions>()
.HasOne(p => p.Signup)
.WithMany(b => b.Auction);
modelBuilder.Entity<Bid>()
.HasOne(p => p.Auction)
.WithMany(b => b.bids);
base.OnModelCreating(modelBuilder);
}
The rest was explained in Stephen Muecke's comments