Having trouble with Entity Framework and relationships - c#

I'm having difficulty understanding how to create relationships with the entity framework. (I'm using the MVC architecture, and code first migrations )for example, if I have a class
public class Employee
{
public int Id { get; set; }
public string PIN { get; set; }
public string FirstName { get; set; }
public string LastName { get; set; }
}
and I have another class, let's say for example I wish to track employees hours
public class EmployeeHours
{
public int Id { get; set; }
public DateTime? ClockIn { get; set; }
public DateTime? ClockOut { get; set; }
public Employee emplyee { get; set; }
}
I'm having a difficult time understanding how I can have these two classes to interact with each other. Like if John Smith's PIN is 1234, and he enters his PIN into a textbox, how do I successfully add his clock in time and date to the employee hours class?
and if I have a view that looks like this for the employee to enter their PIN
#using (Html.BeginForm("ClockIn", "Login"))
{
#Html.LabelFor(c => c.Employee.PIN)
#Html.TextBoxFor(c => c.Employee.PIN)<br />
<button type="submit">Save</button>
}
and the clockIn controller looks like this
[HttpPost]
public ActionResult ClockIn(string Pin)//employee clocking in
{
_context.EmployeeHours.Add();
_context.SaveChanges();
return View();
}
I'm trying to figure out how to store the time and date associated with this employee in the class, so I can go back and see when this employee clocked in. Thanks!

Add a Navigation Property to Employee, EG:
public class Employee
{
public int Id { get; set; }
public string PIN { get; set; }
public string FirstName { get; set; }
public string LastName { get; set; }
public virtual ICollection<EmployeeHours> Hours { get; } = new HashSet<EmployeeHours>();
}
Then use it something like this:
[HttpPost]
public ActionResult ClockIn(string pin)//employee clocking in
{
var emp = _context.Employees.Where(e => e.Pin == pin).First();
var hours = new EmployeeHours();
hours.StartTime = DateTime.Now;
//...
emp.Hours.Add(hours);
_context.SaveChanges();
return View();
}

Related

net core asp with automatic create and update data field

