I'm using Entity framework using code first. I have 3 tables in database:
Place
Profil
PlaceMember
It's a many to many relation. A place can have many Profils as members and a profil can be a member to many Places. The PlaceMember is the relational table.
Code of PlaceDTO:
[Table("Place")]
public partial class PlaceDTO
{
public PlaceDTO()
{
Members = new HashSet<ProfilDTO>();
}
public int id { get; set; }
[StringLength(100)]
public string description { get; set; }
public DateTime dateOut { get; set; }
public DateTime createDate { get; set; }
public int? canceled { get; set; }
public int profilId { get; set; }
public int addressId { get; set; }
public virtual AddressDTO Address { get; set; }
public virtual ProfilDTO Profil { get; set; }
public virtual ICollection<ProfilDTO> Members { get; set; }
}
Code of Profil class:
public partial class ProfilDTO
{
public ProfilDTO()
{
Devices = new HashSet<DeviceDTO>();
Notifications = new HashSet<NotificationDTO>();
Places = new HashSet<PlaceDTO>();
}
public int id { get; set; }
[Required]
[StringLength(50)]
public string pseudo { get; set; }
[Required]
[StringLength(50)]
public string firstname { get; set; }
[Required]
[StringLength(50)]
public string lastname { get; set; }
[Required]
[StringLength(15)]
public string phone { get; set; }
[Required]
[StringLength(50)]
public string emailAdresse { get; set; }
[Required]
[StringLength(150)]
public string password { get; set; }
public DateTime lastUpdatePassword { get; set; }
public DateTime lastUpdateProfil { get; set; }
public DateTime createDate { get; set; }
public byte[] picture { get; set; }
public virtual ICollection<PlaceDTO> Places { get; set; }
}
Code of PlaceMember class:
[Table("PlaceMember")]
public partial class PlaceMemberDTO
{
[Key]
[Column(Order = 0)]
[DatabaseGenerated(DatabaseGeneratedOption.None)]
public int placeId { get; set; }
[Key]
[Column(Order = 1)]
[DatabaseGenerated(DatabaseGeneratedOption.None)]
public int invitedId { get; set; }
}
In database, the placeId and invitedId are both identity key of the table.
When I'm trying to add a new place to the Place table, I'm getting that PlaceDTO_id is not a valid column name despite the fact that the place table in database has an column named id and is the identity column.
context.Address.Attach(place.Address);
context.Address.Add(place.Address);
place.addressId = place.Address.id;
context.Places.Attach(place);
context.Places.Add(place);
context.SaveChanges();
The place object, put in param, contains 3 members that exists in Profil table.
When I remove the Members field from the PlaceDTO table, the place is inserted but nothing added to the PlaceMember table.
What's wrong ?
Related
I have an entity that has a composed primary key. I was wondering how could I populate this entity: do I need to explicitly populate the navigation properties with references or just the ID's?
I am using EF core 7
The entity is the following:
[PrimaryKey(nameof(ClassifierId), nameof(SliceId), nameof(VulnerabilityClassId), nameof(Date))]
public class Classification
{
[Column(name: "classifier_id")]
public int ClassifierId { get; set; }
public Classifier Classifier { get; set; } = null!;
[Column(name: "slice_id")]
public int SliceId { get; set; }
public Slice Slice { get; set; } = null!;
[Column(name: "vuln_class_id")]
public int VulnerabilityClassId { get; set; }
public VulnerabilityClass VulnerabilityClass { get; set; } = null!;
[Column(name: "date")]
public DateTime Date { get; set; }
public ICollection<VulnerabilityClassification> VulnerabilityClassifications { get; set; }
public SliceVulnLabel SliceVulnLabel { get; set; }
[Required]
[Column(name: "label")]
public int Label { get; set; }
[Column(name: "comment")]
public string Comment { get; set; }
[Column(name: "is_active")]
[Required]
public int IsActive { get; set; }
[Column(name: "confidence_degree")]
[Required]
public int ConfidenceDegree { get; set; }
}
I have a case scenario with two tables References and Products alreading containing many entries which can be dynamically related on demand.
public class Reference
{
[Key]
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public Guid ReferenceId { get; set; }
public string Name { get; set; }
public string Status { get; set; }
public virtual ICollection<Product> ManyProducts { get; set; }
public Reference() {}
}
public class Product
{
[Key]
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public Guid ProductId { get; set; }
public string Name { get; set; }
public decimal Price { get; set; }
[ForeignKey("Reference")]
public Guid ReferenceId { get; set; }
public virtual Reference OneReference { get; set; }
public Product() {}
}
When a user ask to link a reference to a product I simply do :
product.ReferenceId = reference.ReferenceId ;
await context.SaveChangesAsync() ;
The entry in Products table is updated correctly, but when I try to access a reference's related data, it does not retrieve any ?? After eager loading :
var reference = await context.References
.Include(r => r.ManyProducts)
.SingleAsync(r => r.ReferenceId == referenceId) ;
or explicit loading :
var reference = await context.References.FindAsync(referenceId) ;
await context.Entry(reference).Collection(s => s.ManyProducts).LoadAsync() ;
reference.ManyProducts is empty. So I have to do something like this :
var reference = await context.References.FindAsync(referenceId) ;
var products = await context.Products.Where(l => l.ReferenceId == referenceId).ToListAsync() ;
result.ManyProducts = products ;
which works fine, but I would like to understand why ?
I´m using DataAnnotation
Sample
public class spread
{
[Key]
public int spreadid { get; set; }
[Required]
public DateTime insertdate { get; set; }
[Required]
public int exchangeid { get; set; }
[ForeignKey("exchangeid"), Display(Name = "Exchange origem")]
public virtual exchange exchange { get; set; } // One to one
[ForeignKey("spreadid")]
public virtual ICollection<spreadhelper> spreadhelper { get; set; } // One to many
}
public class spreadhelper
{
[Key]
public int spreadhelperid { get; set; }
[Required]
public int spreadid { get; set; }
[Required]
public int exchangeid { get; set; }
[ForeignKey("exchangeid"), Display(Name = "Exchange")] // One to one
public virtual exchange exchange { get; set; }
[Required, Range(0, 200)]
public decimal spreadvalue { get; set; }
}
one to one - sample
public class exchange
{
[Key]
public int exchangeid { get; set; }
[Required]
public DateTime insertdate { get; set; }
[Required, MaxLength(50)]
public string name { get; set; }
[MaxLength(128)]
public string token { get; set; }
}
One to many sample
For example, I have the following infrastructure class:
[Table("GeoHistory")]
public partial class GeoHistory
{
public int Id { get; set; }
public int CompanyId { get; set; }
public int DriverId { get; set; }
[StringLength(50)]
public string Name { get; set; }
public string Description { get; set; }
[StringLength(100)]
public string GeofenceLocation { get; set; }
[StringLength(100)]
public string GeofencePrevious { get; set; }
[StringLength(20)]
public string StateLocation { get; set; }
[StringLength(20)]
public string StatePrevious { get; set; }
public DateTime? DateTime { get; set; }
[StringLength(5)]
public string Heading { get; set; }
public decimal? Speed { get; set; }
[StringLength(50)]
public string Status { get; set; }
}
and the following View class (let's forget about domain layer):
public class GeoHistoryViewModel
{
public int Id { get; set; }
public int? CompanyId { get; set; }
public int? DriverId { get; set; }
[StringLength(50)]
public string Name { get; set; }
public string Description { get; set; }
}
as we can see, we edit only part of field list.
Now, we want to update data in DB. Of course, we can write like:
Infrastructure.Main.GeoHistory geoHistory = db.GeoHistories.Find(id);
geoHistory.CompanyId = model.CompanyId;
geoHistory.DriverId = model.DriverId;
geoHistory.Name = model.Name;
........
db.SaveChanges();
It works. But I want to use Automapper. And if I try to do the following:
Infrastructure.Main.GeoHistory geoHistory = mapper.Map<Infrastructure.Main.GeoHistory>(model);
db.GeoHistories.Attach(geoHistory);
db.Entry(geoHistory).State = EntityState.Modified;
db.SaveChanges();
It works, but of course remove values of fields, which are not exist in View Model, but exist in infrastructure class. How to use automapper, but don't lost these fields?
I will appreciate if somebody can tell me why entity framework is not creating join table for following model. It is creating table for type and feature but not the table that will join them.
public class DeviceType
{
[Display(Name = "ID")]
public int DeviceTypeID { get; set; }
public string Name { get; set; }
public string Description { get; set; }
public IEnumerable<DeviceFeature> DeviceFeatures { get; set; }
}
public class DeviceFeature
{
[Display(Name = "ID")]
public int DeviceFeatureID { get; set; }
[Required]
public string Name { get; set; }
public string Description { get; set; }
public IEnumerable<DeviceType> DeviceTypes { get; set; }
}
public class DeviceFeatureView
{
public virtual IEnumerable<DeviceType> DeviceTypes { get; set; }
public virtual IEnumerable<DeviceFeature> DeviceFeatures { get; set;
}
You do not need the bridge to create a many-to-many relationship. EF will figure it out. Change the type of the navigation properties from IEnumerable to ICollection like this:
public class DeviceType
{
public DeviceType()
{
this.DeviceFeatures = new HashSet<DeviceFeature>();
}
[Display(Name = "ID")]
public int DeviceTypeID { get; set; }
public string Name { get; set; }
public string Description { get; set; }
public ICollection<DeviceFeature> DeviceFeatures { get; set; }
}
public class DeviceFeature
{
public DeviceFeature()
{
this.DeviceTypes = new HashSet<DeviceType>();
}
[Display(Name = "ID")]
public int DeviceFeatureID { get; set; }
[Required]
public string Name { get; set; }
public string Description { get; set; }
public ICollection<DeviceType> DeviceTypes { get; set; }
}
More about it here.
I am trying to add an entry to a table that holds a users browsing history information. However, when trying to save the addition an SqlException is thrown:
Cannot insert duplicate key row in object 'dbo.AspNetUsers' with
unique index 'UserNameIndex'. The duplicate key value is
(exampleUserName). The statement has been terminated.
A user is has many browsing histories but a browsing history can only be attached to one user so there is a user as part of the BrowsingHistory DataModel:
namespace DataModels
{
[Table("BrowsingHistory")]
public class BrowsingHistory
{
[Key]
public int BrowsingHistoryId { get; set; }
public int ProductId { get; set; }
public System.DateTime DateTime { get; set; }
public int DeviceId { get; set; }
public int UserId { get; set; }
public virtual AspNetUsers User { get; set; }
public virtual Device Device { get; set; }
public virtual Product Product { get; set; }
}
}
It is to note that I am using the Microsoft Identity classes for my authentication. The user class looks as follows:
namespace DataModels
{
using System;
using System.Collections.Generic;
[Table("AspNetUsers")]
public class AspNetUsers
{
public AspNetUsers()
{
BrowsingHistories = new HashSet<BrowsingHistory>();
Orders = new HashSet<Order>();
AspNetUserClaims = new HashSet<AspNetUserClaims>();
AspNetRoles = new HashSet<AspNetRoles>();
}
[Key]
public int Id { get; set; }
public string Email { get; set; }
public bool EmailConfirmed { get; set; }
public string PasswordHash { get; set; }
public string SecurityStamp { get; set; }
public string PhoneNumber { get; set; }
public bool PhoneNumberConfirmed { get; set; }
public bool TwoFactorEnabled { get; set; }
public DateTime? LockoutEndDateUtc { get; set; }
public bool LockoutEnabled { get; set; }
public int AccessFailedCount { get; set; }
public string UserName { get; set; }
public string HouseName { get; set; }
public string AddressLine1 { get; set; }
public string AddressLine2 { get; set; }
public string AddressLine3 { get; set; }
public string Town { get; set; }
public string County { get; set; }
public string Postcode { get; set; }
public string ContactNumber { get; set; }
public virtual ICollection<BrowsingHistory> BrowsingHistories { get; set; }
public virtual ICollection<Order> Orders { get; set; }
public virtual ShoppingCart ShoppingCart { get; set; }
public virtual ICollection<AspNetUserClaims> AspNetUserClaims { get; set; }
public virtual ICollection<AspNetRoles> AspNetRoles { get; set; }
}
}
The error occurs when trying to save the addition in the repository. On the _context.SaveChanges() line the method below.
public void CreateBrowsingHistoryEntry(BrowsingHistory bhe)
{
_context.BrowsingHistory.Add(bhe);
_context.SaveChanges();
}
Any help with this issue would be greatly appreciated.