I have just started with web API and got my first Get method successfully implemented. I was able to retrieve the data and show it to the client. Now I have to retrieve data from two tables through a single Get method which I am not able too. Here's my code for retrieving data from a single table.
public HttpResponsemessage Get(string Login, string Password)
{
using (Accord_BMHEntities entities = new Accord_BMHEntities())
{
Login = Login.Trim();
EncryptDecrypt EncryptDecryptObj = new EncryptDecrypt();
string EncryptedPassword =
EncryptDeccrypt.Encrypt(Login.Trim().ToUpper(), Password);
var userLogin = entities.ITPLUsers.firstOrDefault(e => e.Login == Login
& e.Password == EncryptedPassword
if (UserLogin == null)
{
return Request.CreateErrorResponse(HttpStatusCode.NotFound)
}
else
{
return Request.CreateResponse(HttpStatusCode.OK , UserLogin)
}
}
}
EmpMaster.cs
public partial class EmpMaster
{
[System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Usage", n
CA2212:DoNotCallOverridableMethodsInConstructors")]
Public EmpMaster()
{
this.EmpPersonal = new HashSet<EmpPersonal>();
}
Public int EmployeeID { get; set; }
Public int DivisionId { get; set; }
Public int ResumeId { get; set; }
Public int GroupId { get; set; }
Public int DepartmentId { get; set; }
Public int WorkplaceId { get; set; }
Public int DesignationId { get; set; }
Public int Code { get; set; }
Public int DesignationId { get; set; }
[System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Usage", n
CA2227:CollectionPropertiesShouldBeReadOnly")]
public virtual ICOllection<EmpPersonal> EmpPersonals { get; set; }
}
EmpPersonal.cs
Public partial class EmpPersonal
{
Public int EmployeeID { get; set; }
Public int DivisionID { get; set; }
Public short Gender { get; set; }
Public short BloodGroup { get; set; }
Public string FlatNo { get; set; }
Public string Premises { get; set; }
Public string Street { get; set; }
Public string Area { get; set; }
Public string City { get; set; }
Public string StateId { get; set; }
Public string CountryId { get; set; }
Public virtual EmpMaster EmpMaster { get; set; }
}
Please note : there are many more properties in both the class. Just to save time i have mentioned some of it.
If you have modeled the relation between the entities, you could use the Entity Framweork Include method (for eager loading) or Load method (for lazy loading).
Docs here: Entity Framework Loading Related Entities
Otherwise you could return an anonymous type:
var userLogin = entities.ITPLUsers.firstOrDefault(e => e.Login == Login
& e.Password == EncryptedPassword;
var empPersonal = entities.EmpPersonal.Where(....your condition...);
if (UserLogin == null)
{
return Request.CreateErrorResponse(HttpStatusCode.NotFound)
}
else
{
return Request.CreateResponse(HttpStatusCode.OK,new {userlogin = UserLogin, empPersonal = empPersonal});
}
I assume you want user address from Address entity along with user data. You have 2 options here. You can either use linq and join to get all related data with one query or you can get user and address datas one by one. In the end, you need to consolidate the data and return one result set since you want to return all of them in a single response.
You obviously need a resultModel for your endpoint.
Like;
public class UserResultModel
{
//Properties from User entity
public int Id { get; set; }
public string Username { get; set; }
//Properties from Address entity
public string City { get; set; }
}
You need to fill this resultModel and return it.
Irrelevant suggestion: I suggest not to check user's authentication like that. MVC has a really nice feature called Filters(Authorization Filters for more detailed)
Try Linq with joins by matching keys. You will get an example from msdn site.Msdn site
Related
I have two tables with a many-to-many relationship and a link table. One is called taskSchedule and the other is called User. They are linked through a table called UserTask. One user can have many tasks assigned to them and one task can have many users assigned to them. I have run the dotnet ef database update command and it has successfully created the link table userTasks in the mysql database.
However, I am trying to get one of the APIs to post information to the taskSchedule table where it needs to add the user id and task id to the userTask link table. However I have no idea on how to do this.
below is the model for user.cs, taskschedule.cs and userTask.cs
user.cs
public class User: IdentityUser<int>
{
public string FirstName { get; set; }
public string LastName { get; set; }
public virtual ICollection<UserRole> UserRoles {get; set;}
public IList<userTask> UserTasks {get; set;}
}
TaskSchedule.cs
public class TaskSchedule
{
public int Id { get; set; }
public string Title { get; set; }
public DateTime? Start { get; set; }
public DateTime? End { get; set; }
public bool isClosed { get; set; }
public bool isDeleted { get; set; }
public byte priorityLevel { get; set; }
public bool hasTimeLimit { get; set; }
public Customer customer { get; set; }
public int? customerId { get; set; }
public List<Note> Notes { get; set; }
public List<AttachmentFile> Attachments { get; set; }
//user currently assigned to the task
// public int? userCurrentAssignedId { get; set; }
// [ForeignKey("userCurrentAssignedId")]
// public User userCurrentAssigned { get; set; }
public List<userTask> UserTasks {get; set;}
//user who last edited the task
public int? userLastEditId { get; set; }
[ForeignKey("userLastEditId")]
public User userLastEdit { get; set; }
public DateTime? userLastEditDate { get; set; }
public DateTime taskCreatedDate { get; set; }
}
}
userTask.cs // linktable
public class userTask
{
public int UserId { get; set; }
public User User { get; set; }
public int TaskScheduleId { get; set; }
public TaskSchedule TaskSchedule { get; set; }
}
Controller to update record
[HttpPut("{id}")]
public ActionResult<TaskSchedule> PutSchedule(int id, [FromBody] TaskSchedule taskSchedule)
{
int tokenUserId = int.Parse(User.FindFirst(ClaimTypes.NameIdentifier).Value);
taskSchedule.userLastEditId = tokenUserId;
DateTime thisDay = DateTime.Now;
string NowDate = thisDay.ToString("g");
taskSchedule.userLastEditDate = Convert.ToDateTime(NowDate);
if(taskSchedule.Start < taskSchedule.End || taskSchedule.Start == null && taskSchedule.End == null) {
TaskSchedule taskSchedulePut = _repo.Update(id, taskSchedule);
return taskSchedulePut;
}
return BadRequest("start time is not less than end time");
}
repository
public TaskSchedule Update(int Id, TaskSchedule taskSchedule)
{
var TaskScheduleDb = _context.TaskSchedules.SingleOrDefault(s => s.Id == Id);
TaskScheduleDb.Title = taskSchedule.Title;
TaskScheduleDb.Start = taskSchedule.Start;
TaskScheduleDb.End = taskSchedule.End;
TaskScheduleDb.userLastEditDate = taskSchedule.userLastEditDate;
// this is where the userId needs to be updated on the usertable.
taskSchedule.UserTasks.FirstOrDefault(u => u.UserId == taskSchedule.UserTasks.);
taskSchedule.UserTasks[0].UserId = taskSchedule.UserTasks.;
_context.SaveChanges();
return TaskScheduleDb;
}
Where in the repository can I add the tokenUserId, coming from the controller, to the userId in the userTask link table? Furthermore, how do I add the taskId for the userTask link table if the taskId is only just being generated?
I don't see why you need to update UserTask table too. So just replace
taskSchedule.UserTasks.FirstOrDefault(u => u.UserId == taskSchedule.UserTasks.);
taskSchedule.UserTasks[0].UserId = taskSchedule.UserTasks.;
_context.SaveChanges();
with
_context.Entry(TaskScheduleDb).State=EntityState.Modified;
_context.SaveChanges();
I have 2 tables in my entity framework:
INATIVOS (Employees)
EMPRESAS (Companies)
When registering an employee I select a company in a #Html.DropDownListFor (List).
The registration is ok, the company is saved correctly. However, when trying to edit a registered employee shows the error "Unable to set field/property on entity" in the Companies list.
INATIVO.cs
public partial class INATIVOS
{
public decimal ID { get; set; }
public string COD_EMPRESA { get; set; }
public string CHAPA { get; set; }
public string NOME { get; set; }
public System.DateTime DATA_NASC { get; set; }
public string PLANO { get; set; }
public short LEI { get; set; }
public short APOSENTADO { get; set; }
public short ESTADO_VIDA { get; set; }
public short ISENTO { get; set; }
public Nullable<System.DateTime> INICIO_VIGENCIA { get; set; }
public Nullable<System.DateTime> FIM_VIGENCIA { get; set; }
public string CPF { get; set; }
public string EMAIL { get; set; }
public string ENDERECO { get; set; }
public string NUMERO { get; set; }
public string COMPLEMENTO { get; set; }
public string BAIRRO { get; set; }
public string CIDADE { get; set; }
public string ESTADO { get; set; }
public string CEP { get; set; }
public string TELEFONE { get; set; }
public string CELULAR { get; set; }
public string OBSERVACAO { get; set; }
public List<DEPENDENTES> DEPENDENTES { get; set; }
public List<EMPRESAS> EMPRESAS { get; set; }
public List<PLANOS_MEDICO> PLANOS_MEDICO { get; set; }
}
InativoController.cs
public ActionResult Index(int? id)
{
INATIVOS inaModel = new INATIVOS();
using (Entidades db = new Entidades())
{
if (id != null)
{
inaModel = db.INATIVOS.Where(x => x.ID == id).FirstOrDefault();
}
inaModel.EMPRESAS = db.EMPRESAS.ToList<EMPRESAS>();
inaModel.PLANOS_MEDICO = db.PLANOS_MEDICO.ToList<PLANOS_MEDICO>();
}
return View(inaModel);
}
If these are navigation properties:
public List<DEPENDENTES> DEPENDENTES { get; set; }
public List<EMPRESAS> EMPRESAS { get; set; }
public List<PLANOS_MEDICO> PLANOS_MEDICO { get; set; }
Then (1) they need to be virtual and (2) they need to be something like IList or ICollection:
public virtual ICollection<DEPENDENTES> DEPENDENTES { get; set; }
public virtual ICollection<EMPRESAS> EMPRESAS { get; set; }
public virtual ICollection<PLANOS_MEDICO> PLANOS_MEDICO { get; set; }
Though, as an aside, what you're doing here is very strange:
inaModel.EMPRESAS = db.EMPRESAS.ToList<EMPRESAS>();
inaModel.PLANOS_MEDICO = db.PLANOS_MEDICO.ToList<PLANOS_MEDICO>();
Essentially what you have in the database is, for a given Employee (INATIVOS) there are relationships to specific Companies (EMPRESAS) and specific Medical Plans (PLANOS_MEDICO). But you're ignoring whatever is in that data and replacing it with all companies and all medical plans in the entire database.
So every time you use this controller action to fetch an existing employee record, it's going to look like that employee has every company and every medical plan. Even though that's not what's in the database. I strongly suspect that's not what you want.
UPDATE: Based on comments on this answer, it sounds like those aren't navigation properties. They're not even properties of the model at all. They're just lists of data needed for the view to populate (presumably) <select> elements.
If they're not part of the data model then remove them from the model. Instead, consider using a view model. For example:
public class InativosViewModel
{
public INATIVOS Inativos { get; set; }
public List<EMPRESAS> EMPRESAS { get; set; }
public List<PLANOS_MEDICO> PLANOS_MEDICO { get; set; }
}
Then in the controller return an instance of the view model, which is a composite object of the model and the data needed for the view:
public ActionResult Index(int? id)
{
InativosViewModel result = new InativosViewModel();
using (Entidades db = new Entidades())
{
if (id != null)
{
result.Inativos = db.INATIVOS.Where(x => x.ID == id).FirstOrDefault();
}
result.EMPRESAS = db.EMPRESAS.ToList<EMPRESAS>();
result.PLANOS_MEDICO = db.PLANOS_MEDICO.ToList<PLANOS_MEDICO>();
}
return View(result);
}
And of course change the model binding in the view itself to now expect and use an instance of InativosViewModel. The resulting POST action can still accept an instance of INATIVOS if it needs to, or it can accept an instance of InativosViewModel just as well. That all depends on what the form structure is and what's being posted to that action.
Alternatively, if you want to keep using the INATIVOS model then still remove those lists from it but use something like ViewBag to send them to the view. Something like this:
public ActionResult Index(int? id)
{
INATIVOS inaModel = new INATIVOS();
using (Entidades db = new Entidades())
{
if (id != null)
{
inaModel = db.INATIVOS.Where(x => x.ID == id).FirstOrDefault();
}
ViewBag.Empresas = db.EMPRESAS.ToList<EMPRESAS>();
ViewBag.PlanosMedico = db.PLANOS_MEDICO.ToList<PLANOS_MEDICO>();
}
return View(inaModel);
}
Then in your view you would populate the <select> elements from there:
#Html.DropDownListFor(
model => Model.COD_EMPRESA,
new SelectList(ViewBag.Empresas, "CODIGO", "DESCRICAO"),
htmlAttributes: new { #class = "form-control"
})
I have a table in my DB called Login. In this table I have an attribute called Head_ID, which is nullable.
Basically, you can have a chief, or you ARE the chief. In the case that you're the chief, the Head_ID should be null. In my application I have the possibility to change one's chief (Coworker changes chief, chief gets downgraded and get's a chief above him), but this int won't be set to
Service.cs
public int EditLogin(LoginDTO login)
{
try
{
var dbLogin = DAO.HourRegInstance.Login.Single(x => x.ID == login.Id);
dbLogin.Name = login.Name;
dbLogin.Username = login.Username;
if (login.Head_Id == 0)
{
//Doesn't work
dbLogin.Head_ID = null;
}
dbLogin.Role_ID = login.Role_Id;
DAO.HourRegInstance.SaveChanges();
return 1;
} catch(Exception e){
return -1;
}
}
Login.cs
public partial class Login
{
public Login()
{
this.HourRegistrationConfirmed = new HashSet<HourRegistrationConfirmed>();
this.HourRegistrationConfirmed1 = new HashSet<HourRegistrationConfirmed>();
this.Login1 = new HashSet<Login>();
this.LoginProject = new HashSet<LoginProject>();
}
public long ID { get; set; }
public string Name { get; set; }
public string Username { get; set; }
public string Password { get; set; }
public long Role_ID { get; set; }
public Nullable<long> Head_ID { get; set; }
public virtual ICollection<HourRegistrationConfirmed> HourRegistrationConfirmed { get; set; }
public virtual ICollection<HourRegistrationConfirmed> HourRegistrationConfirmed1 { get; set; }
public virtual ICollection<Login> Login1 { get; set; }
public virtual Login Login2 { get; set; }
public virtual Role Role { get; set; }
public virtual ICollection<LoginProject> LoginProject { get; set; }
}
How does one accomplish such task?
Solved the problem on my own.
I'm not sure what caused this problem, but after a restart of the service, everything seems to work alright now. Maybe just Entity Framework being Entity Framework?
So I have a model:
[Table("Site")]
public class Store : MPropertyAsStringSettable {
[Key]
[DatabaseGeneratedAttribute(DatabaseGeneratedOption.Identity)]
public int ID { get; set; }
public string Name { get; set; }
public int CompanyID { get; set; }
public bool IsActive { get; set; }
public string Address { get; set; }
public string City { get; set; }
[Display(Name = "Province")]
public int ProvinceID { get; set; }
public string Postal { get; set; }
public string Phone { get; set; }
public int StoreNumber { get; set; }
public bool visible { get; set; }
public DateTime lastShift { get; set; }
}
The field lastShift is from a different table called "Shifts", how do I get it from that table?
EDIT: The lookup will have to be something like this:
select top 1 shiftDate as lastShift from [Shifts] where SiteID = Store.ID
This is how I load my data:
public class MyDbContext: DbContext {
public MyDbContext()
: base("name=DefaultConnection") {
}
public DbSet<UserAccount> UserAccounts { get; set; }
public DbSet<Company> Companies { get; set; }
public DbSet<Store> Stores { get; set; }
public DbSet<ProvinceModel> Provinces { get; set; }
}
And this is how I use it:
MyDbContext database = new MyDbContext();
var myStores = from database.Stores select ID;
EDIT according to your last edit, this is not the case
It will need to be a "navigation property" which means that you'll need to have an explicit (Foreing Key) relationship between Site and Ship
Then you'll have something like this
Store.Shift.LastShift
But if it is a one to many relationship (and LastShift field is not part of Shift table) then
you'll need to do it manually, and use a view model or a custom property that it is not mapped directly to the table and do the assignment somewhere in your code
If you're using a Repository, then you'll need to add a method there to get the last shift
Then (or if you are using ORM directly) you use the code that #Cristi posted, just remember to add the sorting
public ActionResult MyAction(){
var store = db.Stores.Where(x => x.ID == objId).Select(x => new StoreModel(){
Name = x.Name,
ID = x.ID,
lastShift = db.Shifts.FirstOrDefault(y => y.SiteID == x.ID).OrderByDescending(shift => shift.Date);
}).FirstOrDefault();
return View(store);
}
Here is how I solved the problem.
TL,DR: I shared my dbcontext in my controller so I have access to it in my models!
I manually load the lastShiftTime in my Store Model with the following code:
public class Store : MPropertyAsStringSettable {
.......
public DateTime lastShiftTime {
get{
MyDbContext curContext = MyWebProject.Controllers.MyBaseController.database;
if (curContext != null) {
return (from shift in curContext.Shifts where shift.SiteID == ID select shift.ShiftDate).First();
} else {
return new DateTime(0);
}
}
}
}
Here is my Shift Model I created for this, very standard:
[Table("Shifts")]
public class Shift : MPropertyAsStringSettable {
[Key]
[DatabaseGeneratedAttribute(DatabaseGeneratedOption.Identity)]
public int ID { get; set; }
public int SiteID { get; set; }
public string ShiftID_In_POS { get; set; }
public DateTime ShiftDate { get; set; }
}
And here is how I am sharing the context in controller:
public class MyBaseController : Controller {
.........
private static MyDbContext _database;
public static MyDbContext database {
get {
if (_database == null) {
_database = new MyDbContext();
}
return _database;
}
}
}
First of all, I'm very new to the ASP.NET MVC C# and EF. I'm in a process of creating a website that should hopefully help me learn these three wonderful technologies. with that said, I have the following models in my project.
[Table("UserProfile")]
public class UserProfile
{
[Key]
[DatabaseGeneratedAttribute(DatabaseGeneratedOption.Identity)]
public int UserId { get; set; }
public string UserName { get; set; }
public string FirstName { get; set; }
public string Lastname { get; set; }
public string EmailAddress { get; set; }
//public string PhoneNumber { get; set; }
public bool? ChangePassword { get; set; }
public bool? Deletable { get; set; }
//Add more Properties for more fields
public virtual ICollection<CompanyInformation> ParentCompanies { get; set; }
public virtual StaffProfile sProfile { get; set; }
}
And
public class StaffProfile
{
[Key]
[DatabaseGeneratedAttribute(DatabaseGeneratedOption.Identity)]
public int StaffProfileId { get; set; }
public string Alias { get; set; }
public StaffGrouping Group { get; set; }
public ICollection<PhoneNumber> PhoneNumbers { get; set; }
public bool isPhoneNumberDisplayed { get; set; }
public bool TextNotificationsAllowed { get; set; }
public bool EmailNotificationsAllowed { get; set; }
public bool PhoneNotificationsAllowed { get; set; }
}
Staff Grouping
public class StaffGrouping
{
public int id { get; set; }
public string GroupName { get; set; }
}
and just for completeness, the phone numbers model
public class PhoneNumber
{
public int id { get; set; }
public string Number { get; set; }
public string Extension { get; set; }
public PhoneType Type { get; set; }
public bool isPrimary { get; set; }
public bool isInActive { get; set; }
}
public enum PhoneType {
Home,
Mobile,
Work,
Other
}
I'm trying to get all the staffs from the db (including the phone numbers, userprofile and group they are linked to) and adding it to a viewmodel for better integration with my view. currently I'm doing it as such:
public ActionResult ManageStaff()
{
using (var repo = new CompanyInformationRepository(new UniteOfWorkCompanies()))
{
var company = repo.FindCompany(User.Identity.Name);
var Users = repo.CompanyStafflookup(company);
var model = new List<StaffManagementViewModel>();
foreach (var user in Users)
{
var group = repo.StaffGroupLookup(user.sProfile);
//var phoneNumber = user.sProfile.PhoneNumbers.Where(p => p.isPrimary == true).FirstOrDefault();
model.Add(new StaffManagementViewModel
{
FirstName = user.FirstName,
LastName = user.Lastname,
EmailAddress = user.EmailAddress,
PhoneNumber = "(915) 433 - 1739", //phoneNumber.Number,
Group = group.GroupName,
UserID = user.UserId
});
}
return View(model);
}
And my repository:
public IQueryable<HoursOfOperation> CompanyHoursLookup(string userName)
{
var company = FindCompany(userName).id;
//var model = _db.HoursOfOperations.Where(e => e.Company.id == company);
var model = from h in _db.HoursOfOperations
where h.Company.id == company
orderby h.Day ascending, h.From ascending
select h;
return model;
}
public IQueryable<UserProfile> CompanyStafflookup(CompanyInformation company)
{
var model = from s in _db.UserProfiles.Include("sProfile")
where s.ParentCompanies.Any(e => e.companyName == company.companyName)
orderby s.FirstName ascending
select s;
return model;
}
public StaffGrouping StaffGroupLookup(StaffProfile Staff)
{
var Staffwithgroup = _db.StaffProfiles.Where(e => e.StaffProfileId == Staff.StaffProfileId).Include("Group").FirstOrDefault();
return Staffwithgroup.Group;
}
I'm guessing there should be a better more efficient way of doing this as I'm counting at least three trips to the database. I tried to use the .include but on the userprofile but since I don't have a navigation Property to point at the group, its giving me an error. the code I'm talking about is the following:
var model = from s in _db.UserProfiles.Include("sProfile").Include("PhoneNumbers").Include("Group")
where s.ParentCompanies.Any(e => e.companyName == company.companyName)
orderby s.FirstName ascending
select s;
Is there a way to achieve this in one call that basically would return a list of UserProfiles that includes the StaffProfile that includes the PhoneNumbers and finally the Group?
You can simply prefix the include with the full path, i.e. use:
Include("sProfile.Group")
This will include both StaffProfile and it's Group.