I have a problem with EF and MVC on net core.
I have an "item" model. This model has the required "createdate" and "updateDate" fields (I can't have a record without having the record date).
I use both fields with inheritance over BaseEntity.
The point is that to assign these dates, I do it directly in the context, overriding the "SaveChanges" function.
Because of this, by not giving it a value in either the view or the controller, the model evaluation fails because the dates are null. I actually give it value but after passing validation.
What do you think would be the most correct solution?
This is my model:
public class ItemType : BaseEntity
{
[Key]
public int Id { get; set; }
[Required]
public string Description { get; set; }
public ICollection<Item> Items { get; set; }
public int SizeTypeId { get; set; }
public SizeType SizeType { get; set; }
}
public class BaseEntity
{
[Required]
public bool Active { get; set; }
public DateTime? DeleteDate { get; set; }
public DateTime CreatedDate { get; set; }
public DateTime UpdatedDate { get; set; }
public string Comments { get; set; }
}
this is my savechanges override:
public override int SaveChanges()
{
var entries = ChangeTracker
.Entries()
.Where(e => e.Entity is BaseEntity && (
e.State == EntityState.Added
|| e.State == EntityState.Modified));
foreach (var entityEntry in entries)
{
((BaseEntity)entityEntry.Entity).UpdatedDate = DateTime.Now;
if (entityEntry.State == EntityState.Added)
{
((BaseEntity)entityEntry.Entity).CreatedDate = DateTime.Now;
}
}
return base.SaveChanges();
}
this is my controller:
public async Task<IActionResult> Create([Bind("Id,Description,Active,DeleteDate,CreatedDate,UpdatedDate,Comments")] SizeType sizeType)
{
if (ModelState.IsValid)
{
_context.Add(sizeType);
await _context.SaveChangesAsync();
return RedirectToAction(nameof(Index));
}
return View(sizeType);
}
Thanks you very much!!
One easy option would be to just make the properties nullable i.e. DateTime? CreatedDate since the entity isn't already created or deleted it makes sense for the CreatedDate or UpdatedDate properties to be null. However, a better option is to just create a DTO or in other words, a ViewModel that wraps the data of your entity and exposes it to the View. For example:
public class ItemTypeRequest : BaseEntityRequest
{
[Required]
public string Description { get; set; }
public ICollection<Item> Items { get; set; }
public int SizeTypeId { get; set; }
public SizeType SizeType { get; set; }
}
public class BaseEntityRequest
{
[Required]
public bool Active { get; set; }
public string Comments { get; set; }
}
Normally in a well-defined ViewModel, you are exposing to the View only the minimal set of data that is needed to perform the operation. In this case the Create View does not need an Id, CreatedDate, or UpdatedDate, because the entity isn't already created i.e. it does not exist in the database and therefore has no Id or creation date. After creating the ViewModels you can leverage AutoMapper to map the ViewModels data to the entity data
var config = new MapperConfiguration(cfg => {
cfg.CreateMap<ItemTypeRequest , ItemType>();
cfg.CreateMap<ItemType, ItemTypeResponse>();
});
var mapper = config.CreateMapper();
public async Task<IActionResult> Create(ItemTypeRequest itemTypeRequest)
{
if (ModelState.IsValid)
{
ItemType itemType = mapper.Map<ItemType>(itemTypeRequest);
_context.Add(itemType);
await _context.SaveChangesAsync();
return RedirectToAction(nameof(Index));
}
return View();
}
Following the same login, you should create a response DTO/ViewMode this time with more data if you need it i.e.
public class ItemTypeResponse : BaseEntityResponse
{
public int Id { get; set; }
public string Description { get; set; }
public ICollection<Item> Items { get; set; }
public int SizeTypeId { get; set; }
public SizeType SizeType { get; set; }
}
public class BaseEntityResponse
{
public bool Active { get; set; }
public DateTime CreatedDate { get; set; }
public DateTime UpdatedDate { get; set; }
public string Comments { get; set; }
}
This time the properties CreatedDate and UpdatedDate are included since they have values after you have created the entity.

Class Id from view to other class/view MVC 4

So i'm working with MVC 4 and i have a question. I have two classes, Schedule and Simulation(its to simulate a student to enter the classroom). I have a view that gives all the schedules that exists next to a link to simulate entrance (it's another view, another class). I would like to pass the id from the schedule that the person chooses to a attribute in the Simulation class.
Class simulation:
namespace GestorSalas.Models
{
public class Simulation
{
public virtual int SimulationId { get; set; }
public virtual int ScheduleId { get; set; }
public virtual string Utilizador { get; set; }
[DisplayName("Tipo de Utilizador")]
public virtual string TipoUtilizador { get; set; }
public virtual int Codigo { get; set; }
public virtual string Hora { get; set; }
public virtual bool Entrar { get; set; }
}
Class Schedule:
namespace GestorSalas.Models
{
public class Schedule
{
public virtual int ScheduleId { get; set; }
public virtual int DisciplinaId { get; set; }
public virtual int SalaId { get; set; }
[Required]
public virtual int Dia { get; set; }
[Required]
[DisplayName("Hora de Entrada")]
public virtual string HoraEntrada { get; set; }
[Required]
[DisplayName("Hora de Saida")]
public virtual string HoraSaida { get; set; }
public virtual Disciplina Disciplina { get; set; }
public virtual Sala Sala { get; set; }
}
This is the view: (what the user sees)
(Entrar na sala=link to the simulation create view, in this image is the schedule index view).
I would like to pass the id from the schedule in order to appear in the simulation form (or in the table after the creation, much like when we click details or edit and it takes the user id but i want the schedule id).
This is the code in the "Entrar em sala" link:
#Html.ActionLink("Entrar em sala", "Create", "Simulation", new {id = item.HorarioId }, null)
But it doesn't work. Any ideas on how can i do this?
EDIT: The controllers:
Simulation:
To create
//
// GET: /Simulacao/Create
public ActionResult Create()
{
return View();
}
//
// POST: /Simulacao/Create
[HttpPost]
[ValidateAntiForgeryToken]
public ActionResult Create(Simulation simulation)
{
if (ModelState.IsValid)
{
db.Simulacaos.Add(simulation);
db.SaveChanges();
return RedirectToAction("Index");
}
return View(simulation);
}
And the schedule is just the index that is being used here:
public ActionResult Index()
{
var schedules= db.Schedules.Include(h => h.Disciplina).Include(h => h.Sala);
return View(schedules.ToList());
}
Your create method should accept the id as a parameter and use it as needed.
public ActionResult Create(int id)
{
// Id has the scheduleId
// to do : Do something with the Id passed in and return something
}
and in your index view, you need to pass the scheduleId as value of route param Id
#model List<Schedule>
<h2>Simulator</h2>
<table>
<tr><th>HoraEntrada </th><th>Dia </th><th></th></tr>
#foreach(var item in Model)
{
<tr>
<td>#item.HoraSaida</td>
<td>#item.Dia</td>
<td>#Html.ActionLink("Entrar em sala", "Create", "Simulation",
new {#id = item.ScheduleId}, null)
</td>
</tr>
}
</table>

MVC get child count in Index view

I used scaffolding to create the Index, Details, Create, Edit and Delete views and the controller. I have two view models (Parent / Child) relation. In my Index view I want to display the list of Teams as well as some information on the players (Parent / child). For example I want to display in the Index view the teams with the players count per team and last players that was modified. I am not sure where to begin.
Example:
(Team) Red -- (Last Modified) 01/02/2015 -- (Number Players) 10 and so on.
Team ViewModel
public class TeamVM
{
public int ID { get; set; }
public string Name { get; set; }
public DateTime? LastActivity { get; set; }
public string NumberPlayers { get; set; }
public IList<PLayerVM> PlayerVM { get; set; }
}
Player ViewModel
public class PlayerVM
{
public int ID { get; set; }
public int TeamID { get; set; }
public string PlayerInfo { get; set; }
public DateTime? CreateDate { get; set; }
}
Other ViewModel
public class TeamViewModel
{
public List<Team> Teams{ get; set; }
}
Controller
public ActionResult Index()
{
TeamViewModelviewModel = new TeamViewModel();
viewModel.Teams= db.Teams.ToList();
return View(viewModel);
}
db.Products.ToList()?? I assume that is where you mean db.Teams.ToList()?
You are using viewmodels, so you should map the db data to your viewmodels first:
public ActionResult Index()
{
var teams = db
.Teams
.Include("Players") // Assuming your Team entity has a collection of Players
.SelectMany(t => new TeamVM {
ID = t.ID,
// etc..
})
.ToList();
return View(new TeamViewModel { Teams = teams });
}
model:
public class TeamVM
{
public int ID { get; set; }
public string Name { get; set; }
public DateTime? LastActivity { get; set; }
public IList<PLayerVM> PlayerVM { get; set; }
public int NumberPlayers {
get { return PlayerVM.Count(); }
}
}
Then in your view:
#model MyProject.Models.TeamViewModel
<table>
#foreach(var team in Model.Teams.ToList()) {
<tr>
<td>#team.Name</td> // Name
<td>#team.NumberPlayers</td> // Playercount
<td>#team.PlayerVM.Max(p => p.LastActivity).LastActivity</td> // Last edited
</tr>
}
</table>

Saving from a view model to a model in ASP.NET MVC

I have two models, a code model and a tag model which are linked by a many to many relationship. I am trying to add a code entry that includes a possible selection of many tags using a view model (using check boxes for the tags in my view). I am getting the error:
The model item passed into the dictionary is of type 'System.Collections.Generic.List'1[StoRed.Models.Code]', but this dictionary requires a model item of type 'System.Collections.Generic.IEnumerable`1[StoRed.Models.CodeTagViewModel]'.
It feels like I need to somehow convert my data to the acceptable format before trying to save it into the table but I'm new to MVC and I am having trouble finding any useful information on the internet about my specific problem. Any help would be greatly appreciated.
The code model
public class Code
{
[Key]
public int CodeID { get; set; }
[Required]
[StringLength(30)]
public string Title { get; set; }
[Required]
[StringLength(150)]
public string Description { get; set; }
public DateTime DateAdded { get; set; }
public DateTime LastUpdated { get; set; }
[Required]
[StringLength(30)]
public string Project { get; set; }
[Required]
[StringLength(30)]
public string CMS { get; set; }
public int DotNetVersion { get; set; }
[Required]
[StringLength(150)]
public string Dependencies { get; set; }
[StringLength(30)]
public string Author { get; set; }
public string CodeFile { get; set; }
[Required]
[StringLength(100)]
public string TFSLocation { get; set; }
////Creates a relationship in the DB with Tag
//[ForeignKey("TagID")]
public virtual ICollection<Tag> Tags { get; set; }
////Purely for API
//[Required]
public int TagID { get; set; }
}
The Tag model
public class Tag
{
[Key]
public int TagID { get; set; }
[Required]
[StringLength(30)]
public string TagName { get; set; }
public virtual ICollection<Code> Code { get; set; }
}
The context
public class Context : DbContext
{
public DbSet<Code> Code { get; set; }
public DbSet<Tag> Tags { get; set; }
}
The view model
public class CodeTagViewModel
{
public Tag Tag { get; set; }
public Tag TagID { get; set; }
public List<Tag> Tags { get; set; }
public int CodeID { get; set; }
public string Title { get; set; }
public string Description { get; set; }
public DateTime DateAdded { get; set; }
public DateTime LastUpdated { get; set; }
public string Project { get; set; }
public string CMS { get; set; }
public int DotNetVersion { get; set; }
public string Dependencies { get; set; }
public string Author { get; set; }
public string CodeFile { get; set; }
public string TFSLocation { get; set; }
}
Relevant part of the code controller
[HttpPost]
public ActionResult Create(CodeTagViewModel codeTagViewModel)
{
if (ModelState.IsValid)
{
Code code = new Code();
Tag tag = new Tag();
var codeTag = new CodeTagViewModel();
code.Title = codeTagViewModel.Title;
code.Description = codeTagViewModel.Description;
code.DateAdded = codeTagViewModel.DateAdded;
code.LastUpdated = codeTagViewModel.LastUpdated;
code.Project = codeTagViewModel.Project;
code.CMS = codeTagViewModel.CMS;
code.DotNetVersion = codeTagViewModel.DotNetVersion;
code.Dependencies = codeTagViewModel.Dependencies;
code.Author = codeTagViewModel.Author;
code.CodeFile = codeTagViewModel.CodeFile;
code.TFSLocation = codeTagViewModel.TFSLocation;
code.Tags = codeTagViewModel.Tags;
db.Code.Add(code);
db.SaveChanges();
return RedirectToAction("Index");
}
return View(codeTagViewModel);
}
Your best bet is to create some kind of provider/manager/service/factory/handler - choose a name that makes most sense in terms of the job it is doing within the flow of data through your system - that is responsible for taking the ViewModel and mapping the properties of the ViewModel into an instance of the domain model before persisting the domain model to the data store, either itself or by passing the hydrated domain model to a repository layer. You can either do this manually or by using something like AutoMapper. Here's a quick manual example:
Create a CommandHandlers folder in your web project with the interface and dependant handler:
public interface ICodeCommandHandler
{
int Save(CodeTagViewModel input);
}
public class CodeCommandHandler : ICodeCommandHandler
{
private IRepository<Code> repository;
public CodeCommandHandler(IRepository<Code> repository)
{
this.repository = repository;
}
public int Save(CodeTagViewModel input)
{
Code code = new Code();
Tag tag = new Tag();
code.Title = input.Title;
code.Description = input.Description;
code.DateAdded = input.DateAdded;
code.LastUpdated = input.LastUpdated;
code.Project = input.Project;
code.CMS = input.CMS;
code.DotNetVersion = input.DotNetVersion;
code.Dependencies = input.Dependencies;
code.Author = input.Author;
code.CodeFile = input.CodeFile;
code.TFSLocation = input.TFSLocation;
code.Tags.Add(tag);
return repository.Save(code);
}
}
Then in your controller, inject the ICodeCommandHandler in via constructor injection, the same as you do with the repository in the CodeCommandHandler:
private readonly ICodeCommandHandler commandHandler;
public CodeController(ICodeCommandHandler commandHandler)
{
this.commandHandler = commandHandler;
}
[HttpPost]
public ActionResult Create(CodeTagViewModel codeTagViewModel)
{
if (!ModelState.IsValid)
{
return View(codeTagViewModel);
}
var id = codeCommandHandler.Save(codeTagViewModel);
// maybe do something useful with the document id after save
return RedirectToAction("Index");
}
To keep the Repository nice and simple, here's how that could look:
public interface IRepository<T>
{
int Save(T entity);
}
public class CodeRepository : IRepository<Code>
{
public int Save(Code entity)
{
using (var context = new Context())
{
context.Code.Add(entity);
context.SaveChanges();
}
}
}
I've not gone into detail about the dependency injection side of things as that wasn't part of the question but this should give you an idea of where to start

ViewModel adding custom class to another class

I created this viewmodel:
public class PlayerViewModel
{
PlayerRepository repo = new PlayerRepository();
public Player Player { get; set; }
public int SelectedUserID { get; set; }
public SelectList Users { get; set; }
public PlayerViewModel()
{
Player = new Player();
}
public PlayerViewModel(int id)
{
Player = repo.Retrieve(id);
Users = new SelectList(repo.GetUsers());
SelectedUserID = 0;
}
}
this I have in view:
#Html.DropDownListFor(x => x.SelectedUserID, Model.Users)
#Html.ValidationMessageFor(x => x.SelectedUserID)
and this in controller:
[Authorize]
public ActionResult Upravit(int id)
{
var playerview = new PlayerViewModel(id);
return View(playerview);
}
[Authorize,HttpPost]
public ActionResult Upravit(int id, PlayerViewModel playerView)
{
if (ModelState.IsValid)
{
playerView.Player.User = usRepo.GetUserById(playerView.SelectedUserID);
repo.Save(playerView.Player);
return RedirectToAction("Podrobnosti", new { id = playerView.Player.PlayerID });
}
return View(playerView);
}
Now I have problem that " The field SelectedUserID must be a number." and I have in dropdownlist UserName. I modified this many times, I tried with Dictionary and other ways but everyway has some problem. So I want just ask for best way to add custom class User to class Player.
Player class:
public class Player
{
// pokud použijeme virtual a vlastností tak nám EF rozšíří o další možnosti jako lazy loading a další
[Key]
public int PlayerID { get; set; }
[Required]
public string Name { get; set; }
[Required]
public string Surname { get; set; }
public string PhotoUrl { get; set; }
public string Post { get; set; }
public virtual Team Team { get; set; }
public virtual User User { get; set; }
// public int UserID { get; set; }
//public virtual ICollection<Article> Articles { get; set; }
// Here could be next things as number, ...
}
Thanks
Use this constructor instead:
http://msdn.microsoft.com/en-us/library/dd505286.aspx
public SelectList(
IEnumerable items,
string dataValueField,
string dataTextField
)
Something like this:
Users = new SelectList(repo.GetUsers(),"UserID", "UserName");

Categories

Resources