Get related columns from FK table in a one-to-one related table - EF Core (2) - entity-framework

This should be fairly easy but I am struggling quite a bit.
My situation is as follows:
I have a table (Commodity) with a foreign key to another table (CommodityMaterial). In my GET endpoint I want to retrieve all the column data from Commodity and all the columns from CommodityMaterial as well.
I have set it up as follows:
public class Commodity
{
public long CommodityID { get; set; }
public long CommodityMaterialID { get; set; }
public decimal? SpecficWeight { get; set; }
public OmsCommodityMaterial OmsCommodityMaterial { get; set; }
public ICollection<OmsCommodityMaterial> OmsCommodityMaterials { get; set; }
}
The Foreign key table:
public class CommodityMaterial
{
public long? CommodityMaterialID { get; set; }
public string Name { get; set; }
public long? SortOrder { get; set; }
public Commodity Commodity { get; set; }
}
The FLUENT API:
modelBuilder.Entity<Commodity>(entity =>
{
entity.Property(e => e.CommodityID)
.HasColumnName("CommodityID")
.ValueGeneratedOnAdd();
entity.Property(e => e.CommodityMaterialID)
.HasColumnName("CommodityMaterialID");
entity.Property(e => e.SpecficWeight)
.HasColumnName("SpecficWeight")
.HasColumnType("decimal(18, 2)");
entity.HasOne(a => a.OmsCommodityMaterial)
.WithOne(b => b.Commodity)
.HasForeignKey<Commodity>(b => b.CommodityMaterialID);
});
This creates the proper relationship, a CommodityMaterial (ID) is needed when creating a Commodity.
Now I want to get all values / columns of the Commodity table, but also the related columns of the CommodityMaterial table (CommodityMaterialID, Name and SortOrder). After reading some other posts i found out I should/could use Include to get related data, my GET operation looks as follow:
[HttpGet]
public async Task<IEnumerable<Commodity>> GetTest()
{
return await Context.Commodity.Include(x => x.OmsCommodityMaterials).ToListAsync();
}
However, this still only returns the 3 columns of the Commodity table (CommodityID, CommodityMaterialID, SpecficWeight)
Anyone has a clue what I am doing wrong?
SOLUTION
Ok, I am a bit ashamed, the problem was the fact that I was using a DataContract annotation in my Commodity POCO class, which was omitted in the code above, the OmsCommodityMaterial reference was missing a DataMember attribute thus making it's fields invisible...

Although not an answer per se maybe you would find it yourself here.
Include()/ThenInclude() (Eager loading) is one of three possible solutions. I suggest you explore the other two as well - Explicit and Lazy.
EDIT: Further investigating your code it seems a bit strange to have a collection of entities in a one-to-one relationship. Maybe you would want to use just the class and (this is important!) specify class names in < and > when calling HasOne(). If you use ReSharper, or something similar, such tool suggest name simplification, that is to get rid of explicitly specifying the types when templating fluent methods. This is the next special place when EF gives slightly different meaning to known C# features. The first one is the very well-known virtuals.
HTH

I am a bit ashamed by this, the problem was the fact that I was using a DataContract annotation in my Commodity POCO class, which was omitted in the code above, the OmsCommodityMaterial reference was missing a DataMember attribute thus making it's fields invisible...

Related

Make reference table data read-only - EF Core

