How to perform a lookup of a navigation property in EF Core 5 as part of a Create Dto - entity-framework-core

I am new to [tag:Entity Framework] and having difficulties finding information on how to map [tag:navigation properties] in my Create Dto.
I have a JobCreateDto that needs to accept an AddressId and map from the Address database.
I have the following [tag:Fluent API] code but when I send a post request it asks for the other required properties for the address instead of just looking up the existing address by ID. It appears to be trying to create a new address.
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
base.OnModelCreating(modelBuilder);
modelBuilder
.Entity<Job>()
.HasOne(p => p.Address);
}
Here is my HttpPost controller method:
[HttpPost]
public ActionResult<JobReadDto> CreateJob(JobCreateDto JobCreateDto)
{
var JobModel = _mapper.Map<Job>(JobCreateDto);
_repository.CreateJob(JobModel);
_repository.SaveChanges();
var JobReadDto = _mapper.Map<JobReadDto>(JobModel);
return CreatedAtRoute("GetJobById", new {Id = JobReadDto.Id}, JobReadDto);
}
The Create Dto is quite long but here is the navigation property
public virtual Address Address { get; set; }
Any help is appreciated. I remember doing this in a tutorial on Youtube but it was 8 hours into it and I'm not able to find it or a similar tutorial.
Thanks
RyanS
I tried setting up the relationship using Fluent API in the OnModelCreating method of the DbContext but it appears to be trying to create a new address.
Edit to include Dto and Job class
JobCreateDto
using System.ComponentModel.DataAnnotations;
using TreffLandscapingDotNetApp.Models;
namespace TreffLandscapingDotNetApp.Dtos
{
public class JobCreateDto
{
public DateTime DateOfWork { get; set; }
public long Measure { get; set; }
public DateTime StartTime { get; set; }
public string StartLoc { get; set; }
public DateTime EndTime { get; set; }
public string EndLoc { get; set; }
public double startLatitude { get; set; }
public double startLongitude { get; set; }
public double endLatitude { get; set; }
public double endLongitude { get; set; }
public Boolean wasSaltUsed { get; set; }
public Boolean wasBinRefilled { get; set; }
public double TotalTime { get; set; }
public double Subtotal { get; set; }
public double Tax { get; set; }
public double Total { get; set; }
public JobStatus Status { get; set; }
public virtual Address Address { get; set; }
public Agreement Agreement { get; set; }
public int ServiceID { get; set; }
public int ResourceID { get; set; }
}
}
Job class
using System.ComponentModel.DataAnnotations;
namespace TreffLandscapingDotNetApp.Models
{
public struct Coords
{
public double x, y;
public Coords(double p1, double p2)
{
x = p1;
y = p2;
}
}
public enum JobStatus
{
New,
Scheduled,
Closed,
Completed
}
public class Job
{
[Key]
[Required]
public int Id { get; set; }
public DateTime DateOfWork { get; set; }
public long Measure { get; set; }
public DateTime StartTime { get; set; }
public string StartLoc { get; set; }
public double startLatitude { get; set; }
public double startLongitude { get; set; }
public DateTime EndTime { get; set; }
public string EndLoc { get; set; }
public double endLatitude { get; set; }
public double endLongitude { get; set; }
public Boolean wasSaltUsed { get; set; }
public Boolean wasBinRefilled { get; set; }
public double TotalTime { get; set; }
public double Subtotal { get; set; }
public double Tax { get; set; }
public double Total { get; set; }
public JobStatus Status { get; set; }
public virtual Address Address { get; set; }
public Agreement Agreement { get; set; }
public int ServiceID { get; set; }
public int ResourceID { get; set; }
}
}

Related

Accessing a count of a child's child properties

