I am working on a system that sells products. This system has products, with subclasses for every producttype.
public abstract class Product
{
public int ProductId { get; set; }
public string naam { get; set; }
public string barcode { get; set; }
}
public class Card :Product
{
[Display(Name = "Cardnumber")]
public int nummer { get; set; }
public Kaliber kaliber { get; set; }
}
Furthermore, i want to keep a history of all the products i sold with all the data that was correct at that moment.
public class Transaction
{
public int transactionId { get; set; }
public Member member { get; set; }
public virtual ICollection<Product> producten { get; set; }
public double totaalprijs { get; set; }
public DateTime tijdstip { get; set; }
public string kantoorMedewerker { get; set; }
}
The problem is, that entity now makes a FK in Product to Transaction. That's not what i want. I want a seperate table for each of them; a Products table and a SoldProducts table.
I already tried this in my productContext:
public DbSet<Product> producten { get; set; }
public DbSet<Product> uitgifte_producten { get; set; }
That's not possible, because EF doesn't allow multiple object sets per type .
This seems like something trivial, but i can't figure it out.
Making two classes, one Product and one Soldproduct, with both of having subclasses of the producttypes, seems rather ugly. I tried it, but VS2012 complains that it can't converty Product to SoldProduct.
What seems to be a good idea to do this in C#, .net 4.0 and EF?
Why not just link to products from your transaction class using a basic many-to-many relationship?
Using EF Fluent API, you can add a config class with the following:
public class TransactionConfig : EntityTypeConfiguration<Transaction>
{
public TransactionConfig ()
{
this.HasMany(t => t.Products)
.WithMany()
.Map(x =>
{
x.MapLeftKey("TransactionId");
x.MapRightKey("ProductId");
x.ToTable("TransactionProducts");
});
}
}
Then, override the OnModelCreating function of you DbContext with:
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
modelBuilder.Configurations.Add(new TransactionConfig());
}
I assume you are using EF Code First.
When you have a collection of products in your Transactions table, EF will take this to be a typical one to many relationship.
I would add a TransactionDetail table to which I would copy all the details I need from Product:
public class TransactionDetail{
public int TransactionId { get; set; }
public int ProductId { get; set; }
public string naam { get; set; }
public string barcode { get; set; }
}
Update in response to comment:
Still assuming you are using code-first. In your scenario, you could use the TransactionDetail class above as a base class then have more derived classes in respect to the types of products. You will be able to capture all the required details per product and you will only have one more extra table in your db.
I don't know of any way to do something like this in EF.
If you really want to keep all the product data for each transaction, I'd suggest creating product copies for every transaction and then storing them in the DB and referencing them from the transaction. You might also think about creating a self-reference on product, which could then point to the "actual product" for "transaction products".
Another approach I can think of is storing the product history instead of creating a copy of the product, i.e. creating product copies on product change instead of on transaction creation. This way, when creating a transaction you always link to the current version of your product.
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);
I'm struggling with composite keys and extra fields being generated by Entity Framework. I have a question about something which I think is odd.
Let's say I have a one to many relationships with these classes:
File (dossier)
[Table("Dossier")]
public class Dossier
{
[Key]
public string Dossiernummer { get; set; }
[Key]
public string Dossierversie { get; set; }
[Required]
public string Dossierreferentie { get; set; }
[Required]
public string Relatienr { get; set; }
public ICollection<Artikel> Artikels { get; set; } ();
}
And my artikel (article) class:
[Table("Artikel")]
public class Artikel
{
[Key]
public string Artnr { get; set; }
[Key]
public string ArtVersie { get; set; }
public string ArtOmschrijving { get; set; }
public Dossier Dossier { get; set; }
public string Dossiernummer { get; set; }
}
I'm using migrations and a code first approach. For some reason using migrations creates a dossiernummer1 column in the artikel table. I don't understand why and would like it gone. Does anyone know how?
Another thing which I prefer not to have is the second primary key in my artikel table. It puts both keys from the dossier table in the artikel table yet I only want to use Dossiernummer as a foreign key. Do you know how to change this?
When getting all the dossiers from the context I notice something odd as well. When I look into a dossier object the artikels list is empty, even though data exists in the database for that. Is it normal you have to initialize it yourself?
Thanks for any help and info in advance.
Kind regards,
you must use fluent API for set relations and add ColumnAttribute to order keys :
[Table("Artikel")]
public class Artikel
{
[Key]
[Column(Order = 1)]
public string Artnr { get; set; }
[Key]
[Column(Order = 2)]
public string ArtVersie { get; set; }
public string ArtOmschrijving { get; set; }
public Dossier Dossier { get; set; }
public string Dossiernummer { get; set; }
}
[Table("Dossier")]
public class Dossier
{
[Key]
[Column(Order = 1)]
public string Dossiernummer { get; set; }
[Key]
[Column(Order = 2)]
public string Dossierversie { get; set; }
[Required]
public string Dossierreferentie { get; set; }
[Required]
public string Relatienr { get; set; }
public ICollection<Artikel> Artikels { get; set; }
}
in your dbcontext override OnModelCreating method:
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
base.OnModelCreating(modelBuilder);
builder.Entity<Dossier>()
.HasMany(x => x.Artikels)
.WithOne(a => a.Dossier)
.HasForeignKey(a => new { a.Dossiernummer, a.Artnr });
builder.Entity<Artikel>()
.HasKey(x => new {x.Artnr,x.ArtVersie});
builder.Entity<Dossier>()
.HasKey(x => new {x.Dossiernummer,x.Dossierversie});
}
Dossiernummer1:
Artikel has a Dossier. EF knows an K relationship must be set up. This requires that the PK of Dossier must be included in Artikel and wants to add it. It finds you've already put in a field with that name (what for it has no idea) and so it adds it as Dossiernummer1. You should not add Dossiernummer to Artikel - unless you you actually need one for something else - as the only reason it's there is to be an FK. EF will take care of that for you.
Adding Dossierversie to Artikel:
It thinks that the PK of Dossier is Dossiernummer + Dossierversie, and so to point to the correct Dossier it must have both of tem. I don't use code-first so I can't advise you on a) how to specify a PK and another, separate index (I assume that's what you want) versus a compound PK (which is what you appear to have).
Dossier.Artikels is empty: That's the way EF works, known as lazy loading. It gets the 'root' objects by not anything owned by them at first. Once your code accesses an Artikels collection it should load them (for that Dossier) at that point. This prevents EF pulling in what could be a large percentage of your database Imaging an ECommerce system. Getting a Customer list would pull in all Orders related to cutomers in that list; all order lines owned by those orders; all Product data related to the products on those order lines and so on. This would not be a good thing. Instead it just gets the things you've specifically mentioned and then pulls in related items as needed.
Incidentally, when looking as an unloaded collection as an attribute of the owner (e.g. looking at Artikels on a loaded Dossier), the debugger in VS tells me that examining the collection will result in it being loaded and gives me the option to continue or not.
I have threeType MaliOp, LoanEBD, PrmEBD
public class MaliOp
{
public int Id { get; set; }
public int OldId { get; set; }
public byte MaliTable { get; set; }
public string Date { get; set; }
public short OpState { get; set; }
}
public class LoanEBD : MaliOp
{
public int? BId { get; set; }
public int? Loan { get; set; }
public int? PayerBimeGozar { get; set; }
[NotMapped]
public int OldId { get; set; }
}
public class PrmEBD : MaliOp
{
public int? PayerBimeGozar { get; set; }
public int? BId { get; set; }
[NotMapped]
public int OldId { get; set; }
}
the two Entity PrmEBD and LoanEBD Inherit from MaliOp. I want create DBContext in Entity Framework by by using this three types.
I have three Table in Database fro each one of them.
I don't want to use any EF Inhertance Strategy and add each one as Independent Types. but can't and EF Use either one of Inhertance strategy.
How can I do that?
create a IMaliOp Interface and let MaliOp implement it.
let the two classes implement the IMaliOp interface as well
Then use automapper (or something similar) to automatically transfer the information from the Entity object to your regular object.
This two classes would represent DTO (data transfer object). there are many strategies, out there for DTO-s
You should avoid having Classes inherit Entities, otherwise you applications classes will get too tightly coupled, and changes might prove them self to become too painful
In your context, override OnModelCreating;
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
modelBuilder.Entity<PrmEBD>().ToTable("PrmEBDs");
modelBuilder.Entity<LoanEBD>().ToTable("LoanEBD");
modelBuilder.Entity<MaliOp>().ToTable("MaliOp");
}
This will create three separate tables in your database.
You will need to make sure that when you do a query over MaliOp, that you don't pull in instances from all three tables. You may want to go wit the approach of creating a common interface for all three, so that they don't actually inherit from each other, too
Goal. I have a "Gift" entity that describes what someone has to offer (babysitting, dog walking, etc) with a rating. And I want a "GiftCategory" entity that gives general category descriptive information (pets, sports, automotive, etc) for someone to search apon and then get all gift that have those categories. A "Gift" entity can have multiple "GiftCategory" entities associated with it. I want the ability to search for a category and pull out all "Gift" entities that have been created with those categories associated with them. Here is what I have so far but it doesn't seem to work with the entity first approach. Maybe I need another table that connects the two entities because currently the way the two tables are connected doesn't seem correct?
Gift entity:
public class Gift
{
public int Id { get; set; }
public string Name { get; set; }
public ICollection<GiftCategory> Categories { get; set; } // is this incorrect???
public int Rating { get; set; }
}
Category entity:
public class GiftCategory
{
public int Id { get; set; }
public string Name { get; set; }
public string Description { get; set; }
}
The "GiftCategory" table that gets created creates a gift_id column that links the "GiftCategory" back to a gift (not what I want)!!!!
It seems like I would need to create a entity that connects the two entities? Something like:
public class ConnectGifts
{
public int Id { get; set; }
public string GiftId{ get; set; }
public string GiftCategoryID{ get; set; }
}
This way I can have multiple categories for a Gift, but the thing I don't understand is with entity first I really don't need this entity I just need what would be this table to get/query the "GiftCategory" entities for ids then get the gift ids to get all the gifts. So it seems like creating this entity is overkill? Is there a way to do it without creating a third table/entity ("ConnectGifts") with code first? Or am I not understanding that all entities are tables and all tables are entities? I'm also using linq-to-sql for all querying.
You're looking for a many-to-many relationship and can be defined as:
public class Gift
{
public int Id { get; set; }
public string Name { get; set; }
public ICollection<GiftCategory> Categories { get; set; } // is this incorrect???
public int Rating { get; set; }
}
public class GiftCategory
{
public int Id { get; set; }
public string Name { get; set; }
public ICollection<Gift> Gifts { get; set; }
}
So each has a collection of the other. Gift has many Categories and Category had many Gifts. You could use a bridge table like you've done with ConnectGifts but it's not necessary with EF. Using just Gift and GiftCategory, EF will actually create the bridge table for you.
How do I store additional fields in the "link table" that is automagically created for me if I have two entities associated as having a many to many relationship?
I have tried going the "two 1 to many associations"-route, but I'm having a hard time with correctly configuring the cascading deletion.
Unless those extra columns are used by some functions or procedures at the database level, the extra columns in the link table will be useless since they are completely invisible at the Entity Framework level.
It sounds like you need to re-think your object model. If you absolutely need those columns, you can always add them later manually.
You will most likely need to expose the association in your domain model.
As an example, I needed to store an index (display order) against items in an many-to-many relationship (Project <> Images).
Here's the association class:
public class ProjectImage : Entity
{
public Guid ProjectId { get; set; }
public Guid ImageId { get; set; }
public virtual int DisplayIndex { get; set; }
public virtual Project Project { get; set; }
public virtual Image Image { get; set; }
}
Here's the mapping:
public class ProjectImageMap : EntityTypeConfiguration<ProjectImage>
{
public ProjectImageMap()
{
ToTable("ProjectImages");
HasKey(pi => pi.Id);
HasRequired(pi => pi.Project);
HasRequired(pi => pi.Image);
}
}
From Project Map:
HasMany(p => p.ProjectImages).WithRequired(pi => pi.Project);
Maps to the following property on project:
public virtual IList<ProjectImage> ProjectImages { get; set; }
Hope that helps
Ben
Suppose there is a many-to-many association between two types: User and Message, and the association class is defined as UserMessageLink with additional properties.
public class User {
public int Id {get;set;}
}
public class Message {
public int Id {get;set;}
}
//The many-to-many association class with additional properties
public class UserMessageLink {
[Key]
[Column("RecieverId", Order = 0)]
[ForeignKey("Reciever")]
public virtual int RecieverId { get; set; }
[Key]
[Column("MessageId", Order = 1)]
[ForeignKey("Message")]
public virtual int MessageId { get; set; }
public virtual User Reciever { get; set; }
public virtual Message Message { get; set; }
//This is an additional property
public bool IsRead { get; set; }
}