I have two tables I need to join for my Razor Page view. The first table called 'Account' contains an Account record with a int Status. The second table called 'AccountStatuses' contains possible statuses for the Account. Scaffolding created the following code in Account\Index.cshtml.cs
public IList<Account> Account { get;set; }
public async Task OnGetAsync()
{
Account = await _context.Account.ToListAsync();
}
The Account table contains a column "Status" that corresponds to the column "Value" in the AccountStatus table. I want to join on these and return the column "StatusString" from the AccountStatus table to the view.
You do not have to join the two tables to get the values. If you setup properly your models you can let Entity Framework do the work for you. I will give you an example of how I would create the models. First of all, we have the two models:
public class Account
{
[Key]
[DatabaseGeneratedAttribute(DatabaseGeneratedOption.Identity)]
public int AccountID { get; set; }
public string AccountName { get; set; }
public int AccountStatusID { get; set; }
[ForeignKey("AccountStatusID")]
public virtual AccountStatus AccountStatus { get; set; }
}
public class AccountStatus
{
[Key]
[DatabaseGeneratedAttribute(DatabaseGeneratedOption.Identity)]
public int AccountStatusID { get; set; }
public string AccountStatusName { get; set; }
public virtual ICollection<Account> Accounts { get; set; }
}
The Account model has the property AccountStatusID which will contain the id for the status. We also define a virtual property for the AccountStatus model. This will be automatically loaded by EntityFramework for us when we ask it from Entity Framework.
We do something similar for the AccountStatus model but in this model we will have a virtual collection of Account models.
Now we will have to define our ApplicationDbContext class which could be the following:
public class ApplicationDbContext : IdentityDbContext<ApplicationUser>
{
public ApplicationDbContext(DbContextOptions<ApplicationDbContext> options)
: base(options)
{
}
protected override void OnModelCreating(ModelBuilder builder)
{
base.OnModelCreating(builder);
}
public DbSet<Account> Accounts { get; set; }
public DbSet<AccountStatus> AccountStatuses { get; set; }
}
Now we can execute the following queries:
// Get the account by id
Account account1 = await _context.Accounts.SingleOrDefaultAsync(m => m.AccountID == id);
// Get the account by id including the Account status
Account account2 = await _context.Accounts.Include(m => m.AccountStatus).SingleOrDefaultAsync(m => m.AccountID == id);
// account2.AccountStatus contains the AccountStatus
string AccountStatusName = account2.AccountStatus.AccountStatusName;
// Get teh account status by id
AccountStatus AccountStatus1 = await _context.AccountStatuses.SingleOrDefaultAsync(m => m.AccountStatusID == id);
// Get the account status by id include the accounts
AccountStatus AccountStatus2 = await _context.AccountStatuses.Include(m => m.Accounts).SingleOrDefaultAsync(m => m.AccountStatusID == id);
// AccountStatus2.Accounts contain all the accounts which has be set to be equal to the current account status
foreach (var account in AccountStatus2.Accounts)
{
string AccountName = account.AccountName;
}
I hope it helps you.
Related
I am struggling to setup a scenario (using C# classes and OnModelCreating() method) where DB models would act as follows:
First assumption, the mandatory one:
I want to have the ability to create User (AspNetUsers table) without a reference to a Guest. It will be necessary while seeding a DB with an admin user - it will not belong to any of the Event-s.
In summary - at least that's my understanding - User will be PRINCIPAL, Guest will be DEPENDENT (?)
Cascading deletion: I want to delete Users from AspNetUsers table when I delete a given Event (cascade delete).
This functionality already exists for Guests. When I delete an Event, all related Guests are being deleted correctly.
Two questions:
1. How do I actually create Guests that are related to AspNetUsers table?
When it comes to Guests list and its assignement to a ceratin Event, I just do something like:
eventDbObject.Guests = GetGuestsList();
_dbContext.Events.Add(evenDbObject); //Event is created in Events table, Guests table is correctly populated as well
With users it's tricky - I have to Register them first, get their ID, and then assign that ID to a Guest object. Is that way correct?
foreach (var guest in weddingDbObject.Guests)
{
var userCreationResult = await _identityService.RegisterAsync("userName","password"); // my RegisterAsync() method returns actual User
guest.AppUser = userCreationResult.User;
}
2. How to set up cascade deletion in such a scenario?
builder
.Entity<Guest>()
.HasOne(e => e.AppUser)
.WithOne(e => e.Guest)
.OnDelete(DeleteBehavior.Cascade);
Something like this does not seem to work
My classes:
public class Event
{
// PK
public Guid Id {get;set;}
public List<Guest> Guests { get; set; }
}
public class Guest
{
// PK
public Guid Id { get; set; }
public string FirstName { get; set; }
public string LastName { get; set; }
public Guid AppUserId { get; set; }
public AppUser AppUser { get; set; }
}
public class AppUser : IdentityUser<Guid>
{
public WeddingGuest Guest { get; set; }
}
Ok, this is what I ended up with, assumptions are met, everything works as expected.
public async Task<string> AddEventAsync(Event event)
{
using (var transaction = _dbContext.Database.BeginTransaction())
{
try
{
// Add an event to [Events] table and corresponding guests to [Guests] table
_dbContext.Events.Add(event);
// Add users to [AspNetUsers] table
foreach (var guest in event.Guests)
{
var userCreationResult = await _identityService.RegisterAsync(guest.Id, $"{event.Name}-{guest.FirstName}-{guest.LastName}", guest.GeneratedPassword);
if (!userCreationResult.Success)
throw new Exception();
guest.AppUser = userCreationResult.User;
}
transaction.Commit();
_dbContext.SaveChanges();
}
catch
{
transaction.Rollback();
}
}
return event.Name;
}
Classes look like this:
public class Event
{
// PK
public Guid Id {get;set;}
public List<Guest> Guests { get; set; }
}
// PRINCIPAL
public class Guest
{
// PK
public Guid Id { get; set; }
public string FirstName { get; set; }
public string LastName { get; set; }
[JsonIgnore]
public AppUser AppUser { get; set; }
}
// DEPENDENT
public class AppUser : IdentityUser<Guid>
{
public Guid? GuestId { get; set; } // '?' allows for 1:0 relationship
[JsonIgnore]
public Guest Guest { get; set; }
}
Fluent API DbContext configuration:
builder.Entity<AppUser>(entity =>
{
entity.HasOne(wg => wg.Guest)
.WithOne(a => a.AppUser)
.HasForeignKey<AppUser>(a => a.GuestId)
.IsRequired(false)
.OnDelete(DeleteBehavior.Cascade);
});
Hello I am new to servers and REST API and am trying to extract data from a dynamically created table and the data does not sync with the data in the database.
I have an sql database from which I extracted an entity database in asp.net web project.
This is an example for GET of one entity class (exists in database):
public class EmployeeBL
{
private FSProject1Entities db = new FSProject1Entities();
public List<Employee> GetEmployees(string fname, string lname, string depID)
{
return GetEmployeeSearchResult(fname, lname, depID);
}
}
And this is an example for a method from a class such as I created in order to combine data from 2 tables:
public class ShiftEmployeeDataBL
{
private FSProject1Entities db = new FSProject1Entities();
private List<ShiftEmployeeDataBL> GetEmployeeByShiftID(int id)
{
List<ShiftEmployeeDataBL> shiftEmpData = new List<ShiftEmployeeDataBL>();
foreach (Employee emp in db.Employee)
{//build list... }
return shiftEmpData;
}
My problem is that db.Employee via this GET request path (ShiftEmployeeData) is old data and via Employee GET request is good data (assuming the data was updated via Employee path).
And vice versa - it would appear that if I update Employee via ShiftEmployeeData class, it would appear as good data for ShiftEmployeeData class and not update for Employee.
I have APIcontrollers for both classes.
what is happening? I feel like I am missing something.
I tried closing cache options in browser.
update with code for elaboration:
entity Employee:
public partial class Employee
{
public int ID { get; set; }
public string FirstName { get; set; }
public string LastName { get; set; }
public int StartWorkYear { get; set; }
public int DepartmentID { get; set; }
}
employee update(auto generated by entity model code generation from db):
public void UpdateEmployee(int id, Employee employee)
{
Employee emp= db.Employee.Where(x => x.ID == id).First();
emp.FirstName = employee.FirstName;
emp.LastName = employee.LastName;
emp.StartWorkYear = employee.StartWorkYear;
emp.DepartmentID = employee.DepartmentID;
db.SaveChanges();
}
employeeshiftdata class (not a db table but still in the models folder):
public class EmployeeShiftData
{
public int ID { get; set; } //EmployeeID
public string FirstName { get; set; }
public string LastName { get; set; }
public int StartWorkYear { get; set; }
public string DepartmentName { get; set; }
public List<Shift> Shifts { get; set; }
}
employeeshift GET part of the controller:
[EnableCors(origins: "*", headers: "*", methods: "*")]
public class EmployeeShiftDataController : ApiController
{
private static EmployeeShiftDataBL empShiftDataBL = new EmployeeShiftDataBL();
// GET: api/EmployeeShiftData
public IEnumerable<EmployeeShiftData> Get(string FirstName = "", string LastName = "", string Department = "")
{
return empShiftDataBL.GetAllEmployeeShiftData(FirstName, LastName, Department);
}
//...
}
Would need to see the code that interacts with the database, especially the code that makes the updates.
If the changes are written with Entity Framework, are the models themselves properly related with navigational properties?
public class Employee
{
public int Id { get; set; }
public string Name { get; set; }
public List<EmployeeShift> EmployeeShifts { get; set; }
// etc.
}
public class EmployeeShift
{
public int Id { get; set; }
public Int EmployeeID { get; set; }
public Employee Employee { get; set; }
// etc.
}
If those are good, and both models are covered by Entity Framework's context tracking, then both should be updated.
I would like to add an object to a related entity without loading them.
I have Company entity defined like this:
public class Company
{
public int ID { get; set; }
public List<Employee> EmployeeList{ get; set; }
}
And Employee entity like this
public class Employee
{
public int ID { get; set; }
public String Name{ get; set; }
}
I want to add an employee to a list placed in that company object without loading all the employees.
I know I can use this expression
Company myCompany= systemBD.Companies.Include("EmployeeList").Find(1) myCompany.EmployeeList.Add(newEmployee)
but I'm afraid that this would consume a lot of time since I have thousands of employees in my database.
Is there a way to add a new employee to an existing company without loading the list of Employees?
I was looking into the Attach method but it does not seem to work.
using (var systemDB = new CompanyDB())
{
Employee employee = new Employee ();
Company companySearch = systemDB.Companies.Where(d => d.Name.Equals("John")).SingleOrDefault();
if (companySearch != null)
{
if (companySearch.EmployeeList != null)
{
systemDB.Companies.Attach(companySearch );
companySearch.EmployeeList.Add(employee);
systemDB.SaveChanges();
}
}
I tried that code but it doesn't work.
Assuming you have your Company and Employee entities defined to have both a navigation property from a Company to the collection of all of its associated Employees and a property from an Employee to its single associated Company, you can accomplish creating a new Employee and associating it with an existing Company from the Employees DB set.
[Table("Company")]
public partial class Company
{
public Company()
{
this.Employees = new HashSet<Employee>();
}
public int Id { get; set; }
[Required]
[StringLength(50)]
public string Name { get; set; }
public virtual ICollection<Employee> Employees { get; set; }
}
[Table("Employee")]
public partial class Employee
{
public int Id { get; set; }
[Required]
[StringLength(50)]
public string Name { get; set; }
public int CompanyId { get; set; }
public virtual Company Company { get; set; }
}
public partial class Database : DbContext
{
public Database()
: base("name=Database")
{
}
public virtual DbSet<Company> Companies { get; set; }
public virtual DbSet<Employee> Employees { get; set; }
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
modelBuilder.Entity<Company>()
.Property(e => e.Name)
.IsUnicode(false);
modelBuilder.Entity<Company>()
.HasMany(e => e.Employees)
.WithRequired(e => e.Company)
.WillCascadeOnDelete(false);
modelBuilder.Entity<Employee>()
.Property(e => e.Name)
.IsUnicode(false);
}
}
Then assuming you already have a Company in the system with an Id of 1, you can do the following:
using (var database = new Database())
{
var company = database.Companies.Find(1);
if (company != null)
{
var employee = new Employee
{
Name = "John Doe",
Company = company
};
database.Employees.Add(employee);
database.SaveChanges();
}
}
OR...if you are sure that Company Id 1 definitely exists...
using (var database = new Database())
{
var employee = new Employee
{
Name = "John Doe",
CompanyId = 1
};
database.Employees.Add(employee);
database.SaveChanges();
}
I think you would need to change your Database Design to accomplish what you want.
Employee table
ID (Primary key)
Name
Company table
ID (Primary key)
Name
EmployeeCompany table
IDCompany (Foreign Key)
IDEmployee (ForeignKey)
This way you will accomplish what you want
is it possible insert some specific fields from one table to a class in mvc?
for example i have tbl_User . can i insert just field "Name" in "MyClass"?
i wanna pass a model(MyClass) to view that contains some fields of tbl_User .
and i used codefirst.
public class MyClass:tbl_User
{
//i mean can i put some fields of tbl_User instead below code .
//but below code insert all fields of tbl_User
public List<tbl_User> tbl_User { get; set; }
}
Yes, you can; see the code below.
// get /users
public ActionResult Index()
{
using (var db = new YourContext())
{
// We just need to show user name and id will be used to perform actions like edit user ETC. So we have created a reduced model named UserIndexModel.
return db.Users.Select(u => new UserIndexModel { Id = u.Id, Name = u.Name}).ToList();
}
}
Model definitions:
public class User
{
public int Id { get; set; }
public string Name { get; set; }
public string HashPassword { get; set; }
public DateTime CreatedOn { get; set; }
}
public class YourContext : DbContext
{
public DbSet<User> Users { get; set; }
}
View model:
public class UserIndexModel
{
public int Id { get; set; }
public string Name { get; set; }
}
Confusing Situation
I have a situation where I have 2 entities where 1 inherits from the other, that need to map to 2 separate tables, but code use should be around the base of the 2 entities.
Details
public class Team
{
public virtual int Id { get; set; }
public virtual ICollection<Employee> Members { get; set; }
}
public class Employee
{
public virtual int Id { get; set; }
public virtual string Name { get; set; }
public virtual ICollection<Team> Teams { get; set; }
}
public class EmployeeInfo : Employee
{
public virtual int Id { get; set; }
public virtual decimal Amount { get; set; }
}
We have an existing database schema where Employee and EmployeeInfo are separate tables with a FK between EmployeeInfo_Id and Employee_Id.
In our system "managers" will be adding Employee's to the system, with a set of private information (more properties than listed above) like pay, and add them to a Team. Other areas of the system will be using the Team or Employee objects for various other things. We would like to have to code super simple if the mapping can be done.
When a manager creates a new employee we would like the code to look something like this:
public void Foo(string name, decimal pay)
{
// create the employee
var employee = new EmployeeInfo();
employee.Name = name;
employee.Pay = pay;
// add him/her to the team
_team.Employees.Add(employee); // the idea being that consumers of the Team entity would not get the separate employee info properties
// save the context
_context.SaveChanges();
}
The end result would be that the EmployeeInfo properties entered into the EmployeeInfo table and the base Employee data is entered into the Employee table and added to the Team via the association table TeamEmployees.
So far I'm trying the current mappings, and I get an invalid column named "Discriminator." When just adding an employee to a team.
public class TeamConfiguration : EntityTypeConfiguration<Team>
{
public TeamConfiguration()
{
ToTable("Team");
HasKey(t => t.Id);
HasMany(t => t.Members).WithMany(m => m.Teams)
.Map(m =>
{
m.MapLeftKey("Team_Id");
m.MapRightKey("Employee_Id");
m.ToTable("TeamEmployees");
});
}
}
public class EmployeeConfiguration : EntityTypeConfiguration<Employee>
{
public EmployeeConfiguration()
{
ToTable("Employee");
ToTable("EmployeeInfo");
HasKey(t => t.Id);
Property(p => p.Name);
HasMany(m => m.Teams)
.WithMany(t => t.Members)
.Map(m =>
{
m.MapLeftKey("Employee_Id");
m.MapRightKey("Team_Id");
m.ToTable("TeamEmployees");
});
}
}
Also, if I take the many-to-many between team and employee out of the mix I get a FK exception on Employee_Id to EmployeeInfo_Id.
Thanks, JR.
Discriminator is a column that's being added to your table when you use Table Per Hierarchy approach.
I think what you're looking for is "Table per Type (TPT)". Decorate your EmployeeInfo class as follows:
[Table("EmployeeInfo")]
public class EmployeeInfo : Employee
Or add below to your OnModelCreating event:
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
...
modelBuilder.Entity<EmployeeInfo>().ToTable("EmployeeInfo");
...
}
Or, create the following class and use it like modelBuilder.Configurations.Add(new EmployeeInfoConfiguration()); in OnModelCreating method:
public class EmployeeInfoConfiguration : EntityTypeConfiguration<EmployeeInfo>
{
public EmployeeInfoConfiguration()
{
ToTable("EmployeeInfo");
}
}
This will cause EF to create EmployeeInfo table with necessary constraints.
Also, it's good to initialize your collections in your objects' constructors to prevent null exception. For example in Team class:
public Team()
{
this.Employees = new HashSet<Employee>();
}
I copied your code exactly, and changed the following parts:
public class Team
{
public Team()
{
this.Members = new HashSet<Employee>();
}
public virtual int Id { get; set; }
public virtual ICollection<Employee> Members { get; set; }
}
public class Employee
{
public Employee()
{
this.Teams = new HashSet<Team>();
}
public virtual int Id { get; set; }
public virtual string Name { get; set; }
public virtual ICollection<Team> Teams { get; set; }
}
[Table("EmployeeInfo")]
public class EmployeeInfo : Employee
{
public virtual int Id { get; set; }
public virtual decimal Amount { get; set; }
}
In the DbContext, no changes:
public partial class TestEntities : DbContext
{
public DbSet<Employee> Employees { get; set; }
public DbSet<EmployeeInfo> Employee_Info { get; set; }
public DbSet<Team> Teams { get; set; }
}
and your working Foo method:
public static void Foo(string name, decimal pay)
{
var _team = new Team();
var context = new TestEntities();
context.Teams.Add(_team);
// create the employee
var employee = new EmployeeInfo();
employee.Name = name;
employee.Amount = pay;
context.Employees.Add(employee);
context.SaveChanges();
// add him/her to the team
_team.Members.Add(employee);
// save the context
context.SaveChanges();
}
Finally, remove ToTable("EmployeeInfo"); part from EmployeeConfiguration since you have mentioned this correctly in your mode creating event.
For more info about Table Per Type approach, check out this great article.