I have a three classes District, PostalCode and Premise. District contains a virtual list of postalcodes and the postal code class contains a virtual list of premises, from within the district controller is there any was of calculating the number of premises which are in the district, classes as follows:
public class District
{
[Key]
public int DistrictID { get; set; }
public string Name { get; set; }
public int Households { get; set; }
public int Population { get; set; }
public virtual List<PostalCode> PostalCodes { get; set; }
}
public class PostalCode
{
[Key]
public int PostalCodeID { get; set; }
public string FullPostcode { get; set; }
public bool InUse { get; set; }
public decimal Latitude { get; set; }
public decimal Longitude { get; set; }
public int Easting { get; set; }
public int Northing { get; set; }
public string GridReference { get; set; }
public string Ward { get; set; }
public string Parish { get; set; }
[Display(Name = "Introduced")]
[DataType(DataType.Date)]
[DisplayFormat(DataFormatString = "{0:yyyy-MM-dd}", ApplyFormatInEditMode = true)]
public DateTime Introduced { get; set; }
[Display(Name = "Terminated")]
[DataType(DataType.Date)]
[DisplayFormat(DataFormatString = "{0:yyyy-MM-dd}", ApplyFormatInEditMode = true)]
public DateTime? Terminated { get; set; }
public int Altitude { get; set; }
public string Country { get; set; }
[Display(Name = "Last Updated")]
[DataType(DataType.Date)]
[DisplayFormat(DataFormatString = "{0:yyyy-MM-dd}", ApplyFormatInEditMode = true)]
public DateTime? LastUpdated { get; set; }
public string Quality { get; set; }
public string LSOACode { get; set; }
public bool Processed { get; set; }
[Display(Name = "Last Visited")]
[DataType(DataType.Date)]
[DisplayFormat(DataFormatString = "{0:yyyy-MM-dd}", ApplyFormatInEditMode = true)]
public DateTime? LastVisited { get; set; }
public string SalesRep { get; set; }
public virtual List<Premise> Premises { get; set; }
public int? DistrictID { get; set; }
public virtual District District { get; set; }
}
public class Premise
{
[Key]
public int PremiseID { get; set; }
public string MPRN { get; set; }
public string MeterPointAddress { get; set; }
public string DUoSGroup { get; set; }
public string MeterConfigurationCode { get; set; }
public string MeterPointStatus { get; set; }
public int PostalCodeID { get; set; }
public virtual PostalCode PostalCode { get; set; }
public bool Live { get; set; }
public bool Pending { get; set; }
}
I am able to access the list of postal codes within the view of the district controller by using the following code:
#item.PostalCodes.Count()
I thought that I may have been able to use #item.PostalCodes.All().Premises.Count() or some variation of that but this is not being allowed by the compiler, is there any way that this can be accessing the third level premises class from within the district controller?
You can use Include and 2 foreach loops for this. Take a look at this article
public class Customer
{
public int CustomerID { get; set; }
public string Name { get; set; }
public virtual List<Invoice> Invoices { get; set; }
}
public class Invoice
{
public int InvoiceID { get; set; }
public DateTime Date { get; set; }
public int CustomerID { get; set; }
public virtual Customer Customer { get; set; }
public virtual ICollection<Item> Items { get; set; }
}
public class Item
{
public int ItemID { get; set; }
public string Name { get; set; }
public int InvoiceID { get; set; }
public virtual Invoice Invoice { get; set; }
}
And get data:
using (var context = new MyContext())
{
var list = context.Customers.ToList();
foreach (var customer in list)
{
Console.WriteLine("Customer Name: {0}", customer.Name);
foreach (var customerInvoice in customer.Invoices)
{
var items=customerInvoice.Items.Tolist();
}
}
}
Create a partial class District and add a couple field to this partial class:
public partial class District
{
[NotMapped]
public int PostaCodeCount
[NotMapped]
public int PremissesCount
}
Try this query inside of your controller:
var rowDistricts=db.Districts
.Include(pc=>pc.PostalCodes)
.ThenInclude(pr => pr.Premisses)
.ToList();
var districts= new List<District>();
foreach (var item in rowDistricts)
{
item.PostalCodeCount=item.PostalCodes.Count();
item.PremissesCount=item.Premisses.Count();
item.Premisses=null;
item.PostalCodes=null;
districts.Add(item);
}
return View(districts);

