I have two POCO objects:
public class Product
{
public int ProductID { get; set; }
public string Name { get; set; }
public int CategoryID { get; set; }
public virtual Category Category { get; set; }
}
public class Category
{
public int CategoryID { get; set; }
public string Name { get; set; }
}
So obviously, when I create a Product, I can select a category for that product. Or rather, a category is required.
I can't create a product without explicitly selecting a category, and my data is structured in such a way, that I don't want to create a "No Category" category entry.
I've thought about doing a many-to-many mapping between these two tables... but would like to avoid it if possible.
Either I'm doing something silly, or there really is no way to do this.
Any help would be appreciated!
Make the CategoryID nullable. If you do not supply value for CategoryID it will be set to NULL in database.
public class Product
{
public int ProductID { get; set; }
public string Name { get; set; }
public int? CategoryID { get; set; }
public virtual Category Category { get; set; }
}
Related
Im having two models:
public class Customer
{
public int Id { get; set; }
public int Number { get; set; }
public int ParentNumber { get; set; }
public string Name { get; set; }
public string Address { get; set; }
public string City { get; set; }
public string Country { get; set; }
public string Language { get; set; }
}
and
public class Batch
{
public int Id { get; set; }
public int Number { get; set; }
public string FileName { get; set; }
public string ArticleNumber { get; set; }
public string ArticleDescription { get; set; }
public int Weight { get; set; }
public DateTime ProductionDate { get; set; }
public DateTime DeliveryDate { get; set; }
public DateTime BestBeforeDate { get; set; }
public DateTime? ApprovedDateTime { get; set; }
public int CustomerId { get; set; }
public virtual Customer Customer { get; set; }
}
One batch can have a customer attached to it. But since we're importing the data from another system we decided not to take over their id's.
Right now the foreign key says try to find a customer by the property Customer.Id
I'm trying to achieve to get the foreign key point to Customer.Number from Batch.Customer(Id)
How would i succeed in this?
I've tried by defining the Customer.Number to be a Key with the Key attribute.. but this made the primary key go from Id to Number which is not what i wanted...
What are you asking was impossible in EF prior to EF Core. Fortunately in EF Core it can be done by using Alternate Keys feature. But please note that in order to be able to use it, your Cusomer.Number field should be unique.
The solution requires Fluent API configuration.
Start by defining Customer.Number as alternate key:
modelBuilder.Entity<Customer>()
.HasAlternateKey(e => e.Number);
Then set up the relationship as follows:
modelBuilder.Entity<Batch>()
.HasOne(e => e.Customer)
.WithMany()
.HasForeignKey(e => e.CustomerId)
.HasPrincipalKey(e => e.Number);
The last two lines will do what you are seeking for.
As a side note, it would be better to name the property (and column) CustomerNumber in order to avoid confusion of what the value is in it.
I am writing a PhoneGap/Web/JS mobile application that uses the WebAPI and Entity Framework in the backend.
I have a class called Thing which references the User table 4 times (ChangedByUserId, CreatedByUserId etc. The User table is really large (30 user-related fields)
I want to pass as little data over each call as possible, but I need the User's Name for each of these UserID foreign keys. (this is the only information from the user record I need).
When I use the object graph in EF it returns the FULL user record for each foreign key, so a single Thing object becomes massively bloated. I don't want to make multiple calls to get the Thing POCO object and then the User's name by UserID.
What I really want to do is a sort of flattened DTO object which is just the Thing class below but with a string for each user name, e.g. CreatedByUserName, ChangedByUserName etc. Then I would return this DTO as my hydrated POCO object and the data would be small.
So my question is: How do I do this using Entity Framework? (limit related record's return data?)
public partial class Thing
{
public int ThingId { get; set; }
public int FromUserId { get; set; }
public int ToUserId { get; set; }
public string ThingText { get; set; }
public int StatusId { get; set; }
public int ChangedByUserId { get; set; }
public int CreatedByUserId { get; set; }
public virtual User FromUser { get; set; }
public virtual User ToUser { get; set; }
public virtual User CreatedByUser { get; set; }
public virtual User ChangedByUser { get; set; }
}
As you said, you need to flatten Thing
public class FlatThing
{
public int ThingId { get; set; }
public int FromUserId { get; set; }
public int ToUserId { get; set; }
public string ThingText { get; set; }
public int StatusId { get; set; }
public int ChangedByUserId { get; set; }
public int CreatedByUserId { get; set; }
public string FromUserName { get; set; }
public string ToUserName{ get; set; }
}
// assume you have your things
var flatThings = new List<FlatThings>;
foreach (Thing t in things)
flatThings.Add(new FlatThing{ ThingId = t.ThingId, FromUserId = t.FromUserId,
FromUserName = t.FromUser.Name .....});
return flatThings;
When defining a relationship between two types is it important to include a navigation property on both types, such as in the following example:
public class Product
{
public int ProductId { get; set; }
public string Name { get; set; }
public Category Category { get; set; }
}
public class Category
{
public int CategoryId { get; set; }
public string Name { get; set; }
public ICollection<Product> Products { get; set; }
}
Can I do without including the navigation property in Category ?
If you just want it infered by code first convention then yes you need both on either side. I'd also make the collection "virtual" to support lazy loading.
You can set it up using the fluent configuration when the model is built. It would be something like this
modelBuilder.Entity<Product>()
.HasMany(x => x.Category)
I have a table in database which points to itself, i.e. parent_id >> category id. This is the ER diagram
I have modelled this table like following, but it gives *Error : 'Category': member names cannot be the same as their enclosing type :
public class Category
{
[Key]
public int category_id { get; set; }
public string category_name { get; set; }
public int category_parent { get; set; }
public string category_desc { get; set; }
public virtual Category Category { get; set; }
}
How should I model such tables ?
You have to make category_parent nullable and configure navigational property Category to the scalar property category_parent. Try to use proper naming convensions.
public class Category
{
[Key]
[Column("category_id")]
public int Id { get; set; }
[Column("category_name")]
public string Name { get; set; }
[Column("category_parent")]
public int? ParentId { get; set; }
[Column("category_desc")]
public string Description { get; set; }
[ForeignKey("ParentId")]
public virtual Category ParentCategory { get; set; }
}
I think you just need to change the Category property name to something else, so it is not the same as the class name...
public virtual Category SubCategory { get; set; }
public class Category
{
[Key]
public int category_id { get; set; }
public string category_name { get; set; }
public int category_parent { get; set; }
public string category_desc { get; set; }
public int parent_category_id { get; set; } <-- ADD & setup as foreign key
public virtual Category ParentCategory { get; set; } <-- Change name
public virtual ICollection<Category> Categories { get; set; }
}
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
base.OnModelCreating(modelBuilder);
modelBuilder.Entity<Category>.HasMany(cat => cat.Categories)
.WithRequired()
.HasForeignKey(cat => cat.parent_category_id);
}
I am struggling on how to map these two tables together, the Column TitleID on the Name table maps to the TitleID on the Title table. The table primary keys are NameTableID / TitleTableID and are unique, NameID, TitleID (on both tables) are not unique, the current record is found by a null in the DateEnd column.
public class Name
{
public int NameTableID { get; set; }
public int NameID { get; set; }
public int TitleID { get; set; } // Maps to Title.TitleID
public virtual Title Title { get; set; }
public string GivenName { get; set; }
public string FamilyName { get; set; }
public DateTime DateStart { get; set; }
public DateTime? DateEnd { get; set; }
}
public class Title
{
public int TitleTableID { get; set; }
public int TitleID { get; set; } // Maps to Name.TitleID
public string Description { get; set; }
public DateTime DateStart { get; set; }
public DateTime? DateEnd { get; set; }
}
I am assuming I need to add a bit of code to OnModelCreating in my DB Context class, but am struggling with the mapping / code, any ideas?
Thanks,
Martin
This is not relation at all. You cannot build relation on two arbitrary columns. You should read some introduction about how database relations work and what are requirements to build a relation.
In many-to-many you must choose unique key on both ends and there must be junction table which will build pairs of related keys. EF doesn't support unique keys so the only way how to build many-to-many relation is on top of primary keys: If you want many-to-many relation you must build it on Title.TitleTableID and Name.NameTableID.