Entity Framework 4 CTP5 TPT inheritance not working for deep hierarchy? - entity-framework-ctp5

I'm having a problem mapping a a slightly more complicated inheritance relationship using the code-first API in CTP5. When I have this:
Table A
--------
int ID (PK)
Table B
--------
int ID (PK)
varchar Something
public class A {
public int ID { get; set; }
}
public class B : A {
public string Something { get; set; }
}
...everything works just fine. But when I add this:
Table C
-------
int ID (PK)
varchar SomethingElse
public class C : B {
public string SomethingElse { get; set; }
}
...then it errors out with "Invalid column name Discriminator", which implies that EF is getting confused and thinks I'm trying to do a TPH mapping. Has anyone else seen this? Is this a known issues in EF? Do I need to do some special mapping?
Thanks in advance for your help.

as far as I know, hierarchies on multiple levels are not supported in code first.

Related

Many to Many relationship with 2 keys in one table

I am building an ASP.net application and I am using the Entity framework DBFirst. I have 2 tables. One table has 2 keys. The other table has 1 key. So the table structure;
Topic Table
> Id
> ArticleId
> ...
Branch Table
> Id
> ...
I have a problem creating a many to many relationship since there are 2 keys in the topic table. My many to many relation table looks like this;
BranchTopic
> TopicId
> BranchId
How do I solve this? I want a many to many relationship between the Topic and Branch. I don't want any relationship in between the ArticleId in this relationship. When I leave out the ArticleId visual Studio shows the following error;
Error 15 Error 3024: Problem in mapping fragments starting at line 503:Must specify mapping for all key properties (Id, ArticleId) of End Topic in Relationship MathematicsBranchTopic.
C:\Users\Username\documents\visual studio 2013\Projects\Mathematics\Mathematics\Model.edmx 504 11 Mathematics
You just need to specify column order to specify multiple keys
From the documentation:
public class Passport
{
[Key]
[Column(Order=1)]
public int PassportNumber { get; set; }
[Key]
[Column(Order = 2)]
public string IssuingCountry { get; set; }
public DateTime Issued { get; set; }
public DateTime Expires { get; set; }
}

How do I name a many-many table for EF6 and do I need to add special mapping for this?

I am using EF 6 and trying to map a many to many relationship. So far I have:
public partial class ObjectiveDetail
{
public ObjectiveDetail()
{
this.SubTopics = new List<SubTopic>();
}
public int ObjectiveDetailId { get; set; }
public string Text { get; set; }
public virtual ICollection<SubTopic> SubTopics { get; set; }
}
public partial class SubTopic
{
public SubTopic()
{
this.ObjectiveDetails = new List<ObjectiveDetail>();
}
public int SubTopicId { get; set; }
public int Number { get; set; }
public string Name { get; set; }
public virtual ICollection<ObjectiveDetail> ObjectiveDetails { get; set; }
}
Our DBA is going to write the code for the many to many table. Should this be as follows
with a table name of ObjectiveDetailSubTopic or something completely different ?
CREATE TABLE [dbo].[ObjectiveDetailSubTopic] (
[ObjectiveDetailId] INT NOT NULL,
[SubTopicId] INT NOT NULL
);
Can someone tell me if this is the correct way to create the table. Also do I have to
add some code to map the ObjectiveDetail and SubTopic classes to the new join class so
EF will know what to do?
Our DBA is going to write the code for the many to many table. Should
this be as follows with a table name of ObjectiveDetailSubTopic or
something completely different ?
As long as you follow the SQL Database table naming conventions, the table name can be anything. I usually name the join table like yours, by connecting the two table names.
To create the join table using sql, see below:
CREATE TABLE [dbo].[ObjectiveDetailSubTopic](
ObjectiveDetailSubTopicId int identity primary key,
ObjectiveDetailId INT NOT NULL,
SubTopicId INT NOT NULL,
foreign key(ObjectiveDetailId) references ObjectiveDetail(ObjectiveDetailId ),
foreign key(SubTopicId) references SubTopic(SubTopicId )
);
But you don't need to create the join table by your own, Entity Framework will create it for you. You just need to mapping the relationship with the Fluent API in your DbContext class like below:
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
modelBuilder.Entity<ObjectiveDetail>().
HasMany(c => c.SubTopics).
WithMany(p => p.ObjectiveDetails).
Map(m =>
{
m.MapLeftKey("ObjectiveDetailId ");
m.MapRightKey("SubTopicId ");
m.ToTable("ObjectiveDetailSubTopic");
});
}

