Get Value from other Table by ID with Entity Framework - asp.net-core

i have two already existings tables (no foreignkey):
Customer (Id, Name, .....)
Projects (Id, CustomerId, name)
And in my asp.net core application i have two model:
public class Customer {
public int Id { get; set; };
public String Name { get; set; };
}
public class Project {
public int Id { get; set; };
public Customer Customer{ get; set; };
public String Name{ get; set; };
}
And the datacontext classes for this
public class CustomerContext: DbContext
{
public CustomerContext(DbContextOptions<CustomerContext> options) : base(options)
{
}
public DbSet<CustomerContext> Customer { get; set; }
}
public class ProjectContext: DbContext
{
public ProjectContext(DbContextOptions<ProjectContext> options) : base(options)
{
}
public DbSet<ProjectContext> Project{ get; set; }
}
But i cant find out how to fetch the Customer object in the Projectclass by the customerId
Can someone help me please? Thank you
Edit: Now i change my Model Classes like in the answer below
but with the following i get an SQL Exception while loading the page
SqlException: Invalid object name 'Customer'.
projectList = await (from project in _context.Project
join customer in _customerContext.Customer on project.CustomerId equals customer.Id into tmp
from m in tmp.DefaultIfEmpty()
select new Project
{
Id = sollIst.Id,
CustomerId = sollIst.CustomerId,
Customer = m,
Name = sollIst.Name,
}
).ToListAsync();

Update your model classes as below:
public class Customer {
public int Id { get; set; };
public String Name { get; set; };
}
public class Project {
public int Id { get; set; };
public String Name{ get; set; };
public int CustomerID { get; set; }
[ForeignKey("CustomerID")]
public Customer Customer{ get; set; };
}
Merger both DbContext into one.
public class ProjectContext: DbContext
{
public ProjectContext(DbContextOptions<ProjectContext> options) : base(options)
{
}
public DbSet<Project> Projects { get; set; }
public DbSet<Customer> Customers { get; set; }
}
Then execute
projectList = await (from project in _context.Project
join customer in _context.Customer on project.CustomerId equals customer.Id into tmp
from m in tmp.DefaultIfEmpty()
select new Project
{
Id = sollIst.Id,
CustomerId = sollIst.CustomerId,
Customer = m,
Name = sollIst.Name,
}
).ToListAsync();
I hope following links will help you to know how to join two tables across different database.
Joining tables from two databases using entity framework.
Entity framework join across two databases

You will have to create a property in Project class that represent the "foreign key".
Lets say in Project table in the database the "foreign key" is CustomerID, add this to Project class:
public int CustomerID { get; set; }
Then add the ForeignKey attribute to the Customer property:
[ForeignKey("CustomerID")]
public Customer Customer { get; set; }

First, your model classes should be as follows:
public class Customer {
public int Id { get; set; };
public string Name { get; set; };
}
public class Project {
public int Id { get; set; };
[ForeignKey("Customer")]
public int CustomerId{ get; set; };
public string Name{ get; set; };
public Customer Customer { get; set; };
}
Then your DbContext classes should be as follows:
public class CustomerContext: DbContext
{
public CustomerContext(DbContextOptions<CustomerContext> options) : base(options)
{
}
public DbSet<Customer> Customers { get; set; }
}
public class ProjectContext: DbContext
{
public ProjectContext(DbContextOptions<ProjectContext> options) : base(options)
{
}
public DbSet<Project> Projects { get; set; }
}
Now you can use Entity Framework Core and LINQ query to fetch your desired data.

Related

Set one to one and one to many both relationships among two tables

I have two tables called employee and team. I want to set relationships among the tables as follows.
employee belongs to a team. (1: m relationship).
team has a team leader (team leader is also an employee). (1:1 relationship)
employee table
[primary key]
public int RegistrationNumber { get; set; }
public string Name { get; set; }
public string Email { get; set; }
team table
[primary key]
public string Name { get; set; }
public string Description { get; set; }
How can I do it?
Below is a work demo, you can refer to it.
team.cs:
public class team
{
private TeamLeaderDbContext Context { get; set; }
[Key]
public string Name { get; set; }
public string Description { get; set; }
public int? leaderID { get; set; }
public employee leader { get { return Context.employees.Find(leaderID); } }
public ICollection<employee> employees { get; set; }
}
employee.cs:
public class employee
{
[Key]
public int RegistrationNumber { get; set; }
public string Name { get; set; }
public string Email { get; set; }
}
TeamLeaderDbContext:
public class TeamLeaderDbContext : DbContext
{
public TeamLeaderDbContext(DbContextOptions<TeamLeaderDbContext> options)
: base(options)
{
}
public DbSet<team> teams { get; set; }
public DbSet<employee> employees { get; set; }
}
homecontroller:
public class HomeController : Controller
{
private readonly TeamLeaderDbContext _context;
public HomeController(TeamLeaderDbContext context)
{
_context = context;
}
public IActionResult Index()
{
var team = _context.teams.Include(c => c.employees).ToList();
return View();
}
}
result:

