I was reading a textbook that provides an example to use InverseProperty attribute.
Two tables in the database are "Shoes" and "Colors"
corresponding classes are Shoe and Style
public class Shoe
{
public long Id { get; set; }
public string Name { get; set; }
public decimal Price { get; set; }
[Column("ColorId")]
public long StyleId { get; set; }
[ForeignKey("StyleId")]
public Style Style { get; set; }
}
[Table("Colors")]
public class Style
{
[Key]
[Column("Id")]
public long UniqueIdent { get; set; }
[Column("Name")]
public string StyleName { get; set; }
public IEnumerable<Shoe> Shoes { get; set; }
}
and the author says that if we want to name the navigation property on the Style class with a different name "Products", then we need to add InverseProperty arrtibute as:
public class Style
{
[Key]
[Column("Id")]
public long UniqueIdent { get; set; }
[Column("Name")]
public string StyleName { get; set; }
[InverseProperty(nameof(Shoe.Style))]
public IEnumerable<Shoe> Products { get; set; }
}
so my questions are:
Q1- isn't that there is no restriction on naming Collection navigation property such as IEnumerable<Shoe> in this example? we can name it whatever we want as public IEnumerable<Shoe> XXX{ get; set; } where XXX could be any legal property name? or EF still enforce a rule that the property name for Collection navigation property has to be the type in the collection(Shoe in this example) and then append a s char? (Shoe + s is Shoes)
Q2-isn't that InverseProperty attribute be used when there is more than one pair of navigation properties between two entity types? we only have one pair here, so why still need to use InverseProperty?
Related
I've been trying to figure out how to do the following (although my research did not help): I have the these three classes:
public abstract class Classifier
{
public int ClassifierId { get; set; }
public string ClassifierName { get; set; }
public DateTime DateAdded { get; set; }
}
public class ManualClassifier : Classifier
{
public int ManualClassifierId { get; set; }
public string user_name { get; set; }
public string userName { get; set; }
public string firstName { get; set; }
public string lastName { get; set; }
public string email { get; set; }
public string password { get; set; }
}
public class ToolClassifier : Classifier
{
public int ToolId { get; set; }
public string ToolName { get; set; }
}
Both the ManualClassifier and ToolClassifer inherit from Classifier. I'm using EF Core to map this to a database but the question is the following: I've already searched a bit and I must make use of a descriminator which basically is an implicitly created column that will say the type of, in this case, classifier. So far so good. The issue arises when I have a property called ManualClassifierId as well as a ToolId. I want this two properties to map to the ClassifierId property. So in the table representing the entity Classifier, the ClassifierId property will either be the ManualClassifierId or the ToolId.
How can I achieve this mapping? Also, this solution would mean that both child classes would both have empty fileds in the tables (due to inheriting the three properties from the Classifier class). Is there a better solution? Perhaps just erase the Id's from both child classes a let them inherit the parent one?
Thank you in advance!
To use the same column name in both classes, you can add a Column attribute to both properties. Then they will both use that column name in the database. See ColumnAttribute(String).
Use it like this:
public class ManualClassifier : Classifier
{
[Column(Name="ClassifierId")]
public int ManualClassifierId { get; set; }
...........
}
Do the same with ToolId.
So I try to create some ASP.NET project with EF Core.
I want to set propert of one entity as primary key and foreign key to another entity. The relationship is 0..1 - 1. I use DataAnnotations:
public class OfficeAssignment
{
[Key, ForeignKey("InstructorID")]
public int InstructorID { get; set; }
public Instructor Instructor { get; set; }
public string Location { get; set; }
}
But I keep getting column InstructorID as PK and InstructorID1 as FK... Any ideas, why EF behaves like that and how can I achieve my goal?
You should follow convention over configuration as much as you can. An OfficeAssignment entity should have an OfficeAssignmentId PK, like this:
public class OfficeAssignment
{
public int OfficeAssignmentId { get; set; }
//Notice that Id does not have an uppercase D
public int InstructorId { get; set; }
public string Location { get; set; }
public Instructor Instructor { get; set; }
}
However, if you don't want to follow normal conventions, the name of the property that goes in the ForeignKey attribute is the opposite of where it's declared:
public class OfficeAssignment
{
[Key, ForeignKey("Instructor")]
public int InstructorId { get; set; }
public string Location { get; set; }
public Instructor Instructor { get; set; }
}
And, if you want to keep it compile-time safe:
public class OfficeAssignment
{
[Key, ForeignKey(nameof(Instructor))]
public int InstructorId { get; set; }
public string Location { get; set; }
public Instructor Instructor { get; set; }
}
It's enough to set primary key attribute([Key]) in the OfficeAssignment class and in Instructor class we need to set such attribute:
[InverseProperty("Instructor")]
on collection of CourseAssignments. That will work as desired.
I have the following requirement, on my app the Entities will come with some fields, however the user needs to be able to add additional fields to the entity and then values for those fields.
I was thinking something like this but I am not sure if it would be a good approach or not.
The base class is an entity (Not sure which fields I need to add here)
public class Entidad
{
}
Then the Company Class will inherit from Entity
public class Empresa : Entidad
{
[Key]
public int Id { get; set; }
public string Nombre { get; set; }
public string NIT { get; set; }
public string NombreRepresentanteLegal { get; set; }
public string TelefonoRepresentanteLegal { get; set; }
public string NombreContacto { get; set; }
public string TelefonoContacto { get; set; }
public ICollection<CampoAdicional> CamposAdicionales { get; set; }
}
As you can see there is an ICollection of additional fields. that class would have the fieldname, type and id
public class CampoAdicional
{
[Key]
public int Id { get; set; }
public string NombreCampo { get; set; }
public Tiposcampo TipoCampo { get; set; }
}
and then the field value would be something like this:
public class ValorCampo
{
public Entidad Entidad { get; set; }
public CampoAdicional Campo { get; set; }
public string ValorTexto { get;set ; }
public int ValorNumerico { get; set; }
}
However I am not sure if this is the correct model classes for my scenario and whether it would create the tables correctly.
EF works with lazy load so at least there are several "virtual" missings.
In all properties that does not use primitive types and in collections.
Can you extend more than one entity with additional fields? If so you need that ValorCampo contains the entity (Entidad) but the entity should have the Id so you need to move the Id from Empresa to Entidad. Otherwise you need ValorCampo should refer to Empresa not to Entidad
I have one table "Adverts" which stores basic info about adverts (eg: Name, Excerpt, Creation date...), and I need to store more detailed info in a separate table, But, here's my problem. Adverts can be different by type (sell, buy, rent, ...), category (residential, commercial, ...), so, detailed info is also different (eg: Commercial Advert don't need kitchen area property). I want to make few models which will describe detailed info for specific type or category
Here's my Adverts model:
[Table("Adverts_Adverts")]
public class Advert {
[Key]
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public Guid AdvertId { get; set; }
public virtual Metadata Metadata { get; set; }
[Required]
[DataType(DataType.Text)]
public String Name { get; set; }
[DataType(DataType.Html), AllowHtml]
public String Content { get; set; }
[ForeignKey("Section")]
public Guid SectionId { get; set; }
public virtual Section Section { get; set; }
[ForeignKey("Category")]
public Guid CategoryId { get; set; }
public virtual Category Category { get; set; }
[ForeignKey("Type")]
public Guid TypeId { get; set; }
public virtual Type Type { get; set; }
public Decimal Price { get; set; }
[DataType("Enum")]
public Currency Currency { get; set; }
[ForeignKey("Details")]
public Guid DetailsId { get; set; }
public virtual ?????????? Details { get; set; }
[ForeignKey("User")]
public String UserId { get; set; }
public virtual User User { get; set; }
[ReadOnly(true)]
[DataType(DataType.DateTime)]
public DateTime Added { get; set; }
[ReadOnly(true)]
[DataType(DataType.DateTime)]
public DateTime Updated { get; set; }
public Int32 Views { get; set; }
[ReadOnly(true)]
public Status Status { get; set; }
...
}
here's my detailed info model for residential adverts:
[Table("Adverts_Details")]
public class ResidentialDetails {
[Key]
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public Guid DetailsId { get; set; }
[ForeignKey("Advert")]
public Guid AdvertId { get; set; }
public virtual Advert Advert { get; set; }
[Required]
public Int32 Storeys { get; set; }
[Required]
public Int32 Floor { get; set; }
[Required]
public Int32 Rooms { get; set; }
[Required]
public Decimal TotalArea { get; set; }
[Required]
public Decimal LivingArea { get; set; }
[Required]
public Decimal KitchenArea { get; set; }
...
}
and this may be for commercial adverts:
[Table("Adverts_Details")]
public class CommercialDetails {
[Key]
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public Guid DetailsId { get; set; }
[ForeignKey("Advert")]
public Guid AdvertId { get; set; }
public virtual Advert Advert { get; set; }
[Required]
public Int32 OfficesCount { get; set; }
[Required]
public Int32 Floor { get; set; }
[Required]
public Decimal TotalArea { get; set; }
...
}
So, how can I access both, ResidentialDetails and CommercialDetails, data within advert's property "Details"?
(Thank in advance)
This is an architecture problem, which is hard to answer without a complete understanding of your business rules. I can give you some general advice that will hopefully help you along.
As much as possible, remove complexity. I'm not sure what a "kitchen area property" is, but can you generalize it at all? Based upon context, you can call it something different, use it differently, etc. but if it's just a text field, then you can repurpose it in other contexts. Maybe for a residential advert it's "kitchen area" while maybe for commercial it's "break room area". (I really have no idea what this property is for, but I'm just trying to make the point that the same property can have a similar but slightly different meaning in different contexts).
If you can't generalize, then you'll need to start working on inheritance strategies. Create an object graph. How are these types and categories of adverts related. How are they different. Which ones are supergroups of others, etc.? Again, I don't know anything about the business rules at play, but maybe you need classes like Advert, ResidentialAdvert : Advert and CommercialAdvert : Advert. Then, you can add additional properties to these subclasses as necessary.
You'll also need to decide on a relational strategy. By default, EF will implement simple inheritance as STI (single-table inheritance, aka table per hierarchy or TPH for short). In other words, with the classes above, you would end up with an Adverts table with a Discriminator column. The value for this column would be one of "Advert", "ResidentalAdvert", or "CommercialAdvert", indicating which class should be instantiated, but all of the columns for all of the subclasses would reside in the same table. The benefit is that no joins are necessary, but the detriment is that all additional columns on your subclasses must be nullable or have default values. Other possible strategies would include, table per type (TPT), a compositional strategry, or table per concrete type (TPC), where every subtype gets its own unique table with all the fields from all supertypes.
So i am working with entity code firest and i have a user class that looks like this:
public class User
{
[Key]
public string Username { get; set; }
public string FirstName { get; set; }
public string LastName { get; set; }
public DateTime LastModified { get; set; }
}
I am trying to make a "friends table" and no matter what i come up with I get an error on the db creation. This is what I Currently have in the Friends Class:
public class Friend
{
[Key, ForeignKey("User")]
public virtual User MyUser { get; set; }
[Key,ForeignKey("User")]
public virtual User MyFriend { get; set; }
public bool IsAccepted { get; set; }
}
this is the error i get:
The ForeignKeyAttribute on property 'MyUser' on type 'Core.Model.Friend' is not valid. The foreign key name 'User' was not found on the dependent type 'Core.Model.Friend'. The Name value should be a comma separated list of foreign key property names.
What am I missing?
You need to use the Column attribute. Normally I would use something like this:
public class Friend
{
[Key]
[Column(Order = 0)]
public int MyUserId { get; set; }
[Key]
[Column(Order = 1)]
public int MyFriendId { get; set; }
[ForeignKey("MyUserId")]
public virtual User MyUser { get; set; }
[ForeignKey("FriendId")]
public virtual User MyFriend { get; set; }
public bool IsAccepted { get; set; }
}
I'm not sure what would happen if you map the Column attribute directly to the navigation property. You can try it if you like and see what happens.. but the above generally works for me.
Alternatively, if you change to use fluent mapping, you can do something like this:
HasKey(u => new { u.MyUserId , u.MyFriendId });