I have following tables: Animal and ConcreteAnimal. I am going database first approach if it makes any difference in this example. Suppose they are separate entities. Then I have 2 DBSet(s) generated, and can update separately either of the table. Things change once I set ConcreteAnimal as child of Animal in entity designer. Entity Set field of ConcreteAnimal in entity designer becomes disabled and is automatically populated with the value of entity set of its parent class Animal. DBSet for ConcreteAnimal is not longer generated in the context. This means that I can no longer access the second, child, table. How am I supposed to add ConcreteAnimals to the database? Is it expected behavior? If yes, what is the logic behind it?
EDIT:
OK, I tried following code and it works.
using(var context = new MyEntities())
{
var concreteAnimal = new ConcreteAnimal
{
//setting properties here
//both for Animal and specific to ConcreteAnimal
};
context.Animals.Add(concreteAnimal);
context.SaveChanges();
}
Magic happened, and both tables were populated with correct data. This is good. However, it just does not look logical to me why we have DBSet for base class only?
You are currently using TPT Inheritance.
Common information are saved in the Animal table and specific information from a type in their own table.
You will find information you are looking in these articles:
Table per Hierarchy (TPH)
Table per Type (TPT)
Table per Concrete class (TPC)
Edit
Why we have DBSet for base class only
It's actually a good question, I cannot give an exact answer but from my opinion this is not implemented by default because it's not necessary and would be very confusing.
Just imagine some very complex TPT scenario with a lot of inheritance, they would have one DbSet for every concrete class (which could be easily hundred of additional DbSet), then now you will have to ask to yourself, from which DbSet to do you have to retrieve, add or remove this concrete type.
You can easily add the code yourself once it's generated (in a partial class) and it's will work.
public DbSet<ConcreteAnimal> ConcreteAnimals { get; set; }
Related
When using EF Code First to generate a database, you declare the DB sets within the DbContext class for example..
public DbSet< ProductOption > ProductOptions { get; set; }
Now from the reading I have been doing, if you have a related entity in the type ProductOptions you do not need to declare it as EF will look at the dependents and use them to create the required DB tables.
My question is this: Is this recommend practice? And if so how do you then access the related type in code as it does not exist as a data set.
Also this auto-discover feature of EF, does it do it in both directions, i.e if you declare a db set that does not have any related entities inside it, but it is an entity that is in some other entity, will EF find it?
Any clarification is welcome. Thanks
EDIT
Example of what I am saying in terms of not being able to access the Types that are auto discovered is when your seeding data.
You actually do have access to the types without declaring DbSets inside your context class. Inside your context initialization class under the Seed method you can access any of your entities using the yourContext.Set().Add(theObject). This way you do not need to have DBSet properties inside your context class. I am not sure what version of EF you are using or if your are using an initialization class for seeding the data. Regardless, you can still use the .Set().Add, to seed the data.
With regards to the question will EF find the non related entities. Like you said, as long as an object is a property of an entity EF should create a table for it. As long as in code first you declare a mapping for any entity, it should create it and any child entities that may or may not have mappings for themselves. I'd have to see how you are declaring your context and mappings to be sure.
I use Entity Framework in my C# projects for a database connection.
Let's say I have a table on my database which has 3 columns:
table: Car
columns: id, color, type, numberOfDoors
Here is how I create a new record for this table:
Car c = new Car();//default constructor
c.color = "blue";
c.type = "Opel";
c.numberOfDoors = 2;
myDatabase.tblCar.AddObject(c);
myDatabase.SaveChanges();
I'm doing this in a lot of places in my project, so the first 4 lines are repeated a lot. So I was thinking, why doesn't Entity Framework have a constructors with parameters? I thought that maybe the columns which can be null in db can be the reason for this.
Entity Framework is trying to be as general as possible. Plus some models might have 10+ properties off of them. That would be quite messy if you only needed to assign a small subset of them. Another reason is that it would be messy to construct the models inside of Entity Framework.
The classes are generated with the partial keyword and you should be able to add it yourself.
EF generates static constructor CreateX methods for each Entity (class) it generates. For example, on one of my User classes I can do the following:
User user = User.CreateUser(userID, password, email, userTypeId);
This does most of what you would want in terms of quickly setting up a new instance of an entity class. Of course this does NOT add the new instance to the collection or save changes; you might want to create 50,000 instances and saving every time would be wasteful.
I've been at this for a while, and I really cannot figure it out. I'm using EF5, Mvc 4, table-per-type inheritance.
I have classes Tenant and Landlord that inherit from UserProfile. When registering a user selects what account type they want, and based on this selection the details they enter on the form (username, first name, last name, email, account type, password) should be added to UserProfile table. I then want the PK of that table to be added as a foreign key to table that matches the account type they selected, either Tenant or Landlord.
I was trying to achieve the above using this method, but that resulted in the problems described in that question.
I've now created a RegisterTenantModel class, with properties that map to a Tenant, and I'm passing an object of type RegisterTenantModel to the AccountController/Register like so...
// ...
if (tenantModel.AccountType == AccountType.Tenant)
{
using (var db = new LetLordContext())
{
var t = db.UserProfile.Create<Tenant>();
WebSecurity.CreateUserAndAccount(tenantModel.UserName, tenantModel.Password,
t = new Tenant
{
AccountType = tenantModel.AccountType,
DateWantToRentFrom = DateTime.Now,
DateWantToRentTo = DateTime.Now,
DesiredPropertyLocation = "",
Email = tenantModel.Email,
FirstName = tenantModel.FirstName,
UserId = WebSecurity.CurrentUserId,
UserName = WebSecurity.CurrentUserName
// More properties that map to Tenant entity
// ...
},
false);
db.SaveChanges();
...but now I'm getting an error Invalid column name for each of the column names inside t= new Tenant.
Has anyone ever had to do something similar? Am I approaching this the right way? Help would be much appreciated.
EDIT
Based on what Antonio Simunovic posted below, I think I've come to realise what the problem is. My WebSecurity.InitializeDatabaseConnection() goes like this...
WebSecurity.InitializeDatabaseConnection("LetLordContext", "UserProfile",
"UserId", "UserName", autoCreateTables: true);
...so when I call Websecurity.CreatUserAndAccount() in AccountController/Register it's writing to the UserProfile table as per the parameters in WebSecurity.InitializeDatabaseConnection(). Looking at the question linked above, you'll see that, based on the account type selected on the form, either a Tenant or Landlord will be added to the UserProfile table by calling...
var tenant = db.UserProfile.Create<Tenant>();
//...
db.UserProfile.Add(tenant);
db.SaveChanges();
...resulting in duplicate entries being added to the UserProfile table.
I think the solution is to create a new table, and point WebSecurity.InitializeDatabaseConnection() at it.
What does your WebSecurity.InitializeDatabaseConnection() method call looks like? That call identifies database table to store user data.
WebSecurity.CreateUserAndAccount() method does not use context to store supplied data, it simply maps object property names of third call argument to columns in table defined in InitializeDatabaseFile() method.
If you're not familiar with SimpleMembership mechanics, take a look at this article:
http://weblogs.asp.net/jgalloway/archive/2012/08/29/simplemembership-membership-providers-universal-providers-and-the-new-asp-net-4-5-web-forms-and-asp-net-mvc-4-templates.aspx
I saw exactly the same symptoms, and it was because I hadn't declared the base class as a DbSet<BaseClassName> on the DbContext.
(Counterintuitive, and I never need to refer to the collection of base class entities in my application, but there you go.)
I have seen this error when I forget to annotate the subclass with the table name in TPT.
[Table("Tenant")]
public class Tenant : UserProfile { ...
Could it be as simple as this?
EDIT
A quick search also recommends pairing down your fields in the Entity and adding them back in one at a time to see if a single field is to blame (and it will provide an error message that indicates many failed columns). This sounds a little suspect to me, but might be worth a try:
Entity Framework 5 Invalid Column Name error
Solution
Ensure that all derived classes are explicitly mapped to tables. Two simply ways to do this are:
TableAttribute class attribute: [Table("Tenant")]
ToTable Fluent API method: ToTable("Tenant")
Ensure that the base type is abstract.
Ensure that you are not calling the MapInheritedProperties configuration method for any tables. That method implies TPC.
Ensure that the base type is registered as part of the model. Two simple ways to do this are:
Add an accessor property to your subclass of DbContext:
public DbSet<UserProfile> Users { get; set; }
Override OnModelCreating in your subclass of DbContext and call DbModelBuilder.Entity:
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
base.OnModelCreating(modelBuilder);
modelBuilder.Entity<UserProfile>();
}
Explanation
As the error message indicates, Entity Framework appears to be looking for the "base" table's columns in the "derived" table. This indicates that it's trying to use "Table per Concrete Type" (TPC) inheritance rather than "Table per Type" (TPT).
Making Entity Framework use TPT is quite fiddly. If you very carefully study the example implementation at Inheritance with EF Code First: Part 2 – Table per Type (TPT), you might spot what you're missing. The follow-up article, Inheritance with EF Code First: Part 3 – Table per Concrete Type (TPC), summarises the required configuration as follows:
As you have seen so far, we used its Requires method to customize TPH. We also used its ToTable method to create a TPT and now we are using its MapInheritedProperties along with ToTable method to create our TPC mapping.
My experimentation has shown that the above summary is oversimplified. It appears that a single minor seemingly-unrelated configuration difference can cause an unexpected inheritance strategy to be used.
Scenario: Trying to extract and rearange information from one database to an other.
DB A has some data I want to get. I want to store it on DB B in a slightly different structure.
DB A I get using an EDMX database generated model so it uses a derivative of ObjectContext. DB B I would like to be Code generated. So I use the code/model first approach by installing EntityFramework 4.1 via Package manager. So DB B uses a DbContext derivative
When I try to store information from DB A to DB B it's says:
Test method RoutIT.Irma.Import.Service.Test.ImportIrma2ProductTests.ImportProducts threw exception: System.ArgumentException: Could not find the conceptual model type for 'Some entity in DB A's EDMX model'
It actually does it when adding a DB B entity to the DB B's Derived DbContext's DbSet property. So the code is like
using (TransactionScope scope = new TransactionScope(TransactionScopeOption.Required))
{
foreach (FirstPVC pvc in pvcs)
{
this._irmaImport.FirstPVCs.Add(pvc); <--
this._irmaImport.SaveChanges();
}
scope.Complete();
}
}
It happens at the point in the code marked by the arrow above ("<--")
FirstPVC is a DB B property yet at the point of the arrow it complanes about not having a conceptual model for an entity belonging to DB B's context.
This is strange since I try to store an DB B entity to the DB B context. Why should it care about entity's of DB A.
All contexts are contained in the same project. But DB B's Derived DbContext only has knowledge about it's own DbSet<> properties, suddenly when trying to add something to the DbSet<> property it give me the error in bold above.
Anybody know why this happens? Why should DbContext care about entity's of another context specifically one of a ObjectContext derived class.
Perhapse it's usefull to note the entity it's complaining about looks a bit like this
[EdmEntityTypeAttribute(NamespaceName="Irma2Model", Name="AccessProvider")]
[Serializable()]
[DataContractAttribute(IsReference=true)]
public partial class AccessProvider : EntityObject
{
/*****...... ******/
}
Found an answer, its not what you want to hear though:
http://social.msdn.microsoft.com/Forums/en-US/adodotnetentityframework/thread/d2a07542-cb33-48ba-86ed-4fdc70fb3f1a
"If you are using the default code generation for the EDMX file then the generated classes contain a series of attributes to help EF find which class to use for each entity type. EF currently has a restriction that POCO classes can't be loaded from an assembly that contains classes with the EF attributes. (Short answer is no, your classes need to be in separate projects).
This is a somewhat artificial restriction and something that we realize is painful and will try and remove in the future."
So the workaround would be to split the classes into two different assemblies.
I have a very extensible and modular application. I want to extend mapped Entites from other assemblies. Yet, I still need to operate on the base classes.
Example:
Parent class mapping in Assembly A:
public class PersonMap : ClassMap<Person>
{
public PersonMap()
{
Table("Persons");
Id(x => x.Id).GeneratedBy.Assigned();
}
}
Child class mapping in Assembly B:
public class EmployeeMap : SubclassMap<Employee>
{
public EmployeeMap()
{
Table("Persons");
Extends(typeof(Person));
KeyColumn("Id");
LazyLoad();
HasMany<Assignments>(x => x.Assignments).KeyColumn("Id").Inverse().Cascade.AllDeleteOrphan().LazyLoad().NotFound.Ignore();
}
}
Now whenever I create a Person in some code of Assembly A it is saved as Employee by NHibernate. And that leads to a class cast exception due to proxying whenever I save a Person and try to refresh it in Assembly A. Assembly A must not have a dependency on Assembly B.
I need to operate on the parent class in all methods of assembly A. The child class is only used in other assemblies.
How can I map something like that? How can I tell NHibernate to just save it as the parent class? I use SaveOrUpdate to persist the entities; How can I correctly extend entities and yet save them to the same table without discriminator? Can't NHibernate differentiate by object type? Is there a workaround?
I do not want to specify manual proxies as I'd have to create a proxy for every entity! I cannot use the visitor pattern due to dependency problems.
I need a way to extend a mapped entity in a different assembly without such problems! The database is legacy, I cannot change it. How would you work around the issue?
Your goal of mapping a hierarchy to a single table, without a discriminator, presents a conundrum. There must be SOMETHING in the data layer that gives the DAL a clue that a particular record is an Employee as opposed to just a Person. Since you aren't mapping any additional fields on the Persons table for an Employee, and not providing a discriminator, there's nothing about any single record of the Persons table that differentiates Persons from more-derived Employees.
Could you provide the code you're using to retrieve the Person records? In my experience, NHibernate queries virtually always require a domain type to hydrate. It may be that in this case, NHibernate is trying to create objects of the most derived type it knows, and because it can't tell the difference between a base Person and the more derived Employee, all Persons are Employees. You may try Linq, which forces strongly-typed object hydration, as opposed to HQL or other less strongly-referenced query.
Solved it by using a HasOne Mapping on the same Table and not using a subclass. This does not produce ideal code but is reather free of problems.