How to get the discriminator from the id of an entitie?

I would like to now wether my id is an invoice or an individualinvoice
individualinvoice.cs
public class IndividualInvoice : Invoice {
[Required]
public string FirstName { get; set; }
[Required]
public string LastName { get; set; }
}
invoice.cs
public class Invoice {
[Key]
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public string Id { get; set; }
[Required]
public string Company { get; set; }
[Required]
public string Address { get; set; }
[Required]
public int HouseNumber { get; set; }
[Required]
public string Zipcode { get; set; }
[Required]
public string City { get; set; }
[Required]
public string Country { get; set; }
[Required]
public string VATNumber { get; set; }
public Customer Customer { get; set; }
[ForeignKey("Customer")]
[Required]
public string CustomerId { get; set; }
}
gingsengdbcontext.cs
public class GingsengDbContext : IdentityDbContext<GingsengUser> {
public DbSet<Gingseng> Gingsengs { get; set; }
public DbSet<Customer> Customers { get; set; }
public DbSet<Invoice> Invoices { get; set; }
public DbSet<IndividualInvoice> IndividualInvoices { get; set; }
public GingsengDbContext(DbContextOptions<GingsengDbContext> options) : base(options)
{
}
}
And here is my controller where i would like to know from the id if the id corresponds to an individialinvoice or just an invoice? is there any cleaner way than to use singleordefault?
public class InvoicesController : Controller {
private readonly GingsengDbContext context;
private readonly IMapper mapper;
public InvoicesController(GingsengDbContext context, IMapper mapper)
{
this.context = context;
this.mapper = mapper;
}
[HttpGet("{id}")]
public async Task<IActionResult> GetInvoice(string id) {
}
}
Well, the only clean way which works with all EF Core supported inheritance models (currently TPH and TPT) is to use C# is operator. However the classes must not inherit other non abstract class from the same hierarchy like in your example, because IndividualInvoice is a Invoice, hence will be included in DbSet<Invoice> and any query (OfType etc.) which checking for Invoice.
So you can check just for final classes, e.g.
bool isIndividualInvoice = await context.Invoices
.AnyAsync(e => e.Id == id && e is IndividualInvoice);
which btw is the same as
bool isIndividualInvoice = await context.IndividualInvoices
.AnyAsync(e => e.Id == id);
and similar (using Set<IndividualInvoice>() or Set<Invoice>().OfType<IndividualInvoice>).
Another not so clean option which works only for TPH is to retrieve the discriminator property value directly. You have to know its name and type (the defaults are "Discriminator" and string) and use the special EF.Property method similar to this:
var type = await context.Invoices
.Where(e => e.Id == id)
.Select(e => EF.Property<string>(e, "Discriminator")) // <--
.FirstOrDefaultAsync();
// here type will be ether null, "Invoice" or "IndividualInvoice"

Is it possible to link one table to another with entity framework core without FKs?

