I have a code first project with some Base classes.
I then have another project that inherits from those classes, adding some more properties, and then generating the DB.
Lets say for example we have a Customer and an Address:
public class Customer
{
[Key]
public int ID { get; set; }
[Required]
[MaxLength(100)]
public string FirstName { get; set; }
}
public class Address
{
[Key]
public int ID { get; set; }
[Required]
[MaxLength(100)]
public string CityName { get; set; }
[Required]
public Customer Customer { get; set; }
}
You'll notice that the Address has a foreign key to Customer.
This all works fine, but what I want to do is Inherit from Customer (or use a partial class) to add another property.
I stall want the database to have an Address and Customer table, but the implementing project should be able to add:
public class Customer : BaseObjects.Customer
{
[Required]
public int PantSize { get; set; }
}
The problem is, that the Base Address returns a Customer in the BaseObjects namespace, not this extended customer class type. So when I say
public DbSet<Customer> Customers { get; set; }
It either complains of having 2 classes with the same simple name (customer), or if I make them partial classes and generate the DBSet in the base DBContext it doesnt include the new property.
How can I do this?
thanks,
James
Related
Full error:
One or more validation errors were detected during model generation:
EFEmployee_Identity_Source: : Multiplicity is not valid in Role 'EFEmployee_Identity_Source' in relationship 'EFEmployee_Identity'. Because the Dependent Role properties are not the key properties, the upper bound of the multiplicity of the Dependent Role must be '*'.
I am dealing with three types of entities: EFEmployee, EFPerson, and EFOffice. It's kind of weird that I'm getting this error because the code I'm testing only creates an instance of an EFOffice entity. Anyway, here is the EFEmployee entity class:
[Table("employee_entity")]
public class EFEmployee : EFBusinessEntity
{
[ForeignKey("Office")]
public Guid OfficeID { get; set; }
[ForeignKey("Identity")]
public Guid PersonID { get; set; }
[Column("hire_date")]
public DateTime HireDate { get; set; }
[Column("job_title")]
public byte[] JobTitle { get; set; }
[Column("salary")]
public int Salary { get; set; }
[Column("certifications")]
public byte[] Certifications { get; set; }
[Column("vacation_time")]
public int VacationTime { get; set; }
[Column("sick_time")]
public int SickTime { get; set; }
public virtual EFOffice Office { get; set; }
public EFPerson Identity { get; set; }
public virtual EFEmployee ReportingTo { get; set; }
}
And this is my EFPerson entity class:
[Table("person_entity")]
public class EFPerson : EFBusinessEntity
{
[Column("first_name")]
[StringLength(50)]
public string FirstName { get; set; }
[Column("last_name")]
[StringLength(50)]
public string LastName { get; set; }
[Column("phone_num")]
public uint? PhoneNum { get; set; }
[Column("date_of_birth")]
public DateTime DateOfBirth { get; set; }
public EFEmployee Employee { get; set; }
}
You can see that they both inherit from EFBusinessEntity, which is here:
[Table("business_entity")]
public abstract class EFBusinessEntity : IBusinessEntity
{
[Column("tenant_id")]
public Guid TenantId
{
get;
set;
}
[Column("id")]
[Key]
public Guid Id
{
get;
set;
}
}
As you can see, there is a one-to-zero-or-one relationship between EFEmployee and EFPerson, with EFEmployee being the dependent side since there can be a person who is not an employee, but there can't be an employee who is not a person too. Since EFEmployee is the dependent side, I have added a PersonID in EFEmployee with the data annotation (attribute?) above denoting that it's the foreign key to Person:
[ForeignKey("Identity")]
public Guid PersonID { get; set; }
I think I've made it pretty clear for Entity Framework that this is a 1:0..1 relationship. Does anyone know how to solve this error using data annotations (or attributes, whatever those square bracket things above properties are). I can't use fluent API for reasons I'm not getting into.
Generally, with 1:0..1 relationships in Entity Framework, the dependent side needs to use its primary key as the foreign key. Fortunately, for your case, this doesn't seem like it would be a bad idea. You would need to:
Remove the EFEmployee.PersonID property
Add [ForeignKey("Id")] to EFEmployee.Identity
Edit: May not work because key and navigation property are on separate classes. See this.
Having EFEmployee inherit from EFPerson seems like it might be a viable option as well. Inheritance uses TPH by default, but if you want to use TPT (table-per-type), add the [Table] attribute to your type.
I did some more playing around with the models and found out what was wrong. So I kept the foreign key attribute with EFPerson.Identity like jjj suggested:
[ForeignKey("PersonID")]
public virtual EFPerson Identity { get; set; }
Then the other change I had to make was in the EFPerson class. In my EFPerson class I had the navigation property to EFEmployee:
public virtual EFEmployee Employee { get; set; }
However, since this is a 1:0..1 relationship with EFEmployee being the dependent side (i.e. the non-essential side), I removed that navigation property, and when I ran my test it worked.
I want the following table structure:
Person
-person_id
Company
-company_id
Company_Person
-person_id
-company_id
-other_column
Location
-id
Currently my EF is resulting in a 'company_id' column in the Person table also as a FK.
My models look like:
public class Person
{
[Key]
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public int PersonId { get; set; }
public int LocationId {get;set;}
public virtual Location Location {get; set; }
public virtual ICollection<CompanyPerson> CompanyPersons {get; set;}
}
public class Company
{
[Key]
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public int CompanyId { get; set; }
public virtual ICollection<CompanyPerson> CompanyPersons {get; set;}
}
[Table("Company_Person")]
public class CompanyPerson
{
[Key, Column(Order = 0)]
public int PersonId { get; set; }
[Key, Column(Order = 1)]
public int CompanyId { get; set; }
public bool IsActive { get; set; }
public virtual Person Person { get; set; }
public virtual Company Company { get; set; }
}
public class Location
{
[Key]
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public int id {get;set;}
public virtual ICollection<Person> Persons {get;set;}
}
I followed the same pattern as in here: Create code first, many to many, with additional fields in association table
How can I get that extra CompanyId column from being generated in the Person table?
Update
Ok I figured it out, and it turns out it was another association that I didn't post (my bad once again).
In my Company model I had this which I commented out and it generated the correct table. I still want this association so can someone tell me what is why this is happening?
public class Company
{
[Key]
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public int CompanyId { get; set; }
public virtual ICollection<CompanyPerson> CompanyPersons {get; set;}
public virtual ICollection<History> Histories {get; set; }
}
public class History
{
[Key]
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public int Id { get; set; }
[ForeignKey("Company")]
public int CompanyId { get; set; }
public virtual Company Company { get; set; }
}
So when I commented out the everything in the History model except for the Id property, and the Company.History property it generated the table structure I was expecting.
I think that EF is treating your CompanyPerson property as a complex type, because essentially, it can't actually create a M2M relationship with what you've given it. Whether that's actually the problem or not, either way you'll need to fix your CompanyPerson properties to be:
public virtual ICollection<CompanyPerson> CompanyPersons { get; set; }
UPDATE
The oddest part is that your History class would perfectly explain the issue if it only actually was defined as:
public class History : Person
That's why I asked you about any subclasses of Person because EF's default behavior with inheritance is to use TPH (table per hierarchy). In other words, it will simply add all properties of all subclasses to the base class' table, instead of creating a table for each subclass. Plainly and simply, the only source of this column you aren't expecting is going to be one of either:
Company, or some subclass of Company has direct relationship to Person (not through CompanyPerson) and it's configured to be a one-to-one.
Person or some subclass of Person has a relationship to Company.
Ok I found the problem, and surprise surprise the real bug was with me!
In my Company table had this:
public virtual ICollection<Person> Histories { get; set; }
if you didn't catch that, the type should be History and not Person!
public virtual ICollection<History> Histories { get; set; }
Thanks for all that helped with this!
I've been lurking around for quite some time, so here's the first question ;)
I've been playing with Entity Framework 5.0 Code First and I want to do following:
I have two entities and I want every entity to have relation to Address entity in the following way:
I have one Address entity which stores address values, it doesn't have relation to entities for which it has values, instead
There's another entity AddressBook which has reference to Address entity and the coresponding entities (Person, Company, some others in the future)
Here's code:
public partial class Address : BaseEntity
{
[Key]
public int ID { get; set; }
public string Street { get; set; }
public string CityName { get; set; }
public int? PostalCode { get; set; }
public virtual ICollection<Person> Persons { get; set; }
public virtual ICollection<Company> Companies{ get; set; }
}
public partial class Person : BaseEntity
{
[Key]
public int ID { get; set; }
public virtual ICollection<Address> Addresses { get; set; }
}
public partial class Company: BaseEntity
{
[Key]
public int ID { get; set; }
public virtual ICollection<Address> Addresses { get; set; }
}
What will this do is create database schema with tables:
Address
AddressPerson (with composite primary key)
Address_ID
Person_ID
AddressCompany
Address_ID
Company_ID
People
Companies
Here's what I want to do:
Address
AddressBook
Address_ID (PK)
Person_ID (FK)
Company_ID (FK)
People
Companies
What I want to do is have table like AddressBook:
public partial class AddressBook
{
[Key]
public int ID { get; set; }
public virtual Address Address { get; set; }
public virtual Person Person { get; set; }
public virtual Company Company { get; set; }
}
I'm not sure how to define navigational properties in Person and Company class.
They should have ICollection<Address> Addresses navigational property, because I want them to work just with collection of addresses without knowing about underlying AddressBook.
Is it possible to do this with DbModelBuilder or should I write code inside getter and setter of ICollection<Address> Addresses property and get addresses from AddressBook?
Thanks!
You cannot create a mapping in a way that Entity Framework would understand the Addresses collections in Person and Company as true navigation properties (which support eager and lazy loading and so on). For this you need indeed AddressBooks collections. You can add the Addresses as not mapped and readonly helper properties then:
public partial class Person : BaseEntity
{
[Key]
public int ID { get; set; }
public virtual ICollection<AddressBook> AddressBookEntries { get; set; }
public IEnumerable<Address> Addresses
{
get { return AddressBookEntries.Select(ab => ab.Address); }
}
}
(The same with Company.)
An alternative and in my opinion better approach would be to create a common base class for Person and Company, move the Addresses collection into that base class and have a single many-to-many relationship and single join table between this base class and Address:
public abstract class EntityWithAddresses : BaseEntity
{
[Key]
public int ID { get; set; }
public virtual ICollection<Address> Addresses { get; set; }
}
public partial class Person : EntityWithAddresses
{
}
public partial class Company : EntityWithAddresses
{
}
Address has a navigation collection to the new base class:
public partial class Address : BaseEntity
{
[Key]
public int ID { get; set; }
public string Street { get; set; }
public string CityName { get; set; }
public int? PostalCode { get; set; }
public virtual ICollection<EntityWithAddresses> EntityWithAddresses { get; set; }
}
I'm trying to map a couple of legacy tables using Entity Framework. The classes look like this...
public class Customer
{
[Key, Required]
public string Code { get; set; }
public string Domain { get; set; }
public virtual Address BillToAddress { get; set; }
public virtual ICollection<Address> ShipToAddresses { get; set; }
}
public class Address
{
[Column(Order = 0), Key, Required]
public string Code { get; set; }
[Column(Order = 1), Key, Required]
public string Domain { get; set; }
public string Type { get; set; }
public string CustomerReferenceCode { get; set; }
}
Each Customer has one "BillToAddress" that corresponds with an Address whose CustomerReferenceCode contains the Customer's Code and where the Type field contains the text "Customer"
Each Customer has zero or more "ShipToAddresses" that correspond to Addresses whose CustomerReferenceCode contains the Customer's Code and whose where the Type fields contain the text "Ship-To"
I'm able to reference the BillToAddress by adding
[Key, Required]
[ForeignKey("BillToAddress"), Column(Order = 1)]
public string Code { get; set; }
[ForeignKey("BillToAddress"), Column(Order = 2)]
public string Domain { get; set; }
But I've not been able to figure out how to reference the collection of ShipToAddresses for the customer.
See this example (note it is separate classes): Fluent NHibernate automap inheritance with subclass relationship
One easy approach might be:
public class Customer
{
[Key, Required]
public string Code { get; set; }
public string Domain { get; set; }
public virtual ICollection<Address> Addresses{ get; set; }
public virtual Address BillToAddress { get { Addresses.Where(n=>n.Type = Address.BillingAddress)).Single(); }
public virtual ICollection<Address> ShipToAddresses { get { Addresses.Where(n=>n.Type = Address.ShipToAddress)); }
}
One additional comment - this is not implicitly forcing your one Billing address business logic as the example you start with does, so you either need to enforce that elsewhere with the above approach. Also, creating two classes and using TPH as described in the example I link to is the approach I would likely take anyway - which would meet your described goal above directly. Also, in between the two, this may work, using the discriminator in the getter of each property directly.
I'm working with sqlite and the c# sqlite-net library.
Some of my entities:
public class Product
{
[PrimaryKey, AutoIncrement]
public int Id { get; set; }
[MaxLength(50)]
public string Name { get; set; }
[MaxLength(50)]
public string Brand { get; set; }
[MaxLength(50)]
public string Type { get; set; }
[MaxLength(50)]
}
public class ProductImage
{
public int Id { get; set; }
public string Name { get; set; }
public string Container { get; set; }
public int ProductId { get; set; }
}
Now all entities belong to a user and the user should always work only with his own entities. So when I insert an entity in the database I want to store the userId with it.
However... I don't want to add the userId to all my domain classes.
I'm storing my entities like this right now:
await _databaseManager.GetDatabaseInstance().InsertAsync(entity);
and selecting like this:
var products = await _databaseManager.GetDatabaseInstance().Table().ToListAsync();
So is there a way with sqlite-net to add the userId to the database without adding it to all domain classes?
Doesn't it support inheritance, as in looking at the type's hierarchy? I would suggest it would, or at least should. So, if it does you could use an abstract base class or an interface. Something like this:
public abstract class StandardEntity {
public int UserId { get; set; } // decorate with attributes as necessary
}
And inherit:
public class Product : StandardEntity {
}