I have a table (Commodity) which has a one-to-one relationship with another table (CommodityMaterial), in my GET endpoint the Commodity returns it's own columns and also the columns (and values) of the referenced table which works perfectly. However, in the POST operation of the endpoint, a user should not be able to POST data of the reference table (CommodityMaterial), how can this be achieved? I used to disable this by using a DataContract, however, because I need the columns for my GET operator, this is not an option.
I already tried, following this post: https://csharp.christiannagel.com/2016/11/07/efcorefields/, removing the SET on the reference table and making a backing field but this does not seem to work (error that the backing field is read-only).
I also tried setting the SET to protected, but this is not working.
So the question is, how to make the reference table read-only (only available for my GET endpoint and not my POST endpoint).
The Commodity POCO class:
[DataContract]
public class Commodity
{
public Commodity()
{
}
public Commodity(CommodityMaterial commodityMaterial)
{
CommodityMaterial = commodityMaterial;
}
[DataMember]
public long CommodityID { get; set; }
[DataMember]
public long CommodityMaterialID { get; set; }
[DataMember]
public decimal? SpecficWeight { get; set; }
[DataMember]
public CommodityMaterial CommodityMaterial { get; }
}
Fluent part:
modelBuilder.Entity<Commodity>(entity =>
{
entity.Property(e => e.CommodityID)
.HasColumnName("CommodityID")
.ValueGeneratedOnAdd();
entity.Property(e => e.CommodityMaterialID)
.HasColumnName("CommodityMaterialID");
entity.Property(e => e.SpecficWeight)
.HasColumnName("SpecficWeight")
.HasColumnType("decimal(18, 2)");
entity.HasOne(a => a.CommodityMaterial)
.WithOne(b => b.Commodity)
.HasForeignKey<Commodity>(b => b.CommodityMaterialID);
});
The parameters your action accepts should represent what your action does/is allowed to do. If a client should not be able to update a related entity, then the class you bind the request body to, should not have that entity available. Use a view model, essentially:
public class CommodityRequest
{
// all properties you want editable
// exclude `CommodityMaterial` obviously
}
Then:
public IActionResult Update(CommodityRequest model)

Entity Framework Code First Foreign Key Problems

I am trying to use EF Code First on an existing database. I first tried some of the reverse-engineering tools, but I ran into problems with that, so at the moment I am trying to hand-code some of the classes. I am having some trouble getting some of the foreign key relationships set up. Consider two tables. The first is called LocaleValueLookup:
public class LocaleValueLookup
{
public int Id { get; set; }
public Guid Guid { get; set; }
}
This table provides an Id for multi-language text held in a different table (that other table is not important for the purposes of this question). The second table is called SectionType, and it has an optional FK to LocaleValueLookup:
public class SectionType
{
public int EnumId { get; set; }
public string Name { get; set; }
public int? DefaultSectionTextLocaleValueLookupId { get; set; }
// Navigation property
public LocaleValueLookup DefaultSectionTextLocaleValueLookup { get; set; }
}
I have tried various things, including adding a [ForeignKey] attribute to the SectionType.LocaleValueLookup property, and various incantations in the DbContext.OnModelCreating() override, but when I query the DbContext, I can't get the DefaultSectionTextLocaleValueLookup to be anything but null. I can retrieve other objects from the context just fine, and I have verified that DefaultSectionTextLocaleValueLookupId is not null at least some of the time.
My OnModelBuilding() contains the following:
modelBuilder.Entity<LocaleValueLookup>()
.ToTable("LocaleValueLookup")
.HasKey(lvl => lvl.Id);
modelBuilder.Entity<LocaleValueLookup>().Property(lvl => lvl.Id).IsRequired();
modelBuilder.Entity<SectionType>()
.ToTable("SectionType")
.HasKey(st => st.EnumId);
modelBuilder.Entity<SectionType>().Property(st => st.EnumId).IsRequired();
A couple of other points:
I would prefer not to have a SectionType collection on the LocaleValueLookup object. LocaleValueLookup is a low-level class that a lot of other classes depend on, so to include a collection property on LocaleValueLookup for every other class that references it will make for an unwieldy class with a lot of collections on it that I don't need from a domain perspective.
I would prefer to do the mapping setup in DbContext.OnModelCreating() rather than using attributes on my model objects
Any help would be greatly appreciated!
It looks like your foreign key is nullable so that means an optional -> many relationship.
Could you try something like this:
modelBuilder.Entity<SectionType>()
.HasOptional(opt => opt.DefaultSectionTextLocaleValueLookup)
.WithMany() // no navigation on the other side
.HasForeignKey(fk => fk.DefaultSectionTextLocaleValueLookupId);
If you were to write a query like this you should get a value back:
var query =
from st in db.SectionTypes
where st.EnumId == 12345
select new
{
SectionType = st,
LocaleValue = st.DefaultSectionTextLocaleValueLookup
};
It will only be non-null if the foreign key has a value, obviously.

EF Code first and optional:optional relationship