Error in creating a controller file

I am using Entity Framework. I have tried everything, searching and adding keys but Ienter image description here cannot understand what the problem is and how to resolve it.
public class Reservation
{
[Key]
public int BookingID { get; set; }
public int CustomerID { get; set; }
public int RoomID { get; set; }
public string BookingDate { get; set; }
public int Check_In { get; set; }
public int Check_Out { get; set; }
public int Adults { get; set; }
public int Children { get; set; }
public int NoOfNights { get; set; }
[ForeignKey("RoomID")]
public virtual Room Rooms { get; set; }
[ForeignKey("CustomerID")]
public virtual CustomerDetails CustomerDetail { get; set; }
public virtual ICollection<Payment> Payment { get; set; }
}
public class CustomerDetails
{
[Key]
public int CustomerID { get; set; }
public string FirstName { get; set; }
public string LastName { get; set; }
public int PostCode { get; set; }
public string State { get; set; }
public int PhoneNumber { get; set; }
public string Email { get; set; }
public string Password { get; set; }
public virtual ICollection<Reservation> Reservations { get; set; }
}
enter image description here
All tables need a primary key or you can't use Entity Framework.

Including multiple child layers in LINQ to SQL

I have a data model which has three layers as follows:
Market Message
MessageType598
Metered Generation Information
The classes are as follows:
[Table("MarketMessage")]
public partial class MarketMessage
{
public MarketMessage()
{
messageType300 = new HashSet<messageType300>();
messageType300S = new HashSet<messageType300S>();
messageType300W = new HashSet<messageType300W>();
messageType305 = new HashSet<messageType305>();
messageType310 = new HashSet<messageType310>();
messageType310W = new HashSet<messageType310W>();
messageType320 = new HashSet<messageType320>();
messageType320W = new HashSet<messageType320W>();
messageType332 = new HashSet<messageType332>();
messageType332W = new HashSet<messageType332W>();
messageType591 = new HashSet<messageType591>();
messageType594 = new HashSet<messageType594>();
messageType595 = new HashSet<messageType595>();
messageType596 = new HashSet<messageType596>();
messageType597 = new HashSet<messageType597>();
messageType598 = new HashSet<messageType598>();
}
public int MarketMessageID { get; set; }
public DateTime CreatedOn { get; set; }
[Required]
[StringLength(4)]
public string messageType { get; set; }
[Required]
[StringLength(8)]
public string VersionNumber { get; set; }
public DateTime MarketTimestamp { get; set; }
[Required]
[StringLength(35)]
public string TxRefNbr { get; set; }
[Required]
[StringLength(3)]
public string Sender { get; set; }
[Required]
[StringLength(3)]
public string Recipient { get; set; }
[StringLength(10)]
public string alertFlag { get; set; }
[StringLength(50)]
public string fileName { get; set; }
public bool IsDeleted { get; set; }
public virtual ICollection<messageType300> messageType300 { get; set; }
public virtual ICollection<messageType300S> messageType300S { get; set; }
public virtual ICollection<messageType300W> messageType300W { get; set; }
public virtual ICollection<messageType305> messageType305 { get; set; }
public virtual ICollection<messageType310> messageType310 { get; set; }
public virtual ICollection<messageType310W> messageType310W { get; set; }
public virtual ICollection<messageType320> messageType320 { get; set; }
public virtual ICollection<messageType320W> messageType320W { get; set; }
public virtual ICollection<messageType332> messageType332 { get; set; }
public virtual ICollection<messageType332W> messageType332W { get; set; }
public virtual ICollection<messageType591> messageType591 { get; set; }
public virtual ICollection<messageType594> messageType594 { get; set; }
public virtual ICollection<messageType595> messageType595 { get; set; }
public virtual ICollection<messageType596> messageType596 { get; set; }
public virtual ICollection<messageType597> messageType597 { get; set; }
public virtual ICollection<messageType598> messageType598 { get; set; }
}
public partial class messageType598
{
public int messageType598ID { get; set; }
public int MarketMessageID { get; set; }
public DateTime SettlementDate { get; set; }
public int SettlementRunIndicator { get; set; }
[Required]
[StringLength(9)]
public string GenerationUnit { get; set; }
}
[Table("MeteredGenerationInformation")]
public partial class MeteredGenerationInformation
{
[Key]
public int MeteredGenerationInfoID { get; set; }
public DateTime IntervalPeriodTimestamp { get; set; }
public int SettlementInterval { get; set; }
public decimal GenerationUnitMeteredGeneration { get; set; }
public decimal LossAdjustedGenerationUnitMeteredGeneration { get; set; }
public int? MessageType594ID { get; set; }
public int? MessageType598ID { get; set; }
}
I am attempting to join a list of all market messages received of type 598 including the metered generation information but I am unsure how to do this via LINQ to SQL. I have attempted the following:
var list598s = db.MarketMessage.Where(mm => mm.messageType == "598")
.Include(mm => mm.messageType598)
.ToList();
This includes the market messages including the 598 information but how can I include the metered generation information also in this list, I would also ideally like to refine the list based on a date field found within the messageType598 level
As a SQL query I would write the following:
select * from MarketMessage as a
inner join messageType598 as b on a.MarketMessageID = b.MarketMessageID
inner join MeteredGenerationInformation as c on b.messageType598ID = c.messageType598ID
where c.IntervalPeriodTimestamp between '1 aug 2016' and '31 aug 2016'

