I am trying to access the data in the relationship table created by
Entity Framework: I have two tables (posting the corresponding models here)
I have models for Event and Child, but not for ChildEvent.
Each Event has multiple children and each Child has multiple events as it is a many-to-many relationship. Now I am able to add and delete the entries into the tables. But when I am trying to access the children associated with each event, I am getting a null.
I have tried googling and I found some posts on easy loading. I have tried turning that off but the problem persists still. Is there any way I can get the children associated with each event. I do not have a model for ChildEvent? I cannot directly query the ChildEvent table.
Public Class Event { // This is the event model
public int EventId // This is the primary key
public int EventName
public virtual ICollection<Child> Children // used to reference Child table
}
Public Class Child { // This is the Child Model
public int ChildId // primary key
public string FirstName
public virtual ICollection<Event> Events // used to refer to Event table
}
I have the relationship table created the by Entity Framework
ChildEvent:
public int ChildId { get; set; }
public int EventId { get; set; }
This will ensure that Entity Frameowrk knows how you want your Many To Many relationships setup. This way Lazy Loading will work and EF will map Events to Children and Children to Events.
public class EventMap: EntityTypeConfiguration<Event>
{
public EventMap()
{
HasMany(e => e.Children)
.WithMany(c => c.Events)
.Map(m =>
{
m.MapLeftKey("EventId");
m.MapRightKey("ChildId");
m.ToTable("Event_Children");
});
}
}
Related
According to msdn article, the following should create an optional:optional relationship, but instead it creates optional:many relationship. Is the article wrong?
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
modelBuilder.Entity<Optional_1>()
.HasKey(o1 => o1.id1);
modelBuilder.Entity<Optional_2>()
.HasKey(o2 => o2.id2);
modelBuilder.Entity<Optional_1>()
.HasOptional(o1 => o1.Dependent)
.WithOptionalPrincipal(o2 => o2.Principal);
}
public class Optional_1
{
public int id1 { get; set; }
public Optional_2 Dependent { get; set; }
}
public class Optional_2
{
public int id2 { get; set; }
public Optional_1 Principal { get; set; }
}
thank you
The table might look like one to many, but Entity Framework will enforce it as optional:optional because of the navigation properties. Since the navigation property is only a single object and not a collection, there is no way to add multiple.
If you look at the generated tables, it creates a nullable foreign key to your principal table (Optional_1). This allows you to create an Optional_2 that is not associated with an Optional_1.
If you were to insert multiple rows into Optional_2 that have the same foreign key to Optional_1 outside of EF, there wouldn't be anything preventing it from going through. If you were to try and load these entities you would get an error. You can't add a unique index to the column because it needs to allow NULL since it is optional.
I'm trying to map many to many SQL relation in Entity Framework using code first.
Here are my classes:
[Table("B3_360ViewerData")]
public class ViewerData : Entity
{
//.... Other properties ...//
// The navigation property
public EntityCollection<Feature> Features { get; set; }
}
[Table("B3_Feature")]
public class Feature : Entity
{
//.... Other properties ...//
// The navigation property
public ICollection<ViewerData> ViewerData { get; set; }
}
CREATE TABLE [dbo].[ViewerDataFeatures](
[Feature_Id] [int] NULL,
[ViewerData_Id] [int] NULL
)
The insertion in this table went well like I expected, but when I want to retrieve the ViewerData entity i don't get Features collection populated it is null.
Am I missing something?
If I have the following code generating my database it assigns a foreign key from the TankComponent table to the Asset table instead of the Tank table. Can someone explain why? Do I need to turn off a specific convention or change in the Fluent API? Is it really only looking at the column name?
[Table("Asset")]
public abstract class Asset
{
[Key]
public int AssetId { get; set; }
public string Name { get; set; }
public string Description { get; set; }
}
[Table("Tank")]
public class Tank : Asset
{
public Tank()
{
this.TankCompnents = new Collection<TankComponent>();
}
public int TankField1 { get; set; }
public ICollection<TankComponent> TankCompnents { get; set; }
[NotMapped]
public IEnumerable<Floor> Floors { get { return this.TankCompnents.OfType<Floor>(); } }
}
[Table("TankComponent")]
public abstract class TankComponent
{
[Key]
public int TankComponentId { get; set; }
[ForeignKey("Tank")]
public int AssetId { get; set; }
public Tank Tank { get; set; }
public string Name { get; set; }
}
//forgot this in initial post
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
base.OnModelCreating(modelBuilder);
modelBuilder.Entity<Tank>()
.Map(m =>
{
m.Properties(a => new { a.AssetId, a.Name, a.Description });
m.Requires("AssetType").HasValue(1);
m.ToTable("Asset");
})
.Map(m =>
{
m.Properties(t => new { t.AssetId, t.TankField1 });
m.ToTable("Tank");
});
}
This mapping line...
m.Requires("AssetType").HasValue(1);
...and your comments seem to indicate that you possibly have a misunderstanding how Table-Per-Type (TPT) inheritance works.
EF does not need a specific column in the table of the base class Asset to detect what the actual type of the entity with a given primary key value is - unless you would use Table-Per-Hierarchy (TPH) inheritance mapping (i.e. a mapping without having [Table] attributes on your entities). For TPH a specific column - the discriminator - in indeed necessary to distinguish between the types because all properties of all entities in the inheritance tree would be stored in a single table. If you don't specify a discriminator explicitly - like AssetType - EF would create a column called Discriminator by default.
Now, TPT is a different story. If you query an entity that has other derived entities - for example...
var asset = context.Assets.First();
...EF will not only create a SQL query like SELECT TOP(1) * FROM ASSETS on the base table alone but instead a - possibly very complex - query with many LEFT OUTER JOINs to many other tables that belong to all possible derived entities. This query would either find a row in the Tank table or not. If it does find one EF will materialize a Tank object. If not it will materialize an Asset. (Cannot be the case here because Asset is abstract but assume for a moment it would not be abstract.) If Asset has other derived types EF will join their tables as well and decide again about the concrete entity type depending on the existence of joined rows.
So, with TPT the type is detected not by a special column but only by the result of (left outer) table joins.
The line above seems to confuse EF somehow. But it really doesn't belong into a TPT mapping and I would remove your whole mapping with Fluent API.
I've tested that the result is correct when you remove the mapping - i.e. the FK relationship will be created between TankComponent and Tank table (not Asset table).
I have the following classes in which I am trying to map the entity object to the view model:
public class ConsumerIndexItem: MappedViewModel<Consumer>
{
public string UserName { get; set; }
public string RoleDescription { get; set; }
public override void MapFromEntity(Consumer entity)
{
base.MapFromEntity(entity);
UserName = entity.User.UserName;
}
}
public class Consumer: AuditableEntity
{
public virtual User User { get; set; }
public virtual Role Role { get; set; }
}
public class IndexModel<TIndexItem, TEntity> : ViewModel where TEntity : new()
{
public IndexModel()
{
Items = new List<TIndexItem>();
}
public List<TIndexItem> Items { get; set; }
public virtual void MapFromEntityList(IEnumerable<TEntity> entityList)
{
Items = Mapper.Map<IEnumerable<TEntity>, List<TIndexItem>>(entityList);
}
}
public class ConsumerIndexModel: IndexModel<ConsumerIndexItem, Consumer>
However, if I drive the mapping with the following code:
var model = new ConsumerIndexModel();
var list = _repository.List().Where(c => c.Parent == null).ToList();
model.MapFromEntityList(list);
return View(model);
on the line UserName = entity.User.UserName; in ConsumerIndexItem I get the following exception:
A relationship multiplicity constraint violation occurred: An EntityReference can have no more than one related object, but the query returned more than one related object. This is a non-recoverable error.
If I execute ?entity.User.UserName in the Immediate Window I get the expected user name value. What could be wrong here?
Let me explain why I had this exception and you may be able to correlate it with your own situation. I had EF Code First model mapped to the existing database. There was one-to-many relationship between two of the entities. The child table had composite primary consisting of the Id and Date. However, I missed the second segment of the primary key in my fluent map:
this.HasKey(t => t.Id);
The strange part of that was that the model worked but was throwing an exception in certain cases and it was very hard to understand why. Apparently when EF was loading the parent of child entity there were more than one parent since the key had not only Id but Date as well. The resolution was to include the second part of the key:
this.HasKey(t => new { t.Id, t.Date });
The tool that helped me to pinpoint the problem was EF Power Tools, currently it is in Beta 3. The tool gives a context menu for the EF context class where one the item is View Entity Model DDL SQL. Although I could have found this just by checking the code, the tool is nice in showing how close the EF model matches the actual database.
I believe that you're getting this exception because for a some reasons the multiplicity of relationship is violated. In my case it was incorrect mapping, in your it may be something else, I can't tell by looking at your code.
I think the problem maybe that you suppose that every user has only one consumer while this is not correct regarding data.
I had the same problem and it was because the relationship was on-to-many and I made it one-to-one.
I'm sharing data via RIA services using a presentation model on top of LINQ to SQL classes. On the Silverlight client, I created a couple of new entities (album and artist), associated them with each other (by either adding the album to the artist's album collection, or setting the Artist property on the album - either one works), added them to the context, and submitted changes.
On the server, I get two separate Insert calls - one for the album and one for the artist. These entitites are new so their ID values are both set to the default int value (0 - keep in mind that depending on my DB, this could be a valid ID in the DB) because as far as I know you don't set IDs for new entities on the client. This all would work fine if I was transferring the LINQ to SQL classes via my RIA services, because even though the Album insert includes the Artist and the Artist insert includes the Album, both are Entities and the L2S context recognizes them. However, with my custom presentation model objects, I need to convert them back to the LINQ to SQL classes maintaining the associations in the process so they can be added to the L2S context.
Put simply, as far as I can tell, this is impossible. Each entity gets its own Insert call, but there's no way you can just insert the one entity because without IDs the associations are lost. If the database used GUID identifiers it would be a different story because I could set those on the client.
Is this possible, or should I be pursuing another design?
If you create the correct parent-child associations, you'll just need to track the inserted presentation model(PM)-entity relationships:
PM's:
public class Parent
{
[Key]
public int? ParentID { get; set; }
[Include]
[Composition]
[Association("Parent_1-*_Child", "ParentID", "ParentID", IsForeignKey = false)]
public IEnumerable<Child> Children { get; set; }
}
public class Child
{
[Key]
public int? ChildID { get; set; }
[Include]
[Association("Parent_1-*_Child", "ParentID", "ParentID", IsForeignKey = true)]
public Parent Parent { get; set; }
}
Be sure to use [Composition] to force WCF RIA to call the InsertChild method on the DomainService.
Silverlight:
...
public Child NewChild(Parent parent)
{
return new Child
{
ParentID = parent.ParentID,
Parent = parent,
};
}
...
public void SubmitChanges()
{
DomainContext.SubmitChanges(SaveComplete, null);
}
...
If the Parent is not new, it will have a ParentID. If it is new, the Parent ID will be null. By setting the Child.Parent to the reference of the new Parent, RIA understands what you are trying to do preserves the reference after it has been sent to the server.
DomainService on the server:
[EnableClientAccess]
public class FamilyDomainService : DomainService
{
private readonly IDictionary<object, EntityObject> _insertedObjectMap;
public void InsertParent(Parent parent)
{
ParentEntity parentEntity = new ParentEntity();
ObjectContext.AddToParents(parentEntity);
_insertedObjectMap[parent] = parentEntity;
ChangeSet.Associate(parent, parentEntity, (p, e) => p.ParentID = e.ParentID;
}
public void InsertChild(Child child)
{
var childEntity = new ChildEntity();
if (child.ParentID.HasValue) // Used when the Parent already exists, but the Child is new
{
childEntity.ParentID = child.ParentID.GetValueOrDefault();
ObjectContext.AddToChildren(childEntity);
}
else // Used when the Parent and Child are inserted on the same request
{
ParentEntity parentEntity;
if (child.Parent != null && _insertedObjectMap.TryGetValue(child.Parent, out parentEntity))
{
parentEntity.Children.Add(childEntity);
ChangeSet.Associate(child, childEntity, (c, e) => c.ParentID = e.Parent.ParentID);
}
else
{
throw new Exception("Unable to insert Child: ParentID is null and the parent Parent cannot be found");
}
}
_insertedObjectMap[child] = childEntity;
ChangeSet.Associate(child, childEntity, (c, e) => c.ChildID = e.ChildID );
}
protected override bool PersistChangeSet()
{
ObjectContext.SaveChanges();
_insertedObjectMap.Clear();
return true;
}
}
The two important pieces here. First, the '_insertedObjectMap' stores the relationship between newly inserted entities that do not have the ID set. Since you are doing this in a transaction and single call to the DB, the ID will only be set after all entities have been inserted. By storing the relationship, the Child PM can find the entity version of the Parent PM using the database. The Child entity is added to the Children collection on the Parent entity and LINQToSQL or LINQToEnityFramework should handle the foreign key for you.
The second piece is associating the changes after the transaction is committed. In the scenario where the Parent and Child are both submitted, you must remember to set the ParentID foreign key on the Child.
My info from the ChangeSet.Associate() came from: http://blogs.msdn.com/deepm/archive/2009/11/20/wcf-ria-services-presentation-model-explained.aspx