I have 2 tables company and user. Company will have one created by user and one modified user - these will be admin users. User will belong to one company but one admin user could create or modify multiple companies.
I'm having a hard time using entity framework core in my .net core app to join company and user so when I get a company record I have the created by and modified user information.
My company and user classes look like this:
public class Company
{
[Key]
public Guid Id { get; set; }
public DateTime Created { get; set; }
public Guid Created_By { get; set; }
public virtual ApplicationUser CreatedByUser { get; set; }
public DateTime Modified { get; set; }
public Guid Modified_By { get; set; }
public virtual ApplicationUser ModifiedByUser { get; set; }
public string Company_Name { get; set; }
}
public class ApplicationUser: IdentityUser<Guid>
{
[Column("ID")]
public override Guid Id { get; set; }
[Column("CREATED")]
public DateTime Created { get; set; }
[Column("CREATED_BY")]
public Guid? CreatedBy { get; set; }
[Column("MODIFIED")]
public DateTime Modified { get; set; }
[Column("MODIFIED_BY")]
public Guid? ModifiedBy { get; set; }
[Column("FIRST_NAME")]
public string FirstName { get; set; }
[Column("LAST_NAME")]
public string LastName { get; set; }
[Column("EMAIL")]
public override string Email { get; set; }
[Column("NORMALIZED_EMAIL")]
public override string NormalizedEmail { get; set; }
[Column("EMAIL_CONFIRMED")]
public override bool EmailConfirmed { get; set; }
[Column("USER_NAME")]
public override string UserName { get; set; }
[Column("NORMALIZED_USER_NAME")]
public override string NormalizedUserName { get; set; }
[Column("COMPANY_ID")]
public Guid CompanyId { get; set; }
[Column("PHONE_NUMBER")]
public override string PhoneNumber { get; set; }
[Column("PHONE_NUMBER_CONFIRMED")]
public override bool PhoneNumberConfirmed { get; set; }
[Column("TITLE")]
public string Title { get; set; }
[Column("ACTIVE")]
public bool Active { get; set; }
[Column("ROLE_ID")]
public int UserRoleId { get; set; }
[Column("TYPE_ID")]
public int TypeId { get; set; }
[Column("PASSWORD_HASH")]
public override string PasswordHash { get; set; }
[Column("SECURITY_STAMP")]
public override string SecurityStamp { get; set; }
[Column("CONCURRENCY_STAMP")]
public override string ConcurrencyStamp { get; set; }
[Column("TWO_FACTOR_ENABLED")]
public override bool TwoFactorEnabled { get; set; }
[Column("LOCKOUT_END")]
public override DateTimeOffset? LockoutEnd { get; set; }
[Column("LOCKOUT_ENABLED")]
public override bool LockoutEnabled { get; set; }
[Column("ACCESS_FAILED_COUNT")]
public override int AccessFailedCount { get; set; }
}
My DbContext class looks like this:
public class DbContext : IdentityDbContext<ApplicationUser, ApplicationRole, Guid, ApplicationUserClaim, ApplicationUserRole, IdentityUserLogin<Guid>, IdentityRoleClaim<Guid>, IdentityUserToken<Guid>>
{
public DbContext(DbContextOptions<DbContext> options) : base(options)
{
}
public virtual DbSet<Company> Companies { get; set; }
protected override void OnModelCreating(ModelBuilder builder)
{
base.OnModelCreating(builder);
builder.Entity<Company>().ToTable("COMPANY").Property<Guid>("Created_By");
builder.Entity<Company>().HasOne(x => x.CreatedByUser).WithOne().HasForeignKey("Created_By");
builder.Entity<ApplicationUser>().ToTable("USER");
builder.Entity<ApplicationUser>().HasKey(x => x.Id);
builder.Entity<ApplicationUserClaim>().ToTable("USER_CLAIMS");
builder.Entity<ApplicationRole>().ToTable("IDENTITY_ROLES");
builder.Entity<IdentityUserRole<Guid>>().HasKey(p => new { p.UserId, p.RoleId });
builder.Entity<ApplicationUserRole>().ToTable("IDENTITY_USER_ROLES");
}
}
And I was trying to get companies like this:
public async Task<List<Company>> GetAllCompanies()
{
return await _locationDbContext.Companies.ToListAsync();
}
Currently I am getting this error:
System.InvalidOperationException: 'You are configuring a relationship between 'Company' and 'ApplicationUser' but have specified a foreign key on 'Created_By'. The foreign key must be defined on a type that is part of the relationship.'
Is there an easier way to do this? Really all I want is the username of the user that created of modified the company record? If I was doing this with just sql I would just use a basic Join but Im not sure how to do that with entity framework. Worst case I would just get all the companies and then loop through doing a select on the user table where ID = Created_By
As #IvanStoev noticed without FKs it is not possible. But you can still use EF to join 2 tables.
In your case you have to unmap user from company and maybe it is a good idea to make user Guid nullable:
public class Company
{
[Key]
public Guid Id { get; set; }
public string Company_Name { get; set; }
public DateTime Created { get; set; }
public DateTime Modified { get; set; }
public Guid Created_By { get; set; }
public Guid Modified_By { get; set; }
[NotMapped]
public ApplicationUser CreatedByUser { get; set; }
// or better
[NotMapped]
public string CreatedByUser { get; set; }
[NotMapped]
public ApplicationUser ModifiedByUser { get; set; }
//or better
[NotMapped]
public string ModifiedByUser { get; set; }
}
and remove
builder.Entity<Company>().HasOne(x => x.CreatedByUser).WithOne().HasForeignKey("Created_By");
you still can join them like this
var companies= (
from c in _locationDbContext.Companies
join uc in _locationDbContext.ApplicatonUser on c.Created_By equals uc.Id
join um in _locationDbContext.ApplicatonUser on c.Modified_By equals um.Id
select new Company
{
....
CreatedByUser = uc,
ModifiedByUser = um
// or usually
CreatedByUser = uc.FirstName + " " + uc.LastName,
ModifiedByUser = um.FirstName + " " + um.LastName,
}).ToList();
This is one way of accomplishing this.
This is a simplified version of class Company:
public class Company
{
public int CompanyID { get; set; }
public string Name { get; set; }
//
// Relations
public string CreatorID { get; set; }
public ApplicationUser Creator { get; set; }
public string LastModifiedByID { get; set; }
public ApplicationUser LastModifiedBy { get; set; }
}
CreatorID and LastModifiedByID will be used by EF for determining the FK's. These are the fields you have to work with when updating the Db, not Creator and LastModfiedBy, yet you can also do it but with more lines of code.
Now, you can add companies the usual way, this code searches for the company, and creates one if not found, just sample code, not serious, really:
var companyName = "My company";
var company = context.Companies.Include(c => c.Creator).Include(c => c.Creator).FirstOrDefault(c => c.Name == companyName);
if (company == null)
{
company = new Company
{
Name = "My company",
CreatorID = user.Id,
LastModifiedByID = user.Id
};
context.Companies.Add(company);
context.SaveChanges();
}
And retrieving the companies with all relations filled is a matter or using calls to Include() and ThenInclude(), like here:
var companies = context.Companies.Include(c => c.Creator).Include(c => c.Creator).ToList();
I omitted all filtering logic for the sake of simplicity.

