Store foreign key depending on the enum - c#

Few days ago I switch (due to my student project) to Entity Framework and I have to develop Entity whos foreign key will depend of ENUM value , I spend last two day trying to figure it out but unfortunately I was not able to figure it out , so I hope someone here will be able to help me with it :)
Seller.cs
Public int Id {get;set}
public string FullName {get;set}
public string Country {get;set}
public int CentralizationId {get;set}
[ForeignKey("CentralizationId"}
public Centralization Centralization {get;set;}
Buyer.cs
Public int Id {get;set}
public string FullName {get;set}
public string Country {get;set}
public CurrencyType CurrencyType {get;set;}
public int CentralizationId {get;set}
[ForeignKey("CentralizationId"}
public Centralization Centralization {get;set;}
Centralization.cs
public int Id { get; set; }
public int AuthorId { get; set; }
[ForeignKey("UserId")]
public Author Author { get; set; }
public Type Type { get; set; }
[ForeignKey("TypeId")]
public int TypeId { get; set; }
public enum Type
{
Selling = 1,
Buying = 2,
}
So basically what I need is that if Type = 1 on typeId to be Seller.Id where later via getAllIncluding I will be able to get his date (somehow typeId should depend on Type)
I tried using Getters and Setters but didn`t help at all
How I think at the end should looks like for example :
Type = 1 (Selling)
[ForeignKey("TypeId")]
public Seller typeId {get;set;}
Hope someone here will be able to help me :)
Have a nice day !

You can use an Enum for a FK/PK. EF will typically treat this as an int in the database.
For instance if in a database I want a "Type" table with an TypeId to enforce referential integrity but I don't necessarily want a "Type" entity, I just want an enumeration to represent that type:
public enum Type
{
None = 0,
Buying = 1,
Selling = 2
}
public class Something
{
public int Id { get; set; }
// ...
[Column("TypeId")]
public Type Type { get; set; }
}
I would recommend a name that is more descriptive than just "Type" to avoid naming collisions with system types. I.e. "OrderType", "TransactionType" etc.
The [ForeignKey] attribute is used to nominate the FK fields for relationships between entities. In this case we aren't using an entity, but an enumeration so we just use [Column] to tell EF what the column should be named.
The above works for DB-First implementations. If you want to use Code-First where the EF definitions will be responsible for creating the schema, you will need to manually add migrations to create the "Type" table and set up the FK between it and your other table. Otherwise EF will merely leave that TypeId column as a regular column on the "Something" table.
Alternatively you can use Enums as a FK/PK which can allow you to define additional properties to go along with the relationship, and let Code First manage the table relationships as well:
public enum TypeIds
{
None = 0,
Buying = 1,
Selling = 2
}
public class Type
{
[Key, DatabaseGenerated(DatabaseGeneratedOption.None)]
public TypeIds TypeId { get; set; }
public string Description { get; set; }
}
public class Something
{
[Key]
public int Id { get; set; }
// ...
[ForeignKey("Type")]
public TypeIds TypeId { get; set; }
public virtual Type Type { get; set; }
}
This allows you to use the Enumeration as both a PK of an entity containing relevant details about the enumeration, and as the FK within the related entities.
Enumerations as PK/FK are useful in cases where you might have business logic dependent on those states. The important thing to consider with enumerations is that the Application rather than the database should be in control of the ID assignment, hence the use of [DatabaseGenerated(DatabaseGeneratedOption.None)] on the PK of our Type entity/table. This tells EF that the database should not use an Identity but rely on the consumer (our code) to set these IDs. We want to ensure that it is as clear as possible that our Enum is the source of truth for these keys.

Related

EF foreign key reference using Id vs object

What is the difference between foreign key reference using Id vs object.
For example:
FK relation using Id
class Product
{
public int Id { get; set; }
public string Name { get; set; }
public string PhoneNumber { get; set; }
public int CategoryId { get; set; }
}
vs
FK relation using object
class Product
{
public int Id { get; set; }
public string Name { get; set; }
public string PhoneNumber { get; set; }
public Category Category { get; set; }
}
I have noticed from database that by using Id property the column essentially becomes non-null field.
Is that the only difference?
I think we can't query multiple tables at once i.e.querying related data?
When should I choose to use either options?
In your first example you're not adding a relationship, just an integer property named CategoryId.
In your second example, Entity Framework will create an integer column named "Category_ID", but you will be not be able to see this property in your model, so I like to explicitly add it my self and be able to use it along with the navigation property.
class Product
{
public int Id { get; set; }
public string Name { get; set; }
public string PhoneNumber { get; set; }
[ForeignKey("Category")]
public int CategoryId { get; set; }
public Category Category{get;set;}
}
This way you can also control the data type of CategoryId, so you could make it optional (nullable)
public int? CategoryId { get; set; }
*The foreign key data annotation is not needed, unless you have property or navigation property names that do not follow the naming convention for foreign key property names (Bardr), it doesn't harm to explicitly declare it either for clarity purposes
This implies that you're creating a 1 to many relationship (1-*) with products and categories, so in your Category class you would be adding a collection navigation property for products
class Category
{
public int Id{ get; set;}
public string Name{ get; set; }
...
public ICollection<Product> Products{get; set;}
}
Basically it depends on your use case and what type of loading related data you choose. Whether you use Id or object reference or full relationship on both sides (Id and object) it depends on your overall application architecture. If you wil go and use full or object reference everywhere, you will (probably) end up with a mess, and you won't know whether you should query for some entities using their repository or if it'll be okay to include them to some other query. I highly recommend you to take a look at this book, especially chapter 19 (Aggregates) and 21 (Repositories). There you have an in-depth explanation of what I meant and much more. (This does not only apply to applications built in DDD way)

Have Entity Framework use Type property instead creating column per Derived Type

Say I have an abstract Person Table, and derived tables, Student and Teacher, Admin etc. Person has a enum property PersonType with values for each derived class, and a virtual collection Events.
Now when Entity Framework creates the Events table, it creates a column for each class derived from Person, eg. Student_Id, Teacher_Id, Admin_Id etc.
If I have a dozen derived classes, then that's a dozen extra columns, only 1 of which is ever in use.
How do I tell Entity Framework to instead refer to the PersonType property in conjunction with the Id instead of creating all these unnecessary columns?
So it has simple solution with DataAnnotations, if i understood you correctly.
[Column("TableID")]
I'm going to give you a working example from a project of mine. So i have an abstract Class which has called Trip. Cruise and Hotel classes are derived from Trip class. I need to Store Comments on a single table and as you have concerned i dont need CruiseID nor HotelID on Comments table so i named TripID with Column Annotation and entity framework will create table magically if we annotated well
public class Comment
{
public int CommentID { get; set; }
[Column("TripID")]
[ForeignKey("Trip")]
public int TripID { get; set; }
public virtual Trip Trip { get; set; }
public string Text { get; set; }
}
public class CruiseComment : Comment
{
[Column("TripID")]
[ForeignKey("Cruise")]
public int CruiseID { get; set; }
public virtual Cruise Cruise { get; set; }
}
public class HotelComment : Comment
{
[Column("TripID")]
[ForeignKey("Cruise")]
public int HotelID { get; set; }
public virtual Hotel Hotel { get; set; }
}
It's not necessary to use ForeignKey annotation. It's needed when you have different namings.

Determining if a model should have foreign keys / navigation properties

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.

EF abstract base class with key where derived class has different key

Time for a dumb question. I think the database design is screwy, but there isn't much I can do about that part of it. I have a table in the database "Table1" and then "Table2" which is essentially an extension of Table1 (I also have Table3, Table4, etc). My problem is that Table2 has it's own unique key, even though it's a one for one relationship. Then Table2Component uses Table2Id as it's foreign key. However, when I try to use that in my code I think it's pointing to Table1Id. I get the error message:
System.Data.Entity.Edm.EdmAssociationConstraint: : The types of all properties in the Dependent Role of a referential constraint must be the same as the corresponding property types in the Principal Role. The type of property 'Table2Id' on entity 'Table2Component' does not match the type of property 'Table1Id' on entity 'Table2' in the referential constraint 'Table2Component_Table2'.
Here is the code
[Table("Table1")]
public abstract class Table1
{
[Key]
[Column("table1_id")]
public string Table1Id { get; set; }
[Column("name")]
public string Name { get; set; }
[Column("type_cd")]
public string TypeCode { get; set; }
}
[Table("Table2")]
public class Table2 : Table1
{
[Key]
[Column("table2_id")]
public int Table2Id { get; set; }
[ForeignKey("Table1Id")]
public virtual Table1 Table1 { get; set; }
// this table also has a table1_id column
// but I guess I don't need it here, correct?
[Column("column1")]
public string Column1 { get; set; }
public virtual ICollection<Table2Component> Table2Components { get; set; }
}
[Table("Table2Component")]
public class Table2Component : ISubItem
{
[Key]
[Column("table2_component_id")]
public int Table2ComponentId { get; set; }
[Column("table2_id")]
public int Table2Id { get; set; }
[Column("description")]
public string Description { get; set; }
public bool Required { get { return true; } }
[ForeignKey("Table2Id")]
public virtual Table2 Table2 { get; set; }
}
Any suggestions? Should I be more forceful in trying to get the database changed?
Started as comment.... finish as simple answer, since no one else jumped in.
Search for Entity Framework 1:1 relationship eg https://stackoverflow.com/a/14997417/1347784 the restriction is both tables must have the same foreign key when using 1:1
No not necessarily better database design. It is Just the why the EF team built the framework. Ive learnt to live with the restrictions. In code first scenario, no big deal. Try the powertool to reverse engineer the alternative approach when you start with the DB. EF will use 1:M even though you may see it as 1:1. Also OK in my view.

How to store two same entities in a database? Entity framework, c#

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!

Categories

Resources