According to msdn article, the following should create an optional:optional relationship, but instead it creates optional:many relationship. Is the article wrong?
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
modelBuilder.Entity<Optional_1>()
.HasKey(o1 => o1.id1);
modelBuilder.Entity<Optional_2>()
.HasKey(o2 => o2.id2);
modelBuilder.Entity<Optional_1>()
.HasOptional(o1 => o1.Dependent)
.WithOptionalPrincipal(o2 => o2.Principal);
}
public class Optional_1
{
public int id1 { get; set; }
public Optional_2 Dependent { get; set; }
}
public class Optional_2
{
public int id2 { get; set; }
public Optional_1 Principal { get; set; }
}
thank you
The table might look like one to many, but Entity Framework will enforce it as optional:optional because of the navigation properties. Since the navigation property is only a single object and not a collection, there is no way to add multiple.
If you look at the generated tables, it creates a nullable foreign key to your principal table (Optional_1). This allows you to create an Optional_2 that is not associated with an Optional_1.
If you were to insert multiple rows into Optional_2 that have the same foreign key to Optional_1 outside of EF, there wouldn't be anything preventing it from going through. If you were to try and load these entities you would get an error. You can't add a unique index to the column because it needs to allow NULL since it is optional.

Entity Framework Code First One-to-One Required-Required Relationship

When using Entity Framework Code First 4.3.1 it is possible to create relationships with a multiplicity of 1-to-1. That is, one entity on each end of the relationship.
It is possible to configure 1-to-1 relationships to be required-required or required-optional ^. However, when I switch between the two I do not see any differences in:
The database schema generated. I am targeting SQL Server 2008.
The runtime behaviour of EF.
As such, I am able to create a RequiredPrincipalAs record without a corresponding RequiredDependentAs record, despite the relationship being configured as required-required. This seems to contradict the documentation for HasRequired(...):
Configures a required relationship from this entity type. Instances of the entity type will not be able to be saved to the database unless this relationship is specified. The foreign key in the database will be non-nullable.
http://msdn.microsoft.com/en-us/library/gg671317
The required-required relationship entities:
public class RequiredPrincipalA
{
public int Id { get; set; }
public virtual RequiredDependentA DependentA { get; set; }
}
public class RequiredDependentA
{
public int Id { get; set; }
public virtual RequiredPrincipalA PrincipalA { get; set; }
}
The required-optional relationship entities:
public class RequiredPrincipalB
{
public int Id { get; set; }
public virtual OptionalDependentB DependentB { get; set; }
}
public class OptionalDependentB
{
public int Id { get; set; }
public virtual RequiredPrincipalB PrincipalB { get; set; }
}
The DbContext and model configuration:
public class AppContext : DbContext
{
public DbSet<RequiredPrincipalA> PrincipalAs { get; set; }
public DbSet<RequiredDependentA> DependentAs { get; set; }
public DbSet<RequiredPrincipalB> PrincipalBs { get; set; }
public DbSet<OptionalDependentB> DependentBs { get; set; }
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
modelBuilder.Entity<RequiredPrincipalA>()
.HasRequired(o => o.DependentA)
.WithRequiredPrincipal(o => o.PrincipalA);
modelBuilder.Entity<RequiredPrincipalB>()
.HasOptional(o => o.DependentB)
.WithRequired(o => o.PrincipalB);
}
}
The test code:
Database.SetInitializer(new DropCreateDatabaseAlways<AppContext>());
using (var ctx = new AppContext())
{
ctx.Database.Initialize(force: false);
ctx.PrincipalAs.Add(new RequiredPrincipalA());
ctx.PrincipalBs.Add(new RequiredPrincipalB());
ctx.SaveChanges();
}
I am aware I could add a [Required] data attribute to the navigation properties of RequiredPrincipalA.DependentA and RequiredDependentA.PrincipalA. This would cause EF validation to prevent the scenario above. However, I do not want to do this because it also validates the navigation property is populated when updating an existing entity. This means the application has to pre-fetch the entity at the other end of the relationship for every update.
Why do I not see any difference in the behaviour of EF just when changing a relationship between required-required and required-optional?
^ Note that optional-optional is also supported but this doesn't form part of my question. There are obvious differences in the generated database schema and runtime behaviour when an optional-optional relationship is configured.
I don't know why required-required is allowed for this case but it cannot exist in the database because relation is build on primary keys. Required-required means that A cannot be inserted if related B doesn't exist and B cannot be inserted if related A doesn't exist => neither A or B can be inserted.
Database relation has always principal and dependent entity - principal can always exist without dependent.
Real required-required in EF can be achieved only when both A and B are mapped to the same table (table splitting) because in such case they are both inserted with single insert command.
Not really an answer but I have more to say than will fit in comments. But you know, I write 900 page books...it's just how I roll. :)
Oddly I would expect the fluent configuration to behave the same way as the data annotation and am confused that it's not doing it. (I've pinged Rowan Miller with a link to this thread to get his feedback.) And the behavior I mean is: validating the constraint during SaveChanges.
On the database side, I'm with Ladislav.In the model, EF defines the 1:1 using the keys of the related entities. But in the database, you can't have FKs in both tables, so only the dependent table in the database will require that constraint that it's PK maps to an existing PK in the principal table.
And finally, I understand your reason for not wanting EF to enforce the relationship if you aren't going to always deal with teh full graph. I think 1:1 relationships are the most confusing of the EF relationship mappings and I always find myself having to go back for reminders of the rules and how things should work.
Old question. But since EF6 is still used and even available for .Net standard and this issue can be a real nuisance, I think it's worth mentioning something I couldn't find in other answers.
It is true that both HasRequired - WithRequiredPrincipal and HasOptional - WithRequired produce the same database schema and the same runtime behavior. That is, with both mappings it's possible to save a principal without a dependent entity and to remove the dependent later. So much for HasRequired.
But there is a way to make EF validate the required relationship when creating the entities, which is by simply adding a [Required] attribute:
public class RequiredPrincipalA
{
public int Id { get; set; }
[Required] // <== here
public virtual RequiredDependentA DependentA { get; set; }
}
public class RequiredDependentA
{
public int Id { get; set; }
public virtual RequiredPrincipalA PrincipalA { get; set; }
}
As said, only when creating the entities. It's still possible to set RequiredPrincipalA.RequiredDependentA = null and save it successfully. But I think that, fortunately, the likelihood of that happening in code is far lower than forgetting to set the required dependent.

