EF Navigation Properties and Saves - entity-framework

Suppose I have these two POCOs:
public class Album {
public int ID { get; set; }
[Required]
public string Title { get; set; }
public short Rating { get; set; }
public int ArtistID { get; set; }
[Required]
public virtual Artist Artist { get; set; }
}
public class Artist {
public int ID { get; set; }
[Required]
public string Name { get; set; }
public virtual ICollection<Album> Albums { get; set; }
}
and then I execute some code like this:
using (PickContext db=new PickContext()) {
Album pick=db.Albums.SingleOrDefault(a => a.ID==pickID);
if (pick==null) return;
pick.Rating=4;
db.SaveChanges();
I was surprised that I got a validation exception like this:
Property: "Artist", Error: "The Artist field is required."
When I changed my query to include the Artist:
Album pick=db.Albums.Include("Artist").SingleOrDefault(a => a.ID==pickID);
I no longer got the exception. If I don't tell EF to populate all properties, and they're not required, will it simply overwrite these FKs in the database? I would have thought that if I retrieve an entity and don't assign a property, the property won't be changed in the database. Is this not true?

You don't need to use the required attribute for the Artist. It simply telling to EF your navigation property is always required to be there. Since you have defined,
public int ArtistID { get; set; }
as not nullable the ArtistID will be required in the in the database level (I think that is what you expected from the required attribute here). I think you can just remove the required attribute and then this should be working as expected.

Related

How to map DTO object to entity when some properties are missing in the DTO?

I am receiving PostDTO object in the controller and I am mapping it to Post entity and updating database. The problem occurs because PostDTO doesn't have Status property and Post entity does have Status property, so when I map PostDTO to Post, Post.Status becomes null. I don't want it to be null, I want it to stay unaffected in database. I could retrieve post from database and manually map Status property but is there better solution for this?
public class PostDTO
{
public int Id { get; set; }
public string Title { get; set; }
public string Content { get; set; }
public bool Urgent { get; set; }
}
public class Post
{
public long Id { get; set; }
public string Title { get; set; }
public string Content { get; set; }
public bool Urgent { get; set; }
public string Status { get; set; }
}
var post = _mapper.Map<Post>(postDto); //here post.Status becomes null
You could either use an approach like this to ensure the field isn't updated:
https://stackoverflow.com/a/10271910/84206
OR you can first GET the entity from the database, so that the Status is populated from the database, then apply the DTO changes to the entity.
There's other approaches, but they can't really be applied with EF.

How to include only specific properties of navigation property into the query by entity framework when lazy loading disabled?

LazyLoading is disabled on my project. I want to get Product which is Id = 1 with Category navigation property of it. But I need just Id and Name properties of Category. That's why I want Category navigation property to has only these two fields.Is it possible to create such a query ?
public class Product
{
public int Id { get; set; }
public string Name { get; set; }
public dobule Price{ get; set; }
public string Description { get; set; }
public bool IsDeleted { get; set; }
public DateTime CreatedDate { get; set; }
public DateTime ModifiedDate { get; set; }
public int CategoryId{ get; set; }
public Category Category{ get; set; }
}
public class Category
{
public int Id { get; set; }
public string Name { get; set; }
public dobule Description{ get; set; }
public Category IsDeleted { get; set; }
public DateTime CreatedDate { get; set; }
public DateTime ModifiedDate { get; set; }
}
If you only want a few specific fields you will need to select them explicitly. Something like this would work:
dbContext.Products
.Select(p => new Product
{
Id = p.Id,
Name = p.Name,
// etc... The fields you need from product go here
Category = new Category
{
Id = p.Category.Id,
Name = p.Category.Name
}
}
It might be better to have a Product and Category model class that only has the two fields. Now your method would return a Category object that lacks values for most fields which the caller might not expect. Depends on what exactly you're doing.
Depends if you know what do you want before calling the DB.
If you know what do you know, then you can use some 'Include' logic or the awnser from #Sangman or check docu here.
If you already have the entity in the memory and then you decide to load additional navigation property.
context.Entry(yourEntity).Reference(a => a.Category).Load();
More examples here.

Entity Framework one to many : SqlException

I have a little problem when I try to save an item in my DB using EntityFramework.
My classes looks like:
public partial class Site
{
public int Id { get; set; }
public string Name { get; set; }
public string LongName { get; set; }
public string Adress { get; set; }
public City City { get; set; }
public Country Country { get; set; }
public string VATNumber { get; set; }
}
public class Country
{
public int CountryId { get; set; }
public string Name { get; set; }
public string IsoCode { get; set; }
}
And when I try to create a new site in my controller it works, but when I try to add a link to an existing Country :
if (SiteProvider.GetSiteByName(Site.Name) == null)
{
Site.Country = CountryProvider.GetCountryById(1);//it's working, i see the link to the right country
SiteProvider.Create(Site);
}
public static void Create(Site Site)
{
using (MyDBContext Context = new MyDBContext())
{
Context.Site.Add(Site);
Context.SaveChanges(); //here is the problem
}
}
I got this error:
SqlException: Cannot insert explicit value for identity column in
table 'Country' when IDENTITY_INSERT is set to OFF
Thanks in advance for your help.
Add CountryId property to Site class and when adding a new Site set CountryId instead of Country property
public int CountryId { get; set; }
[ForeignKey("CountryId")]
public Country Country{ get; set; }
You have a slight issue with your use of contexts here. You have used one DBContext instance to load the country (where this country object will be tracked) and then a second DBContext to save the site (where the first country object is a property).
It is preferable to perform all your operations for a single unit of work by using one DB context (that would be shared between your classes) and the responsibility for disposing of it to be handled outside your repository layer.

Entity Framework Core error on Insert when exists a foreign key to a View

This is my model (semplified):
PRAT is the main table
public partial class PRAT
{
public int ID { get; set; }
public string PRATICA { get; set; }
public int ANNO { get; set; }
public string VARIANTE { get; set; }
[ForeignKey("ID")]
public VW_PRATICHE_CONTIPO VW_PRATICHE_CONTIPO { get; set; }
}
VW_PRATICHE_CONTIPO is a View (not a table!) in the database that contains some data related to PRAT table
public class VW_PRATICHE_CONTIPO
{
public int ID { get; set; }
public DateTime? DATAPRES { get; set; }
public string PROTGEN { get; set; }
public string TIPO { get; set; }
public string TIPOEXTRA { get; set; }
public string TIPOISTANZA { get; set; }
public string TIPOPRAT { get; set; }
}
The one-to-one relation between the table and the View is based on the ID field.
I need this because I want to do a query like this:
context.PRAT.Include(x=> x.VW_PRATICHE_CONTIPO)
This query works as exptected.
The problem happens when I try to save a new entity in PRAT.
When i do this:
context.PRAT.Add(prat);
await context.SaveChangesAsync();
I got this error:
The property 'ID' on entity type 'PRAT' has a temporary value. Either set a permanent value explicitly or ensure that the database is configured to generate values for this property.
If I remove the navigation property from PRAT all works fine, but I can't do the Include in my Query.
Can anybody help me?
Thank you.

Why am I getting an extra foreign key column with Entity Framework Code First Foreign Key Attributes?

I recently came across this strange problem with Entity Framework Code First.
My class looks like this
public class Status
{
[Key]
public int StatusID { get; set; }
public string Name { get; set; }
public int MemberID { get; set; }
[ForeignKey("MemberID")]
public virtual Member Member { get; set; }
public int PosterID { get; set; }
[ForeignKey("PosterID")]
public virtual Member Poster { get; set; }
public virtual ICollection<StatusLike> StatusLikes { get; set; }
public virtual ICollection<StatusComment> StatusComments { get; set; }
}
My Member class looks like this
public class Member
{
[Key]
public int MemberID { get; set; }
public string FirstName { get; set; }
public string LastName { get; set; }
public string Bio { get; set; }
public virtual ICollection<MemberCourseTaken> MemberCourseTakens { get; set; }
public virtual ICollection<Status> Statuses { get; set; }
public virtual ICollection<Club> FoundedClubs { get; set; }
public string EmailAddress { get; set; }
public string Password { get; set; }
public string Phone { get; set; }
public int AccountSourceID { get; set; }
public AccountSource AccountSource { get; set; }
public int AddressID { get; set; }
public Address Address { get; set; }
public string ProfilePhoto { get; set; }
public int MemberRankID { get; set; }
public MemberRank MemberRank { get; set; }
public DateTime Created { get; set; }
public DateTime Modified { get; set; }
}
And for whatever reason the database table that is created has the following columns
StatusID
Name
MemberID
PosterID
Member_MemberID
with MemberID, PosterID, and Member_MemberID being foreign keys.
How can I keep Member_MemberID from being generated?
Your Member_MemberID column is created because of the Member.Statuses property. I can imagine that this is not what you want. Probably members and statuses should exist independent of each other, so you need a junction table.
I don't know if you already use the OnModelCreating override of the DbContext, but that's the place to change the mapping between Member and Status:
protected override void OnModelCreating(DbModelBuilder mb)
{
mb.Entity<Member>().HasMany(m => m.Statuses).WithMany();
}
This will create a table MemberStatuses table with the two Id columns as foreign keys. This is a way to model a many-to-many relationship without a navigation property on the "other" side of the association. (I don't think you want a Members property in Status).
I've seen this before. In my case (Using EF 6.1), it was because my Fluent API Mapping was set up like so:
// In my EntityTypeConfiguration<Status>
HasRequired(x => x.Member).WithMany().HasForeignKey(x => x.MemberID);
That code works perfectly fine, but it doesn't tell EF that my Member class's Collection Navigational Property Status ha been taken into account. So, while I explicitly handled the existence of a Member Navigational Property in my Status Class, I now left an orphaned related collection property. That orphaned property, being a collection, tells EF that my Status class needs to have a Foreign Key to it. So it creates that on the Status Class.
To fix it, I had to be 100% explicit.
HasRequired(x => x.Member).WithMany(x => x.Statuses).HasForeignKey(x => x.MemberID)
It could bee that your Statuses Collection property in Member needs an attribute telling it that it is already considered, and not to go auto-creating mappings. I don't know that attribute.