What I am trying to do is I have two tables which can be linked many to many, but the object on either side may not exist yet. Whilst this gives me chills just thinking about it, it is required for what I am working on.
I've never had to do this before with the Entity Framework Code First, I have no problem creating the many to many relationship but only when both sides exist, so I'm not 100% certain that this is even possible but hopefully someone will be able to identify how I can achieve this.
So far as entities on both sides can be optional, you can do this using third entity:
public class Entity1
{
public int Id { get; set; }
}
public class Entity2
{
public int Id { get; set; }
}
public class Many2ManyRelationEntity
{
public int Id { get; set; }
public int? Entity1Id { get; set; }
public int? Entity2Id { get; set; }
}
Related
I'm working on a serverside blazor project (.net 6) using Entity Framework with code first. I have two tables, let's say (in order to protect private data), we have the Tables Band and Bandsman. Originally, every band could have exactly one bandsman, a bandsman could be connected to more then one band though. It's an example, so please don't question this assumptive circumstances.
I created two classes:
[Table("Band")]
public partial class Band
{
[Key]
public int Id { get; set; }
public string BandName { get; set; }
public int? BandsmanId { get; set; }
public virtual Bandsman Bandsman { get; set; }
}
[Table("Bandsman")]
public partial class Bandsman
{
[Key]
public int Id { get; set; }
public string Name { get; set; }
public virtual List<Band> Band { get; set; }
}
So far everything works fine. Entity Framework set the correct foreign key. But now I have to insert a second bandsman. Let's say, the first bandsman is a keyboarder, now I need a drummer as well. So I altered the existing classes:
[Table("Band")]
public partial class Band
{
[Key]
public int Id { get; set; }
public string BandName { get; set; }
public int? BandsmanId { get; set; }
public int? DrummerId { get; set; }
public virtual Bandsman Bandsman { get; set; }
public virtual Bandsman Drummer { get; set; }
}
[Table("Bandsman")]
public partial class Bandsman
{
[Key]
public int Id { get; set; }
public string Name { get; set; }
public virtual List<Band> Band { get; set; }
public virtual List<Band> Drummer { get; set; }
}
I know I have to tell Entity Framework now how to map the tables. So I added mapping instructions to the OnModelCreating-Method in DbContext:
builder.Entity<Band>().HasOne(a => a.Bandsman).WithMany().HasForeignKey(b => b.BandsmanId);
builder.Entity<Band>().HasOne(a => a.Drummer).WithMany().HasForeignKey(b => b.DrummerId);
This doesn't work. When I create the migrations I see that Entity Frameworks tries to create new Columns BandsmanId1 and BandsmanId2 to the Band-Table instead of using the Columns I defined.
So I tried to add the instructions the other way around, too, in addition to the previous ones:
builder.Entity<Bandsman>().HasMany<Band>(a => a.Band).WithOne().HasForeignKey(b => b.BandsmanId);
builder.Entity<Bandsman>().HasMany<Band>(a => a.Drummer).WithOne().HasForeignKey(b => b.DrummerId);
It's still the same, Entity Framework tries to add new columns and map the foreign keys to them.
I also tried to rename Band.BandsmanId to Band.KeyboarderId or rather add and map a new column with the new name (so existing data won't get lost), rename Band.Bandsman to Band.Keyboarder and Bandsman.Band to Bandsman.Keyboarder. With no effect, Entity Framework still seems incapable to use the colums I want it to use. I guess the instructions I added to OnModelCreating in DbContext are incorrect, but I'm not able to find out how to put it right. I found some examples here on stackoverflow and elsewhere, but I can't manage to convert one of this examples to my code.
So I hope someone can help me to put the classes and instructions right.
After posting my question, I found the solution in a post that was shown as possibly related:
Entity Framework Code First - two Foreign Keys from same table
I was close, my only mistake was not to name the virtual List-Property of the Bandsman-Class in the .HasMany()-Part of the instructions. So Entity Framework didn't now these properties were related to the foreign key columns in the band-table and tried to create the assumed-to-be-missing columns on its own. This way it works:
builder.Entity<Band>().HasOne(a => a.Bandsman).WithMany(b => b.Band).HasForeignKey(a => a.BandsmanId);
builder.Entity<Band>().HasOne(a => a.Drummer).WithMany(b => b.Drummer).HasForeignKey(a => a.DrummerId);
My database entity project has count about 30 columns, I would like to create clear EF Core model which many of them is grouped in several classes.
For example, overriding entity is Sensors and it has two elements (Int ID, ElectricalData electricalData), ElectricalData is a seperate class which has next 3 seperate classes and two bool and string objects.
A model constructed in this way in Add-Migration process returns feedback that sub-entities doesn't have a primary key, but they shouldn't contain PK, because only Sensors class should have a primary key.
How can I solve this problem? Does this idea is correct?
Code below:
public class SensorModel
{
[Key]
public int ID { get; set; }
public ElectricalDataModel ElectricalData { get; set; }
}
public class ElectricalDataModel
{
public TensionModel Tension { get; set; }
public CurrentModel Current { get; set; }
public string SecurityClass { get; set; }
public ResistanceModel Resistance { get; set; }
public bool ReversePolarizationSecurity { get; set; }
}
public class TensionModel
{
public double Minimum { get; set; }
public double Maximum { get; set; }
public string Current { get; set; }
}
//.......................................... and so on
What are you asking was called Complex Types, and the EF Core term is Owned Entity Types. By default they share the same table as the owner and are used to just logically separate (group) the related properties - exactly the goal you are describing.
The easiest way to identify a class as owned type in EF Core 2.1 is to mark it with OwnedAttribute:
[Owned]
public class ElectricalDataModel
{
// Properties..
}
[Owned]
public class TensionModel
{
// Properties..
}
//.......................................... and so on
Of course the same can be achieved via the OwnsOne fluent API, which also allows you to configure the column names and other attributes for the owned entity per owner.
I'm building a fairly simple MVC project and still getting my head around where to use navigation properties and foreign keys with code first.
This is the main model class:
public class GroceryItem
{
public int ID { get; set; }
public string Name { get; set; }
public GroceryCategory Category { get; set; }
public QualityProfile Quality { get; set; }
public GroceryStore BestStore { get; set; }
public double BestPrice { get; set; }
public double LastSeenPrice { get; set; }
//Navigation Properties
public virtual ICollection<GroceryItem> SimilarItems { get; set; }
}
and these are the relating classes:
public class GroceryStore
{
public int ID { get; set; }
public string Name { get; set; }
public string Address { get; set; }
public Uri Website { get; set; }
}
public class QualityProfile
{
public int ID { get; set; }
public string Name { get; set; }
public string Description { get; set; }
/// <summary>
/// Rank out of 1-10, 10 being the best
/// </summary>
public byte Ranking { get; set; }
}
public class GroceryCategory
{
public int ID { get; set; }
public string Name { get; set; }
public string Description { get; set; }
}
Which brings me to my question, is the navigation property of SimilarItems I have in the GroceryItem class sufficient to represent a list of multiple grocery items or does this not work as it is referring to itself?
Additionally...do the Category, Quality and BestStore properties require ID properties to represent a foreign key inside of the GroceryItem class (e.g. CategoryID), or is the way I have this represented OK?
----EDIT----
--Refactored Code--
I've re-factored my model based on the suggestions below, which I think better accommodates the suggestions you've made (yes a 2nd time), realised my model was a little flawed and extracted out the price component into a separate purchases Model.
public class GroceryItem
{
public int ID { get; set; }
public string Name { get; set; }
[ForeignKey("Category")]
public int CategoryID { get; set; }
[ForeignKey("Quality")]
public int QualityID { get; set; }
//Navigation Properties
public virtual QualityProfile Quality { get; set; }
public virtual GroceryCategory Category { get; set; }
}
However the last thing I'm uncertain about which is on topic to this post, is if I have a collection as a part of the model (one that does not reference itself like in the first example), can I just represent that with a navigation property or does an extra step need to be taken?
Ie. If I was to allow multiple different categories on a GroceryItem, instead of looking like this:
[ForeignKey("Category")]
public int CategoryID { get; set; }
public virtual GroceryCategory Category { get; set; }
it would look like this:
public virtual ICollection<GroceryCategory> Categories { get; set; }
The best answer to your question(s) is, "It depends". Navigation properties are one way of informing Entity Framework that there's a relationship between entities. By convention, if you have a navigation property such as:
public Category Category { get; set; }
Entity Framework will create a column on the table named in the form of [RelatedPropertyName]_[RelatedPK]. Given your classes, the property above would cause a column named Category_ID. There's nothing more you need to do make it work. The relationship will automatically be handled by EF.
However, doing it this way, you won't have access to this foreign key property. It's not exposed in the public API of your entity. Often, especially when selecting related items from a select list and similar such scenarios, this becomes problematic, as you must store the selected value some place else, usually a property on a view model, and then use this to query the related thing from the database before setting it on the entity it belongs to and finally saving the entity. Whereas, with an actual foreign key property, you can simply post directly back to this and Entity Framework will automatically wire up the related entity. As a result, I tend to always follow the following pattern with my navigation properties:
public int FooId { get; set; }
public virtual Foo Foo { get; set; }
In most scenarios, Entity Framework will automatically connect those two, such that FooId will hold the foreign key relationship for the Foo navigation property. However, occasionally, EF will trip up and try to create the implicit foreign key behind the scenes, still, but you can correct that behavior by explicitly telling EF that this is the foreign key:
[ForeignKey("Foo")]
public int FooId { get; set; }
Roughly the same applies with collection navigation properties. EF will see this as an indication that there's a one-to-many relationship in play and add the implicit foreign key on the opposite entity. Given your collection:
public virtual ICollection<GroceryItem> SimilarItems { get; set; }
The opposite entity is actually the same entity, which presents an interesting use case. Typically, EF would handle this by assuming there's a one-to-many relationship. You'd end up with a column named GroceryItem_ID on your dbo.GroceryItems table. Here, though, you would not only have no access to the foreign key directly, but you also have no public API for accessing the parent GroceryItem either. That may not be a problem, but it's something to be aware of. The only way you'd be able to manage the relationship is through the collection on the parent, not through a child item in that collection.
However, since this is self-referential and you have not specify a foreign key or instance navigation property, all EF will see is a collection on both sides of the relationship, so my guess is that you'll actually end up with an M2M with an intermediary table. I can't test that theory out myself at the moment, and I haven't tried this particular scenario myself previously.
To create a true one-to-many, you would need to create another navigation property similar to:
public virtual GroceryItem ParentGroceryItem { get; set; }
And, even, then, I don't think EF will get the point without a little Fluent configuration:
HasMany(m => m.SimilarItems).WithOptional(m => m.ParentGroceryItem);
You could also use WithRequired in other scenarios instead of WithOptional, which would obviously make the relationship a required one, but since this is self-referential, it's impossible to have it required, because there will have to be at least one root node with no parent.
I have two entities with exactly the same properties:
public class Oil
{
public Guid Id { get; set; }
public string Title { get; set; }
public int Price { get; set; }
public int Ammount { get; set; }
}
public class Filter
{
public Guid Id { get; set; }
public string Title { get; set; }
public int Price { get; set; }
public int Ammount { get; set; }
}
Questions:
1) Can I somehow store them in one table? If so, than how?
2) Or should I implement inheritance? And what type then?
Edits:
In my case these two entities are just the same, they will not have any different properties in the future.
I implemented Table-per-Hierarchy approach, but there is another issue
(I have another type that has collections of oils and filters):
public class Warehouse
{
public Guid Id { get; set; }
public ICollection<Filter> Filters { get; set; }
public ICollection<Oil> Oils { get; set; }
}
So, when I create database, I get Warehouse_Id and Warehouse_Id1 fields in it. I don't want the Oil and Filter classes to have Warehouse property in them, how can I get just one field for Warehouse id in the db table?
If I include WarehouseId as a property in OilFilterBase class I will get 3 warehouse_id in the database table.
p.s. I also have DbSet<Oil> and DbSet<Filter> in my Context and don't have DbSet<OilFilterBase>.
It's hard to say what's best without knowing more about your requirements. What makes these two entities different? If they perform different functions and just happen to have the same properties, then it would probably be a good idea to store them in separate tables; that makes the most sense conceptually, and it would make things much easier if, say, you decided you wanted to add additional properties to one of them in the future.
On the other hand, if they're really the same at every level, it's also worth asking if you really need two different entity types to store them.
For the middle ground where the two classes serve related purposes but also differ in some ways, then yes, some form of inheritance might be a good approach -- either having one entity type derive from the other, or creating a new common base type and having both entities derive from that.
If you decide this is the best approach, then it looks like a good candidate for Table-per-Hierarchy mapping. You could restructure your code something like this:
public abstract class OilFilterBase
{
public Guid Id { get; set; }
public string Title { get; set; }
public int Price { get; set; }
public int Amount { get; set; }
}
public class Oil : OilFilterBase
{
}
public class Filter : OilFilterBase
{
}
...and then the Entity Framework will, by default, create a single table with an automatically-generated discriminator column, and store all instances of both entity types in that table.
If you decide that either of those entity types should have additional fields, then you could look at some of the other inheritance options, like Table-per-Type, that create separate but related tables for each entity type.
The first thing to do is decide how these classes fit together conceptually, and then figure out the best way to implement that in EF terms. If you can give more information about what these entities are and how they work, it'll be easier for people here to give good advice.
Response to Edits:
I think what's happening with the extra columns (Warehouse_Id and Warehouse_Id1) is this:
Because you're setting up the relationships for Oil and Filter separately, it's not comfortable assuming you want to use the base class's WarehouseId property as the foreign key -- what if you only wanted to set up that relationship for Oil and not Filter? It shouldn't be writing to the base class column in that case. So, it decides to create new properties instead.
Fortunately, you can use the [ForeignKey()] attribute (or the fluent API) to tell it what you really want, like this:
using System.ComponentModel.DataAnnotations.Schema;
public abstract class OilFilterBase
{
public Guid Id { get; set; }
public string Title { get; set; }
public int Price { get; set; }
public int Amount { get; set; }
public Guid WarehouseId { get; set; }
}
public class Oil : OilFilterBase
{
}
public class Filter : OilFilterBase
{
}
public class Warehouse
{
public Guid Id { get; set; }
[ForeignKey("WarehouseId")]
public virtual ICollection<Filter> Filters { get; set; }
[ForeignKey("WarehouseId")]
public virtual ICollection<Oil> Oils { get; set; }
}
Also, I think you'll need to include a DbSet<OilFilterBase> (in addition to DbSet<Oil> and DbSet<Filter>) in your context in order to get Table-per-Hierarchy inheritance to work -- try it and see.
Good luck!
I am doing code first and using a Table-per-type design. I am getting the following error when I extend the second object to multiple tables:
A value shared across entities or associations is generated in more than one location. Check that mapping does not split an EntityKey to multiple store-generated columns.
My database looks like:
Thanks for the up-vote, editing to add my picture:
The POCO for the project looks like:
public abstract class Project {
public int ProjectID { get; set; }
public string Title { get; set; }
public string Description { get; set; }
public virtual ICollection<Tag> Tags { get; set; }
public virtual ICollection<LocationElement> LocationElements { get; set; }
public abstract string getProjectIdentifier();
}
And for a Location Element:
public enum HowObtainedCodes {
Provided = 1,
Estimated = 2,
Summarized = 3
}
public abstract class LocationElement {
public int LocationElementID { get; set; }
public int ProjectID { get; set; }
public HowObtainedCodes HowObtainedCodeID { get; set; }
}
And for a point:
[Table("ProvidedPoints")]
public class ProvidedPoint : LocationElement {
public double Lat { get; set; }
public double Long { get; set; }
public string Description { get; set; }
}
The link from projects (abstract) to scientific licences works fine, and my objects load / persist as expected. Further I can add LocationElements object in if I make it not abstract. As soon as I extend LocationElements and try to save a ProvidedPoint object I get the above message. My first thought was that the LocationElementID on ProvidedPoints was set as an Identity column, but this was not the case.
My question is: Am I doing something unexpected by trying to link two TPT objects together in this way? Am I missing something else?
As noted by #leppie above, I had to decorate the LocationElement class with the annotation [Table("LocationElements")], which immediately fixed the problem. My understanding with EF was that this was not necessary with the base table for a TPT design, and further I had not done it on the Project / ScientificLicence pair (that is, I only decorated the ScientificLicence object).
I am assuming this has something to do with the way LocationElements are added/persisted when I save a new Project object. If anyone has any additional insight I would love to know more.
Hope this helps someone else and a big thank you to leppie!