Getting ObjectContext error even after calling ToList

When calling the method directly below I get a ObjectDisposedException when calling Mapper.Map with the retrieved list.
System.ObjectDisposedException: The ObjectContext instance has been disposed and can no longer be used for operations that require a connection.
public IEnumerable<Models.Provider> Get(string owner)
{
List<Data.Models.Provider> providers;
using (var db = new Data.ProviderDirectoryContext())
{
providers = db.Providers.Where(p => p.Owner.Name == owner).ToList();
}
var dtoProviders = Mapper.Map<List<Data.Models.Provider>, List<Models.Provider>>(providers);
return dtoProviders;
}
I previously had the code like this (below), I wasn't getting an error, but the database was getting pounded when doing the mapping, and it was taking too long. I don't want to hit the database, when doing the mapping.
public IEnumerable<Models.Provider> Get(string owner)
{
using (var db = new Data.ProviderDirectoryContext())
{
var providers = db.Providers.Where(p => p.Owner.Name == owner).ToList();
var dtoProviders = Mapper.Map<List<Data.Models.Provider>, List<Models.Provider>>(providers);
return dtoProviders;
}
}
How can I retrieve all the data before doing the mapping?
Here is the DbContext and the Data.Models.Provider for your reference.
public class ProviderDirectoryContext : DbContext
{
public DbSet<Owner> Owners { get; set; }
public DbSet<Location> Locations { get; set; }
public DbSet<LocationAuditLog> LocationAuditLog { get; set; }
public DbSet<Office> Offices { get; set; }
public DbSet<OfficePhoneNumber> OfficePhoneNumbers { get; set; }
public DbSet<OfficeAuditLog> OfficeAuditLog { get; set; }
public DbSet<OfficeDay> OfficeDays { get; set; }
public DbSet<Provider> Providers { get; set; }
public DbSet<ProviderPhoneNumber> ProviderPhoneNumbers { get; set; }
public DbSet<ProviderAuditLog> ProviderAuditLog { get; set; }
public DbSet<ProviderType> ProviderTypes { get; set; }
public DbSet<ProviderSpecialty> ProviderSpecialties { get; set; }
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
modelBuilder.Conventions.Remove<PluralizingTableNameConvention>();
modelBuilder.Entity<Provider>().HasRequired(cn => cn.Owner).WithMany().WillCascadeOnDelete(false);
modelBuilder.Entity<Office>().HasRequired(cn => cn.Owner).WithMany().WillCascadeOnDelete(false);
}
}
public class Provider
{
public int Id { get; set; }
public int OwnerId { get; set; }
public virtual Owner Owner { get; set; }
public int? ProviderTypeId { get; set; }
public virtual ProviderType ProviderType { get; set; }
public int? ProviderSpecialtyId { get; set; }
public virtual ProviderSpecialty ProviderSpecialty { get; set; }
[Required]
[StringLength(75)]
public string FirstName { get; set; }
[StringLength(75)]
public string MiddleName { get; set; }
[Required]
[StringLength(75)]
public string LastName { get; set; }
[StringLength(100)]
public string EmailAddress { get; set; }
public virtual ICollection<ProviderPhoneNumber> PhoneNumbers { get; set; }
public string Note { get; set; }
public DateTime? InactiveOn { get; set; }
public int OfficeId { get; set; }
public virtual Office Office { get; set; }
public virtual ICollection<ProviderAuditLog> AuditLog { get; set; }
[Required]
public DateTime CreatedOn { get; set; }
[Required]
[StringLength(75)]
public string CreatedBy { get; set; }
[Required]
public DateTime ModifiedOn { get; set; }
[Required]
[StringLength(75)]
public string ModifiedBy { get; set; }
}
Thanks for the help!
The problem is that the Models.Provider class contains other classes like Models.Office, and Models.PhoneNumbers that were not eagerly loaded by the query. In addition to that, the Models.Provider class needs to be flattened. The Mapper wants to recursively map everything, and it keeps going down to the next class. For example, Provider.Office.Location.Offices.
The solution is to flatten Models.Provider and add .Include() to the query so it eagerly loads the data required.
I'll clean this up a bit more, but this is currently working.
public IEnumerable<Models.Provider> Get(string owner)
{
List<Data.Models.Provider> providers;
using (var db = new Data.ProviderDirectoryContext())
{
providers = db.Providers
.Where(p => p.Owner.Name == owner)
.Include("ProviderType")
.Include("ProviderSpecialty")
.Include("Office")
.Include("PhoneNumbers")
.ToList();
}
var dtoProviders = Mapper.Map<List<Data.Models.Provider>, List<Models.Provider>>(providers);
return dtoProviders;
}
public class Provider
{
public int Id { get; set; }
public int OwnerId { get; set; }
public int OfficeId { get; set; }
public string OfficeName { get; set; }
public int? ProviderTypeId { get; set; }
public string ProviderTypeName { get; set; }
public int? ProviderSpecialtyId { get; set; }
public string ProviderSpecialtyName { get; set; }
public string FirstName { get; set; }
public string MiddleName { get; set; }
public string LastName { get; set; }
public string EmailAddress { get; set; }
public virtual ICollection<PhoneNumber> PhoneNumbers { get; set; }
public string Note { get; set; }
public DateTime? InactiveOn { get; set; }
public DateTime CreatedOn { get; set; }
public string CreatedBy { get; set; }
public DateTime ModifiedOn { get; set; }
public string ModifiedBy { get; set; }
}
I am not sure how much this will help with performance but declaring the variable you don't want to dispose outside the using statement should fix your dispose exception.
public IEnumerable<Models.Provider> Get(string owner)
{
IEnumerable<Models.Provider> dtoProviders;
using (var db = new Data.ProviderDirectoryContext())
{
List<Data.Models.Provider> providers = db.Providers.Where(p => p.Owner.Name == owner).ToList();
dtoProviders = Mapper.Map<List<Data.Models.Provider>, List<Models.Provider>>(providers);
}
return dtoProviders;
}