EF is not cascade deleting

The problem is i cannot perform cascade deletion using only EF codefirst conventions. They, in particular, say: "If a foreign key on the dependent entity is not nullable, then Code First sets cascade delete on the relationship"
I have parent and child entities:
[Table("AssociationPages")]
public class AssociationPage
{
[Column("AssociationPageID"), Required, Key]
public int Id { get; set; }
[Required, ForeignKey("AssociationSetting")]
public int AssociationId { get; set; }
public virtual AssociationSetting AssociationSetting { get; set; }
}
[Table("AssociationSetting")]
public class AssociationSetting
{
[Required, Key, DatabaseGenerated(DatabaseGeneratedOption.None)]
public int AssociationId { get; set; }
public virtual ICollection<AssociationPage> Pages { get; set; }
}
My AssociationPages table in MS SQL Server looks like:
CREATE TABLE [dbo].[AssociationPages](
[AssociationPageID] [int] IDENTITY(1,1) NOT NULL,
[AssociationId] [int] NOT NULL,
...
)
and a FK (but it shouldnt matter as EF has its own conventions):
ALTER TABLE [dbo].[AssociationPages] WITH CHECK ADD CONSTRAINT [FK_ChamberPages_Chambers] FOREIGN KEY([AssociationId])
REFERENCES [dbo].[AssociationSetting] ([AssociationId])
GO
So i have non-nullable FK everywhere but once i try to delete parent AssociationSetting row then getting the "The DELETE statement conflicted with the REFERENCE constraint FK_ChamberPages_Chambers. The conflict occurred in database ..., table dbo.AssociationPages, column AssociationId message". I know i can set constraints inside database or with EF fluent API but why this is not working?
Thanks for your ideas!
update
WillCascadeOnDelete() doesnt work as well :(
It may be that Code First is not setting up the cascade since you are not following the naming conventions.
Try this:
public class AssociationPage
{
public int Id { get; set; }
public int AssociationSettingId { get; set; }
public virtual AssociationSetting AssociationSetting { get; set; }
}
public class AssociationSetting
{
public int Id { get; set; }
public virtual ICollection<AssociationPage> Pages { get; set; }
}
Okay, in investigation purposes i created a simpliest parent-child tables, put two rows - one per table, created FK relationship as "No Action" on cascade delete and wrote EF Code First entities.
Then I set up FK relationship variuos ways - via column attributes, Fluent API, explicitly specifying WillDeleteOnCascade() method or alltogether but had no luck trying to delete parent row. The only way I achieved this when retrieved both parent and child records prior to removing. At this point SQL profiler shown that rows being deleted one by one both for parent and children tables.
Summarizing the above I suppose the cascading in EF Code First means the setting constraints on the database being created by EF. I might be missing something thu.

The member with identity does not exist in the metadata collection. Parameter name: identity

We are using EF Code First 4.3.1.
We are developing an ASP.NET Web Role referring to multiple class libraries.
There are two class libraries each containing classes and an individual DBcontext.
Lets say the Library1 has classes A and B.
DBcon1: DbSet and DbSet
Lets say the Library2 has classes C and D.
Class C{
[Key]
public int CId{ get; set;}
[Required]
public virtual A referencedA {get; set;}
}
DBcon2: DbSet<C> and DbSet<D>
When I try to use the DBcon2 as such:
using (var con = new DBcon2())
{
C vr = new C();
vr.CId= 1;
vr.referencedA = DBCon1.As.First();
con.Cs.Add(vr);
con.SaveChanges();
}
I get an exception as:
"The member with identity does not exist in the metadata collection.
Parameter name: identity"
Both DBCon1 and DBcon2 are using the sane SQL Server Database "SampleDB".
Please point me in the right direction.
I got this error and fixed it by not trying to set the navigation property in the related table, just set the foreign key id instead
eg
public class Student()
{
public int StudentId { get; set; }
public string StudentName { get; set; }
public int CourseId { get; set; }
public virtual Course Course { get; set; }
}
public class Course()
{
public int CourseId { get; set; }
public string CourseName { get; set; }
public virtual ICollection<Student> Students { get; set; }
}
Main code:
var myCourse = new Course();
var myCourseId = 1;
var student = new Student() {
CourseId = myCourseId
// Course = myCourse <-- this would cause the error
}
Might not be your issue but maybe it will point you in the right direction and hopefully will help someone else.
The exception is a bit cryptic, but pretty clear if you realise that a context needs information about entities (metadata) to be able to write sql statements. Thus, your DBcon2 context has no clue where to find the primary key of an A, because it has no metadata about A.
You could however set an integer property A_Id (or the like), but then you'll have to write custom code to resolve it to an A.
Another option is to merge (parts of) the contexts, if possible.

What's Automapper for?

What’s Automapper for?
How will it help me with my domain and controller layers (asp.net mvc)?
Maybe an example will help here...
Let's say you have a nicely-normalized database schema like this:
Orders (OrderID, CustomerID, OrderDate)
Customers (CustomerID, Name)
OrderDetails (OrderDetID, OrderID, ProductID, Qty)
Products (ProductID, ProductName, UnitPrice)
And let's say you're using a nice O/R mapper that hands you back a well-organized domain model:
OrderDetail
+--ID
+--Order
|--+--Date
|--+--Customer
|-----+--ID
|-----+--Name
+--Product
|--+--ID
|--+--Name
|--+--UnitPrice
+--Qty
Now you're given a requirement to display everything that's been ordered in the last month. You want to bind this to a flat grid, so you dutifully write a flat class to bind:
public class OrderDetailDto
{
public int ID { get; set; }
public DateTime OrderDate { get; set; }
public int OrderCustomerID { get; set; }
public string OrderCustomerName { get; set; }
public int ProductID { get; set; }
public string ProductName { get; set; }
public Decimal ProductUnitPrice { get; set; }
public int Qty { get; set; }
public Decimal TotalPrice
{
get { return ProductUnitPrice * Qty; }
}
}
That was pretty painless so far, but what now? How do we turn a bunch of OrderDetails into a bunch of OrderDetailDtos for data binding?
You might put a constructor on OrderDto that takes an OrderDetail, and write a big mess of mapping code. Or you might have a static conversion class somewhere. Or, you could use AutoMapper, and write this instead:
Mapper.CreateMap<OrderDetail, OrderDetailDto>();
OrderDetailDto[] items =
Mapper.Map<OrderDetail[], OrderDetailDto[]>(orderDetails);
GridView1.DataSource = items;
There. We've just taken what would otherwise have been a disgusting mess of pointless mapping code and reduced it into three lines (really just two for the actual mapping).
Does that help explain the purpose?
If you have an object of one type and you want to populate the properties of an object of another type using properties from the first type, you have two choices:
Manually write code to do such a mapping.
Use a tool that will automatically handle this for you.
AutoMapper is an example of 2.
The most common use is to flatten models into a data transfer objects (or, in general, mapping across layer boundaries). What's very nice about AutoMapper is that for common scenarios you don't have to do any configuring (convention over configuration).
Map objects between layers. Good example: Here

Resources