Mapping properties to (differently named) foreign key fields in Entity Framework CTP5

I'm trying to use the Entity Framework CTP5 Fluent API to map an exist database. I have the following classes:
public class Shop
{
public long Id
{
get;
set;
}
}
public class Sale
{
public long Id
{
get;
set;
}
public virtual Shop Shop
{
get;
set;
}
}
The corresponding tables are called "Stores" and "Sales". Sales has a StoreId foreign key that points to the Id field in the Stores table.
I'm struggling to map the Sale.Shop.Id to the StoreId in the table. I'm not at liberty to change it to ShopId, so need to map it.
In CTP4, I was using:
modelBuilder.Entity<Sale>().MapSingleType(x =>
new
{
Id = x.Id,
StoreId = x.Shop.Id
});
I tried the following:
modelBuilder.Entity<Sale>().Property(x => x.Shop.Id).HasColumnName("StoreId");
However, it seems this only works with a primitive type.
How do I specify this mapping?
Update: I've added a revised version for the Release Candidate of EF 4.1 below
After some hunting, I've found the answer that works for me:
EF4.1 RC version:
modelBuilder.Entity<Booking>().HasRequired(b => b.Booker)
.WithMany(m => m.BookedSlots).Map(p=>{
p.MapKey("BookerID");
});
in your case:
modelBuilder.Entity<Sale>().HasRequired(sale => sale.Shop)
.WithMany().Map(s=> {
s.MapKey("StoreId");
});
My version is slightly different because I have navigation properties on both sides of the relationship.
I think the best way to solve this would be to upgrade your independent Association to be a Foreign Key Association meaning that instead of hiding the foreign key ShopId, actually including it in Sale class. Then you can use Data Aannotations/Fluent API to change its column name to match to your existing schema:
public class Shop
{
public long Id { get;set; }
}
public class Sale
{
public long Id { get; set; }
[Column(Name="StoreID")]
public long ShopId { get; set; }
public virtual Shop Shop { get; set; }
}
Which results to the desired DB Schema:
I think what you're looking for is the RelatedTo attribute. More information in this ADO.NET team blog post.