Querying a table that relates to multiple entity types

So currently I have an application model of:
Note -> Thing
(A note can relate to many things)
A thing can be numerous entities (for this example lets use Computer & Car)
e.g
Note -> Computer
-> Car
So right now, I have the schema of
Note -> ComputerNote -> Computer
Note -> CarNote -> Car
The problem is that because the entity links are in separate tables, it requires a new query to be written rather than just using filtering in the WHERE clause.
Ideally it would be nice to have an EntityId & EntityTypeId column & on the Note table that would hold the primary key of the related entity and the type of the entity. Thus application logic could look for all Car notes where the car is x without a separate query for each type, but.. this would mean I lose referential integrity. Is there a better way, or is what I have suggested an acceptable design?
Entity Framework Model's:
public partial class Note
{
public Note()
{
NoteComputer = new HashSet<NoteComputer>();
NoteCar = new HashSet<NoteCar>();
NoteThing = new HashSet<NoteThing>();
}
public int Id { get; set; }
public string Value { get; set; }
public string CreatedByUserId { get; set; }
public DateTime CreatedDateTime { get; set; }
public ICollection<NoteComputer> NoteComputer { get; set; }
public ICollection<NoteCar> NoteCar { get; set; }
public ICollection<NoteThing> NoteThing { get; set; }
}
public partial class NoteCar
{
public int Id { get; set; }
public int NoteId { get; set; }
public int CarId { get; set; }
public Car Car { get; set; }
public Note Note { get; set; }
}
public partial class NoteComputer
{
public int Id { get; set; }
public int NoteId { get; set; }
public int ComputerId { get; set; }
public Computer Computer { get; set; }
public Note Note { get; set; }
}
public partial class NoteThing
{
public int Id { get; set; }
public int NoteId { get; set; }
public int ThingId { get; set; }
public Thing Thing { get; set; }
public Note Note { get; set; }
}
As there seems to be no nice way to handle this at the database level, I've found it best to handle this at the application level using the concrete type database schema, to produce dynamic joins.
Example in Entity Framework Core:
public GenericEntityProvider
{
private readonly IEnumerable<IEntityProvider> _entityProviders;
private readonly DatabaseContext _context;
public GenericEntityProvider(IEnumerable<IEntityProvider> entityProviders, DatabaseContext context)
{
_entityProviders = entityProviders;
_context = context;
}
public IEnumerable<Note> Get(Type type, int id) {
var provider = _entityProviders.GetPredicate(type, id);
return _context.Notes.Where(provider);
}
}
public CarNoteProvider : IEntityProvider
{
public Expression<Func<Note, bool>> GetPredicate(Type type, int id)
{
return x => x.CarNote.Any(cn => cn.CarId == id);
}
}

Automapper generates left join in SQL

I'm using Automapper in my .NET Core 2.2 back-end service. I use ProjectTo method to generate my result.
This is my Product class:
public class Product
{
public DateTime? SellEndDate { get; set; }
public string Name { get; set; }
public bool AllowToShow { get; set; }
public Category Category { get; set; }
public Guid CategoryId { get; set; }
public Brand Brand { get; set; }
public Guid BrandId { get; set; }
}
And it is the result class:
public class Dto
{
public string Name { get; set; }
public CategoryDto Category { get; set;}
public BrandDto Brand { get; set; }
}
The configuration in the profile is a normal one. The generated SQL statement for category table has Inner Join, but for brand table it uses a Left Join.
Why is that?