Entity Framework [Key] tag not being recognised

I am getting warning errors of no key having been defined for each of my class library classes despite the fact that I have the [Key] tag and including the System.ComponentModel.DataAnnotations namespace, here is my context:
Context:
namespace Project.Data
{
public class ProjectContext : DbContext, IProjectContext
{
public ProjectContext(string connString)
: base(connString)
{
this.Configuration.LazyLoadingEnabled = true;
Database.SetInitializer<ProjectContext>(new ProjectInitializer());
this.Database.CreateIfNotExists();
this.Database.Initialize(true);
}
public IDbSet<Article> Article { get; set; }
public IDbSet<Brand> Brand { get; set; }
public IDbSet<Colour> Colour { get; set; }
public IDbSet<Customer> Customer { get; set; }
public IDbSet<CustomerCredit> CustomerCredit { get; set; }
public IDbSet<Delivery> Delivery { get; set; }
public IDbSet<DesignerTicket> DesignerTicket { get; set; }
public IDbSet<EuroRate> EuroRate { get; set; }
public IDbSet<Gift> Gift { get; set; }
public IDbSet<GZero> GZero { get; set; }
public IDbSet<InvoiceStock> InvoiceStock { get; set; }
public IDbSet<PrintOptions> PrintOptions { get; set; }
public IDbSet<Product> Product { get; set; }
public IDbSet<ProductLocation> ProductLocation { get; set; }
public IDbSet<Sale> Sale { get; set; }
public IDbSet<SaleAccount> SaleAccount { get; set; }
public IDbSet<SalesToWeb> SalesToWeb { get; set; }
public IDbSet<Shop> Shop { get; set; }
public IDbSet<Staff> Staff { get; set; }
public IDbSet<Ticket> Ticket { get; set; }
public IDbSet<Transfer> Transfer { get; set; }
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
modelBuilder.Conventions.Remove<PluralizingTableNameConvention>();
}
}
}
Context Interface:
namespace Project.Data
{
public interface IProjectContext
{
IDbSet<Article> Article { get; set; }
IDbSet<Brand> Brand { get; set; }
IDbSet<Colour> Colour { get; set; }
IDbSet<Customer> Customer { get; set; }
IDbSet<CustomerCredit> CustomerCredit { get; set; }
IDbSet<Delivery> Delivery { get; set; }
IDbSet<DesignerTicket> DesignerTicket { get; set; }
IDbSet<EuroRate> EuroRate { get; set; }
IDbSet<Gift> Gift { get; set; }
IDbSet<GZero> GZero { get; set; }
IDbSet<InvoiceStock> InvoiceStock { get; set; }
IDbSet<PrintOptions> PrintOptions { get; set; }
IDbSet<Product> Product { get; set; }
IDbSet<ProductLocation> ProductLocation { get; set; }
IDbSet<Sale> Sale { get; set; }
IDbSet<SaleAccount> SaleAccount { get; set; }
IDbSet<SalesToWeb> SalesToWeb { get; set; }
IDbSet<Shop> Shop { get; set; }
IDbSet<Staff> Staff { get; set; }
IDbSet<Ticket> Ticket { get; set; }
IDbSet<Transfer> Transfer { get; set; }
}
}
[Key] decorated class example:
namespace Project.Data
{
public class Article
{
[Key]
public int ArticleID;
public bool IsCore;
public string Make;
public string Product;
public decimal Sale;
public string Department;
public string Scale;
public string Detail;
public DateTime InDate;
public decimal Reduce;
public bool IsOnSale;
public string VAT;
public bool IsOnWeb;
public string ProductCode;
public string Pick;
public string MemoDetail;
public string LOC;
public string ColourCode;
public bool StatusFlag;
public string Terminal;
}
}
Despite have the [Key] placed on Article I am getting the following message for the article class as shown below and this is repeated for each of the classes:
Project.Data.Article: : EntityType 'Article' has no key defined. Define the key for this EntityType.
Anyone see what I am doing wrong here? would be greatly appreciated
Define the members of your class as public properties as opposed to public variables like you have here, by including {get; set;} at the end of the declaration