Using EF 6 code first. I have an Entity class and a Name class, and want to query a list of all Entity objects with their respective Names so that I can show on a Gridview control.
These are my class definitions:
public class Entity
{
[Key]
public int EntityKey { get; set; }
public virtual ICollection<Name> Names { get; set; }
}
public class Name
{
[Key]
public int NameKey { get; set; }
public int EntityKey { get; set; }
public string FirstName { get; set; }
public string LastName { get; set; }
public virtual Entity Entity { get; set; }
}
In my Data Access Layer, I have a method that gets all entities from database:
public List<Entity> GetEntities()
{
SdmDBContext entityDbContext = new SdmDBContext();
return entityDbContext.Entities.ToList();
}
but with this query, I don't get the Names that are associated to the Entity class. I have tried LINQ and lambda expressions, as well as an .Include operation, but not sure how to formulate the syntax properly.
How can I change the DAL method to return the Names associated with all existing entities?
You code is loading the list of Entities in the Entity table, and with 'lazy loading' it should also return associated Name collections when you access them. You declare your ICollection as virtual to enable 'lazy loading' because a proxy object will be created to make DB requests when you access something that hasn't been eagerly loaded.
Include is just a request to eagerly load related entities by using the navigation properties defined on the object. You can call Include using a lambda or string representing the name of the navigational property you want to eagerly load.
For example:
public List<Entity> GetEntities()
{
SdmDBContext entityDbContext = new SdmDBContext();
return entityDbContext.Entities.Include("Names").ToList();
}
Related
In my database, I have the tables.
Item:
ItemID, Name
Sets:
SetID, Name
In the database this would normally be a many to many relationship, so I have a foreign key table in between them called ItemSets.
ItemSets:
ItemID, SetID
When I implement Entity Framework, it removes the ItemSets table and creates this on both of the classes it auto creates:
Item:
public Item()
{
this.Sets = new HashSet<Set>();
}
public System.Guid ItemID { get; set; }
public string Name { get; set; }
[System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Usage", "CA2227:CollectionPropertiesShouldBeReadOnly")]
public virtual ICollection<Set> Sets { get; set; }
Set:
public Set()
{
this.Items = new HashSet<Item>();
}
public System.Guid SetsID { get; set; }
public string Name { get; set; }
[System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Usage", "CA2227:CollectionPropertiesShouldBeReadOnly")]
public virtual ICollection<Item> Items { get; set; }
Now what I'm trying to do is select all of the Items that have the same SetsID (logically they are part of a set) and return them as an Item list.
Two questions:
Is the table still being represented in this way through the two classes?
Is this doable in EF without using custom SQL and only using linq?
Answer like Mithgroth Said
db.Items.Where(i => i.SetID == someSetId)
My code:
public IEnumerable<Item> GetItemsInSet(Set set)
{
return IronHelmContext.Items
.Where(x => x.Sets == set)
.ToList();
}
So what it is to note: EF allows this relationship to be determined by just calling the classes of each other. In the Item class there is a .Sets method (due to custom repository) this allows for the comparison to the Set class object.
You can naturally do
db.Items.Where(i => i.SetID == someSetId)
The data comes from the many-to-many table you declared.
I have a Parent and Child class/table and I'm using Fluent API to configure my mapping. The tables are joined on non primary key fields in each table, so according to what I've read, I can't configure the join in Entity Framework. Because of that, I'm going to load my Parent.Children property manually (parent.Children = (from x in context.Children...)
However, I'm getting an exception on my mapping of Parent. My classes look like
public class Parent
{
// Unique primary key
public int ParentPrimaryKey { get; set; }
// These two fields together compose the foreign key to join to Child
public string ParentForeignKey1 { get; set; }
public string ParentForeignKey2 { get; set; }
public List<Child> Children { get; set; }
}
public class Child
{
// Unique primary key
public int ChildPrimaryKey { get; set; }
// These two fields together compose the (non-unique) foreign key to join to Parent
public string ChildForeignKey1 { get; set; }
public string ChildForeignKey2 { get; set; }
}
When I try to query context.Parents, I get an exception because the SQL that Entity Framework generates is trying to join Parent to Child and is looking for a property on Child called Child_ParentPrimaryKey.
If I add modelBuilder.Entity<Parent>().Ignore(p => p.Children); I get the exception System.NotSupportedException: The specified type member 'Packages' is not supported in LINQ to Entities. Only initializers, entity members, and entity navigation properties are supported.
How can I keep Children as a non-mapped property on Parent without getting errors?
You can exclude a property from EF mapping by adding the [NotMapped] attribute:
using System.ComponentModel.DataAnnotations.Schema;
public class Parent {
// ...
[NotMapped]
public List<Child> Children { get; set; }
}
DataAnnotations - NotMapped Attribute
Thanks to everyone who responded. Your comments made me realize the problem was that I was using the Parent.Children property in my LINQ query. I removed that, and it's working now.
I'm using ASP.NET 5 with Entity Framework 7.
I have this models:
public class ParentModel
{
public Guid Id { get; set; }
public virtual ChildModel Children { get; set; }
}
public class ChildModel
{
public Guid Id { get; set; }
public virtual AnotherChildModel AnotherChild { get; set; }
}
public class AnotherChildModel
{
public Guid Id { get; set; }
public string Text { get; set; }
}
When I'm trying to add ParentModel to database, it doesn't automatically add ChildModel and AnotherChildModel to database, while ParentModel completely correct in code, for example:
var parent = new ParentModel() { Children = new ChildModel() { AnotherChild = new AnotherChildModel() { Text = "sometext" }}};
So, simple parentSet.Add(parent) doesn't work, is there another way, except for manually adding all models in sets?
EDIT:
Exception I have:
DbUpdateException: An error occurred while updating the entries. See the inner exception for details.
SqlException: The INSERT statement conflicted with the FOREIGN KEY constraint "FK_ParentModel_ChildModel_ChildrenId". The conflict occurred in database "aspnet5-WebApplication1-922849d0-b7da-4169-8150-9a2d05240a47", table "dbo.ChildModel", column 'Id'. The statement has been terminated.
In the current RC1 version of EF7, Add() only recursively adds adhering objects in collections (i.e. true children), not referenced entities, as EF6 did.
So if you'd have the following model (that's more consistent with the names you chose) ...
public class ParentModel
{
public Guid Id { get; set; }
public virtual ICollection<ChildModel> Children { get; set; }
}
... the children would also be added by the single statement parentSet.Add(parent).
I don't know if this is intended behavior. The RC has already proven to come with issues a "release candidate" shouldn't have. But maybe it's an OO-inspired design decision that parents encapsulate their children and not the reverse.
I would like to use EF Code first to handle a custom association (or navigation property) that is not based on a key but on one of the property values of an entity.
My classes are these
public class MyConfig
{
public int Id { get; set; }
public int InstanceNumber { get; set; }
public bool IsValid { get; set; }
public int Code { get; set; }
}
public class MyAsset
{
public int Id { get; set; }
public string Name { get; set; }
public List<MyConfig> MyConfigs { get; set; }
public int InstanceValue { get; set; }
}
So I would like a collection of MyConfig entities on any of the MyAsset entities. When the MyAsset entity is loaded from the database, I would like it to contain a list of MyConfig-entities whose value in its "InstanceNumber" matches the InstanceValue of the MyAsset entity.
Can I do this with EF6 codefirst? I have read up on overriding the built-in conventions of EF, but I have only been able to find solutions that work with specific primary keys.
And does the above make sense at all?
Well I can see that why you would like to do this but currently your only option is to use the Include extension method and get all MyConfig instances then you need to filter the list of MyConfig instances in MyAsset instance you queried like this:
public class MyAssetRepository : IMyAssetRepository
{
public MyAsset Get(int assetId)
{
using (var context = new AssetContext())
{
var selectedAsset = context.MyAssets.Include(a => a.MyConfigs).Single(a => a.Id == assetId);
selectedAsset.MyConfigs = selectedAsset.MyConfigs
.Where(c => c.InstanceNumber == selectedAsset.InstanceValue)
.ToList();
return selectedAsset;
}
}
}
As in this example getting a MyAsset instance is encapsulated in a repository so the logic of getting a MyAsset instance with its MyConfig entities is encapsulated and hidden from the code outside of the repository.
Of course the overhead of getting all MyConfig entities will be present which could be resolved by using an Include extension method that accepts a filter argument, however it does not exist currently.
I am still in the EF learning process and I am trying to get more familiar with EF lazy loading.
Please consider the following class and test:
[Table("Tenant")]
public class Tenant : IEntity
{
public int Id { get; set; }
public virtual string Name { get; set; }
[Key]
public string Guid { get; set; }
public virtual ICollection<User> Users { get; set; }
public virtual ICollection<CaseType> CaseTypes { get; set; }
public Tenant()
{
Users = new List<User>();
CaseTypes = new List<CaseType>();
}
}
And the test:
[Test]
public void TenantLazyLoading()
{
var tenant = _applicationContext.Tenants.Create();
tenant.Guid = "d176dc7c-6b96-4ab6-bddf-ce5a12024c39";
_applicationContext.Tenants.Attach(tenant);
Assert.AreEqual(1, tenant.Users.Count); // Pass, the navigation property users was loaded (lazy)
Assert.AreEqual("localhost", tenant.Name); // Fail, the tenant name is not loaded
}
The lazy loading apparently only works on the Navigation properties, but not on the Tenant properties. I made both properties (Users and Name) virtual, but that doesn't seem to matter.
How can I lazy load the local properties of Tenant?
That's the way how it works. If you create an entity manually and Attach it to the context you are telling EF that you don't want to load the entity's scalar properties.
There is no lazy loading of scalar properties, you always must do it explicitly, either by adding...
_applicationContext.Entry(tenant).Reload();
...after Attach or by replacing the first three lines by:
var tenant = _applicationContext.Tenants
.Find(new Guid("d176dc7c-6b96-4ab6-bddf-ce5a12024c39"));