All,
Is it possible to use the same FK for two tables.
Probably it is not a good practice, but I have a two different classes that can be both booked:
public class Course {
public Course() {
BookingRefs = new HashSet<BookingRef>();
}
public long Id { get; set; }
public string Title { get; set; }
// other props ...
[InverseProperty(nameof(BookingRef.Course))]
public virtual ICollection<BookingRef> BookingRefs { get; set; }
}
public class GiftCard {
public GiftCard() {
BookingRefs = new HashSet<BookingRef>();
}
public long Id { get; set; }
public string Prop1 { get; set; }
public int Prop2 { get; set; }
// other props ...
[InverseProperty(nameof(BookingRef.Course))]
public virtual ICollection<BookingRef> BookingRefs { get; set; }
}
// this is the bookin reference for a Course or an GiftCard
public class BookingRef {
public BookingRef() {
}
public long Id { get; set; }
// other props ...
/// <summary>The item (usually the course but theoretically anything with a long id)</summary>
public long? ItemId { get; set; }
// maybe a generic Object?
[ForeignKey(nameof(ItemId))]
public Object GiftCard { get; set; }
// maybe 2 items possibly null?
[ForeignKey(nameof(ItemId))]
public Course Course { get; set; }
// maybe 2 items possibly null?
[ForeignKey(nameof(ItemId))]
public GiftCard GiftCard { get; set; }
}
Is it possible to use the same FK for two tables
No. The relational model doesn't allow that. You can introduce a superclass of all your bookable things and have a FK to that, but you shouldn't do that just get a single collection rather than multiple.
Think of it from the relational data perspective. How would the database know what table an "Item ID" pointed at? How would it index it?
This would be a case for using a null-able FK to each related table on the booking. These FKs do not need to reside in the entity, just the navigation properties. You can leverage .Map(x => x.MapKey) in EF6 or .HasForeignKey("") in EF Core to leverage a shadow property.
This does not enforce if you want a booking to only be associated to a course or a gift card but not both. That would need to be catered for at the application level, and I would recommend using a scheduled maintenance task to evaluate the data for violations to that rule. (Look for bookings holding both a course ID and a gift card ID for example)
You can alternatively keep the joins "loose" and evaluated by the application based on a discriminator similar to an inheritance model. (ItemId + ItemType) However you have to resolve the relationship load separately in your application based on the ItemType and lose out on any FK, indexing, and data integrity checks in the database. This could be a significant performance & maintenance cost to save adding a couple FKs.
Related
During my project in which I make some sort of webshop, I've came across a problem with my .NET backend where I use Entity Framework Code First with Fluent API.
In my frontend, Orders can be made and are passed to my backend where they end up as a Order object (code below). This Order contains a User and a Dictionary where Items and their ordered quantities are stored. My current goal is to store those Orders in my database to retrieve an Order history.
My understanding is that by itself, EF can't map a Dictionary. Being a student and having done mostly frontend, I don't really know how to tackle this.
I've tried converting that Dictionary to a List of ItemWrappers (containing both the Item and the amount) and making 2 tables: Order (OrderId, UserId) and OrderItem (OrderId, ItemId, Amount). This converts the Many-to-Many (Users to Items and Order is derived from the relation attribute) to a One-to-Many (Order to OrderItem).
I understand this from a purely database perspective. I could have managed if I were to write all the queries myself, but given that EF adds some abstraction to that, I am a bit lost. How do you suggest I do this?
The code is simplified to only show the class structure. Id is always generated on add and is used as primary key.
public class User {
public int Id { get; set; }
public string Name { get; set; }
}
public class Item {
public int Id { get; set; }
public string Name { get; set; }
}
public class Order {
public IList<OrderItemWrapper> ItemsList { get; set; }
//Either one of these 2
public Dictionary<Item, int> Items { get; set; }
public User User { get; set; }
public int Id { get; set; }
}
public class OrderItemWrapper {
public Item Item { get; set; }//Will use ItemId as key
public int Amount { get; set; }
}
Could you please go through my explanation for many-to-many relationship here.
Is the following tree of SchoolContext correct?
I have a domain model that has a collection of entities configured in the normal 1 to many relationship, however I want to store a reference to a specific item in that collection using a FK in this model
The list as defined in the model
public ICollection<SLWOUpdate> Updates { get; set; }
The reference to the specific item in the list
public int? SLWOUpdateId { get; set; }
[ForeignKey("SLWOUpdateId")]
public virtual SLWOUpdate LastUpdate { get; set; }
Of course the code is responsible for updating the specific item as opposed to having EF do it.
Is this kind of relationship configurable in EF?
The reason I want to do this is for querying filtering purposes as part of complex query that must execute as one statement
Ended up adding a new domain model to represent the LastUpdate which simply holds a primary key to this entity and a FK to the LastUpdate
New Domain Model to represent the Last Update
public virtual SLCurrentWOUpdate LastUpdate { get; set; }
public class SLCurrentWOUpdate
{
[Key]
public int SLWorkOrder_Id { get; set; }
public SLWorkOrder SLWorkOrder { get; set; }
public int? SLWOUpdateId { get; set; }
[ForeignKey("SLWOUpdateId")]
public SLWOUpdate SLWOUpdate { get; set; }
}
I can query this as part of a larger more complex set of predicates... I just have to reach into the model one reference deeper:
db.SLWorkOrders
.Where(t => t.TAutoDeclined != null && t.TClosedPendingPayment != null)
.Where(t => t.LastUpdate.SLWOUpdate.UpdateStatusType.SystemName == "CHANGE_PRIORITY");
Feels kind of hackish.. but it works..
I use Entity Framework Code First to access my SQL Server database. The "Client" table currently has about 90 columns:
[Table("Clients")]
public class Client
{
public int Id { get; set; }
public string Property1 { get; set; }
...
public string Property90 { get; set; }
}
I have decided to vertically partition this table into 3 tables, because often not all the properties are used. However, I still have legacy code (that I can't change right now) that expects the full Client object with all 90 columns.
My solution so far is to split the Client class into 3 classes corresponding with the new tables, and then use Table Per Type inheritance to allow the legacy code to access the Client object as though the original Clients table is still there:
[Table("Clients")]
public class Client: Client1
{
public int Id { get; set; }
public string Property1 { get; set; }
...
public string Property30 { get; set; }
}
[Table("Client1s")]
public class Client1: Client2
{
public int Id { get; set; }
public string Property31 { get; set; }
...
public string Property60 { get; set; }
}
[Table("Client2s")]
public class Client2
{
public int Id { get; set; }
public string Property61 { get; set; }
...
public string Property90 { get; set; }
}
However, this somehow seems a bit clunky to me.
Is there a more elegant way to achieve vertical partitioning with Entity Framework Code First?
So, considering you refer to the existing approach as being used by "legacy" systems and your new partitioned approach is most likely intended to be the new "correct" way going forwards, my advice would be to keep them as separated as possible.
What you could look to do is replace the existing monolithic Clients table with a database view that joins the 3 separate, partitioned tables back together. Then you can hook up the existing Clients class in all it's former glory to the view, leaving your legacy systems relatively untouched, in theory.
I'd also recommend ditching the inheritance idea and leaving the 3 new partitioned classes completely independent of one another. Otherwise, both legacy and new systems will be extremely sensitive to any changes being made to classes and properties within that entire inheritance chain.
By doing it this way you are then free to change and evolve the new classes independently and modify any underlying table structures however you see fit in the future. Providing you maintain the views integrity and consistency, your legacy systems should continue to function as normal without any repercussions or regressions, mostly :-)
In my humble experience, shielding the old from changes in the new far outway the slight inconveniences of having some code duplication and stricter boundaries.
To answer your elegance question more directly I'd say that insulating your classes against unnecessary coupling and avoiding the "ripple effect" yields the more elegant solution.
Anyway, I hope it helps.
I'm going to assume your actual classes have much more meaningful property names, right?
// LEGECY SYSTEMS
[Obsolete("Use the newer partitioned classes going forwards")]
[Table("vClients")]
public class Client
{
public int Id { get; set; }
public string Property1 { get; set; }
...
public string Property90 { get; set; }
}
// NEW STRUCTURES
[Table("Client1s")]
public class Client1
{
public int Id { get; set; }
public string Property1 { get; set; }
...
public string Property30 { get; set; }
}
[Table("Client2s")]
public class Client2
{
public int Id { get; set; }
public string Property31 { get; set; }
...
public string Property60 { get; set; }
}
[Table("Client3s")]
public class Client3
{
public int Id { get; set; }
public string Property61 { get; set; }
...
public string Property90 { get; set; }
}
Updating the the base tables via the view can be done using some INSTEAD OF triggers, like so:
CREATE TRIGGER ClientsLegacyInsertAdapter on vClients
INSTEAD OF INSERT
AS
BEGIN
BEGIN TRANSACTION
INSERT INTO Client1s
SELECT Id, PropertyA1, Property2, ..., Property30
FROM inserted;
INSERT INTO Client2s
SELECT Id, Property31, Property32, ..., Property60
FROM inserted;
INSERT INTO Client3s
SELECT Id, Property61, Property62, ..., Property90
FROM inserted;
COMMIT TRANSACTION
END
You should be able to use the same technique for UPDATE and DELETE commands also.
Many times I have a general purpose entity that other entities contain a collection of. I don't want to have a new collection entity for each parent entity type that needs it but would like to re-use a single general purpose entity. For performance reasons, I also don't want to explicitly define many-to-many relationships as in this answer. The simplest example would be a collection of strings.
public class MyString
{
public Guid Id { get; set; }
public string Value { get; set; }
}
public class MyEntity
{
public Guid Id { get; set; }
public virtual List<MyString> { get; set; }
}
public class MyOtherString
{
public Guid Id { get; set; }
public string Value { get; set; }
}
public class MyOtherEntity
{
public Guid Id { get; set; }
public virtual List<MyOtherString> { get; set; }
}
I'd really like to combine MyString and MyOtherString into a single entity:
public class GeneralPurposeString
{
public Guid Id { get; set; }
public string Value { get; set; }
}
public class MyEntity
{
public Guid Id { get; set; }
public virtual List<GeneralPurposeString> { get; set; }
}
public class MyOtherEntity
{
public Guid Id { get; set; }
public virtual List<GeneralPurposeString> { get; set; }
}
Except now I'm going to have an additional foreign key in GeneralPurposeString for every entity that contains a collection of GeneralPurposeString.
What I would like would be a way to have an additional parent category column on the GeneralPurposeString table (but not the entity) that would specify which entity the item belongs to. I use Guid for primary keys, so the tables could look something like this:
CREATE TABLE [GeneralPurposeString]
(
[Id] uniqueidentifier NOT NULL
CONSTRAINT PK_GeneralPurposeString PRIMARY KEY,
[ParentEntityCategory] uniqueidentifier NOT NULL,
[ParentEntityId] uniqueidentifier NOT NULL,
[Value] nvarchar(MAX)
)
And some how in Code First to specify that MyEntity has a certain category, and that it's collection of GeneralPurposeString uses that category, and MyOtherEntity uses another category (Guid) for it's collections of GeneralPurposeString.
The key would be that GeneralPurposeString could be a collection in any other entity and that loading the parent entity and including the collection would automatically load without having to explicitly specify the category.
The purposes for all of this are
Allow .NET code to have GeneralPurposeString code that wasn't replicated everywhere (actual utility or business logic code). This can probably also be accomplished through inheritance and explicit mapping but that would still leave multiple tables in the database (see #2).
Have only one table in the database for GeneralPurposeString. This is more of a tidiness issue. Performance would possibly be better with multiple tables, but indexing on ParentEntityCategory/ParentEntityId and covering Value should be good performance for lookups.
Not have to explicitly code this relationship and the lookups everywhere it's needed.
I'm thinking if I can get over #2 and be OK with a separate table behind the scenes and implementing a derived class, that will be the simplest route to go.
So just:
public class GeneralPurposeString
{
public Guid Id { get; set; }
public string Value { get; set; }
}
// It's just a GeneralPurposeString with a fancy MyEntity membership pin
public class MyEntityString: GeneralPurposeString {}
public class MyEntity
{
public Guid Id { get; set; }
public virtual List<MyEntityString> Strings { get; set; }
}
// Cool GeneralPurposeStrings belong to MyOtherEntity
public class MyOtherEntityString: GeneralPurposeString {}
public class MyOtherEntity
{
public Guid Id { get; set; }
public virtual List<MyOtherEntityString> Strings { get; set; }
}
public class MyContext: DbContext
{
public DbSet<MyEntity> MyEntities { get; set; }
public DbSet<MyOtherEntity> MyOtherEntities { get; set; }
}
I don't have to add the derived classes to the DbContext and the tables get named with the plural of the derived class by default, so it's actually pretty straight forward.
My previous train of thought with the Parent Category would require additional coding/annotation even if EF supported it. This uses purely convention and nothing extra needed in annotations or in OnModelCreating().
I'm not seeing any harm in extra tables at this point in time. I don't see a need (currently) to have all of the data in one table for reporting, but that really depends on the type of general purpose entity, so I may need to revisit this in the future, or I may just take the many-to-many route if I do need the data in one table.
And I can still have:
public static class GeneralPurposeStringExtensions
{
public static void SassThatHoopyFrood(this GeneralPurposeString s)
{
// do stuff
}
}
I want to know what is the difference between creating classes with or without using "hashset" in constructor.
Using code first approach (4.3) one can creat models like this:
public class Blog
{
public int Id { get; set; }
public string Title { get; set; }
public string BloggerName { get; set;}
public virtual ICollection<Post> Posts { get; set; }
}
public class Post
{
public int Id { get; set; }
public string Title { get; set; }
public DateTime DateCreated { get; set; }
public string Content { get; set; }
public int BlogId { get; set; }
public ICollection<Comment> Comments { get; set; }
}
or can create models like this :
public class Customer
{
public Customer()
{
BrokerageAccounts = new HashSet<BrokerageAccount>();
}
public int Id { get; set; }
public string FirstName { get; set; }
public ICollection<BrokerageAccount> BrokerageAccounts { get; set; }
}
public class BrokerageAccount
{
public int Id { get; set; }
public string AccountNumber { get; set; }
public int CustomerId { get; set; }
}
What is hashset doing here?
should i use hashset in the first two models also?
is there any article which shows the application of hashset?
Generally speaking, it is best to use the collection that best expresses your intentions. If you do not specifically intend to use the HashSet's unique characteristics, I would not use it.
It is unordered and does not support lookups by index. Furthermore, it is not as well suited for sequential reads as other collections, and the fact that it allows you to add the same item multiple times without creating duplicates is only useful if you have a reason to use it for that. If that is not your intention, it can hide misbehaving code and make problems difficult to isolate.
The HashSet is mostly useful in situations where insertion and removal times are very important, such as when processing data. It is also extremely useful for comparing sets of data (again when processing) using operations like intersect, except, and union. In any other situation, the cons generally outweigh the pros.
Consider that when working with blog posts, inserts and removes are quite rare compared to reads, and you generally want to read the data in a specific order, anyway. That is more or less the exact opposite of what the HashSet is good at. It is highly doubtful that you would ever intend to add the same post twice, for any reason, and I see no reason why you would use set-based operations on posts in a class like that.
The HashSet does not define the type of collection that will be generated when you actually fetch data. This will always be of type ICollection as declared.
The HashSet created in the constructor is to help you avoid NullReferenceExceptions when no records are fetched or exist in the many side of the relationship. It is in no way required.
For example, based on your question, when you try to use a relationship like...
var myCollection = Blog.Posts();
If no Posts exist then myCollection will be null. Which is OK, until you fluent chain things and do something like
var myCollectionCount = Blog.Posts.Count();
which will error with a NullReferenceException.
Where as
var myCollection = Customer.BrokerageAccounts();
var myCollectionCount = Customer.BrokerageAccounts.Count();
will result in and empty ICollection and a zero count. No exceptions :-)
I'm fairly new to Entity Framework but this is my understanding. The collection types can be any type that implements ICollection<T>. In my opinion a HashSet is usually the semantically correct collection type. Most collections should only have one instance of a member (no duplicates) and HashSet best expresses this. I have been writing my classes as shown below and this has worked well so far. Note that the collection is typed as ISet<T> and the setter is private.
public class Customer
{
public Customer()
{
BrokerageAccounts = new HashSet<BrokerageAccount>();
}
public int Id { get; set; }
public string FirstName { get; set; }
public ISet<BrokerageAccount> BrokerageAccounts { get; private set; }
}