Conditionally Exclude Child Property in Entity Framework Query - entity-framework

Given the following object graph
public class Bar {
public Guid Id {get;set;}
public string SomeBar {get;set;}
}
public class Foo {
public Guid Id {get;set;}
public string FooString {get; set;}
public IList<Bar> Bars {get;set;}
}
How do I conditionally exclude the Bars property properly so that Entity Framework doesn't load the entire object graph?
// my thoughts
public Task<Foo> GetFooByIdAsync(Guid id, bool includeBars)
{
var query = _db.Foos.Where(f => f.Id == id);
if(!includeBars)
{
return query.Select(f=> new Foo{
Id = f.Id,
FooString = f.FooString
}).ToListAsync();
} else {
return query.ToListAsync();
}
}

Just conditionally add the Include(f => f.Bars) into the query expression. Like this:
using System;
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations.Schema;
using System.Data.Common;
using System.Data.Entity;
using System.Data.Entity.ModelConfiguration;
using System.Data.SqlClient;
using System.Linq;
using System.Threading.Tasks;
namespace ConsoleApp8
{
public class Bar
{
public Guid Id { get; set; }
public string SomeBar { get; set; }
}
public class Foo
{
public Guid Id { get; set; }
public string FooString { get; set; }
public IList<Bar> Bars { get; set; }
}
class Db: DbContext
{
public DbSet<Foo> Foos { get; set; }
public DbSet<Bar> Bar { get; set; }
public async Task<Foo> GetFooByIdAsync(Guid id, bool includeBars)
{
var query = (IQueryable<Foo>)Foos.AsNoTracking();
if (includeBars)
{
query = query.Include(f => f.Bars);
}
query = query.Where(f => f.Id == id);
var results = await query.ToListAsync();
return results.FirstOrDefault();
}
}
class Program
{
static void Main(string[] args)
{
Database.SetInitializer(new DropCreateDatabaseAlways<Db>());
using (var db = new Db())
{
db.Database.Initialize(false);
db.Database.Log = m => Console.WriteLine(m);
var r1 = db.GetFooByIdAsync(Guid.Empty, false).Result;
var r2 = db.GetFooByIdAsync(Guid.Empty, true).Result;
}
Console.WriteLine("Hit any key to exit");
Console.ReadKey();
}
}
}
outputs
Opened connection asynchronously at 9/11/2017 11:37:26 PM -05:00
SELECT
[Extent1].[Id] AS [Id],
[Extent1].[FooString] AS [FooString]
FROM [dbo].[Foos] AS [Extent1]
WHERE [Extent1].[Id] = #p__linq__0
-- p__linq__0: '00000000-0000-0000-0000-000000000000' (Type = Guid, IsNullable = false)
-- Executing asynchronously at 9/11/2017 11:37:26 PM -05:00
-- Completed in 22 ms with result: SqlDataReader
Closed connection at 9/11/2017 11:37:26 PM -05:00
Opened connection asynchronously at 9/11/2017 11:37:26 PM -05:00
SELECT
[Project1].[C1] AS [C1],
[Project1].[Id] AS [Id],
[Project1].[FooString] AS [FooString],
[Project1].[C2] AS [C2],
[Project1].[Id1] AS [Id1],
[Project1].[SomeBar] AS [SomeBar]
FROM ( SELECT
[Extent1].[Id] AS [Id],
[Extent1].[FooString] AS [FooString],
1 AS [C1],
[Extent2].[Id] AS [Id1],
[Extent2].[SomeBar] AS [SomeBar],
CASE WHEN ([Extent2].[Id] IS NULL) THEN CAST(NULL AS int) ELSE 1 END AS [C2]
FROM [dbo].[Foos] AS [Extent1]
LEFT OUTER JOIN [dbo].[Bars] AS [Extent2] ON [Extent1].[Id] = [Extent2].[Foo_Id]
WHERE [Extent1].[Id] = #p__linq__0
) AS [Project1]
ORDER BY [Project1].[Id] ASC, [Project1].[C2] ASC
-- p__linq__0: '00000000-0000-0000-0000-000000000000' (Type = Guid, IsNullable = false)
-- Executing asynchronously at 9/11/2017 11:37:26 PM -05:00
-- Completed in 2 ms with result: SqlDataReader
Closed connection at 9/11/2017 11:37:26 PM -05:00
Hit any key to exit

Related

EF Core Queryable<T>. Count() returns different number than Queryable<T> .ToList().Count(). Is this even possible or is it a bug?

I have a query that grabs some filtered data, but it's giving me some strange results. See the attached image with the VS Code debugger (the var sourceis a Queryable, something like _dbContext.ModelName)
var count= await source.CountAsync();
is giving a different result than
var count2 = (await source.ToListAsync()).Count();
How is this even possible? With these results, everything I thought I knew about EF becomes a lie.
The same is true for the sync methods.
Can anyone explain to me in which scenario is this possible? Could it be a bug in EF Core 3.1?
Context of the program: side project, DataBase is not accessed by anyone, just by me. There is no other operations in this scenario
edit : the variable source has an Include, so it is _dbContext.ModelName.Include(b=>b.OtherModel). When I remove the Include, it works.
edit2 The ModelName.OtherModel property is null in some cases, but OtherModel.Id (the primary key) cannot be null, so, I guess, when Include performs the Join, excludes the occurrences of ModelName that haven't an OtherModel. Could be this?
Under normal circumstances, with referential integrity intact, this cannot happen.
Take a look at the following code, where both count operations will correctly return a result of 3:
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.Logging;
namespace IssueConsoleTemplate
{
public class IceCream
{
public int IceCreamId { get; set; }
public string Name { get; set; }
public int IceCreamBrandId { get; set; }
public IceCreamBrand Brand { get; set; }
}
public class IceCreamBrand
{
public int IceCreamBrandId { get; set; }
public string Name { get; set; }
public virtual ICollection<IceCream> IceCreams { get; set; } = new HashSet<IceCream>();
}
public class Context : DbContext
{
public DbSet<IceCream> IceCreams { get; set; }
public DbSet<IceCreamBrand> IceCreamBrands { get; set; }
protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
{
optionsBuilder
.UseMySql(
"server=127.0.0.1;port=3306;user=root;password=;database=So63071963",
b => b.ServerVersion("8.0.20-mysql"))
//.UseSqlServer(#"Data Source=.\MSSQL14;Integrated Security=SSPI;Initial Catalog=So63071963")
.UseLoggerFactory(
LoggerFactory.Create(
b => b
.AddConsole()
.AddFilter(level => level >= LogLevel.Information)))
.EnableSensitiveDataLogging()
.EnableDetailedErrors();
}
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.Entity<IceCream>()
.HasData(
new IceCream {IceCreamId = 1, Name = "Vanilla", IceCreamBrandId = 1},
new IceCream {IceCreamId = 2, Name = "Chocolate", IceCreamBrandId = 2},
new IceCream {IceCreamId = 3, Name = "Matcha", IceCreamBrandId = 3});
modelBuilder.Entity<IceCreamBrand>()
.HasData(
new IceCreamBrand {IceCreamBrandId = 1, Name = "My Brand"},
new IceCreamBrand {IceCreamBrandId = 2, Name = "Your Brand"},
new IceCreamBrand {IceCreamBrandId = 3, Name = "Our Brand"});
}
}
internal static class Program
{
private static void Main()
{
//
// Operations with referential integrity intact:
//
using var context = new Context();
context.Database.EnsureDeleted();
context.Database.EnsureCreated();
// Does not use INNER JOIN. Directly uses COUNT(*) on `IceCreams`:
// SELECT COUNT(*)
// FROM `IceCreams` AS `i`
var databaseSideCount = context.IceCreams
.Include(s => s.Brand)
.Count();
// Does use INNER JOIN. Counts using Linq:
// SELECT `i`.`IceCreamId`, `i`.`IceCreamBrandId`, `i`.`Name`, `i0`.`IceCreamBrandId`, `i0`.`Name`
// FROM `IceCreams` AS `i`
// INNER JOIN `IceCreamBrands` AS `i0` ON `i`.`IceCreamBrandId` = `i0`.`IceCreamBrandId`
var clientSideCount = context.IceCreams
.Include(s => s.Brand)
.AsEnumerable() // or ToList() etc.
.Count();
Debug.Assert(databaseSideCount == 3);
Debug.Assert(clientSideCount == 3);
Debug.Assert(databaseSideCount == clientSideCount);
}
}
}
Here it is also not possible to damage the referential integrity, because it is guarded by a foreign key constraint in the database.
If you create your database on your own however (using a custom crafted SQL script) and leave out the foreign key constraint, but still let EF Core believe that there is one in place, and then violate the referential integrity by using a non existing ID in a foreign key column, you can get different results for database-side (here 3) and client-side (here 2) count operations:
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.Logging;
namespace IssueConsoleTemplate
{
public class IceCream
{
public int IceCreamId { get; set; }
public string Name { get; set; }
public int IceCreamBrandId { get; set; }
public IceCreamBrand Brand { get; set; }
}
public class IceCreamBrand
{
public int IceCreamBrandId { get; set; }
public string Name { get; set; }
public virtual ICollection<IceCream> IceCreams { get; set; } = new HashSet<IceCream>();
}
public class Context : DbContext
{
public DbSet<IceCream> IceCreams { get; set; }
public DbSet<IceCreamBrand> IceCreamBrands { get; set; }
protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
{
optionsBuilder
.UseMySql(
"server=127.0.0.1;port=3306;user=root;password=;database=So63071963",
b => b.ServerVersion("8.0.20-mysql"))
//.UseSqlServer(#"Data Source=.\MSSQL14;Integrated Security=SSPI;Initial Catalog=So63071963")
.UseLoggerFactory(
LoggerFactory.Create(
b => b
.AddConsole()
.AddFilter(level => level >= LogLevel.Information)))
.EnableSensitiveDataLogging()
.EnableDetailedErrors();
}
}
internal static class Program
{
private static void Main()
{
//
// Operations with referential integrity violated:
//
using var context = new Context();
// Manually create MySQL database with a missing reference between
// the Matcha ice cream and any brand.
context.Database.ExecuteSqlRaw(
#"
DROP DATABASE IF EXISTS `So63071963`;
CREATE DATABASE `So63071963`;
USE `So63071963`;
CREATE TABLE `IceCreamBrands` (
`IceCreamBrandId` int NOT NULL AUTO_INCREMENT,
`Name` longtext CHARACTER SET utf8mb4 NULL,
CONSTRAINT `PK_IceCreamBrands` PRIMARY KEY (`IceCreamBrandId`)
);
CREATE TABLE `IceCreams` (
`IceCreamId` int NOT NULL AUTO_INCREMENT,
`Name` longtext CHARACTER SET utf8mb4 NULL,
`IceCreamBrandId` int NOT NULL,
CONSTRAINT `PK_IceCreams` PRIMARY KEY (`IceCreamId`)
);
INSERT INTO `IceCreamBrands` (`IceCreamBrandId`, `Name`) VALUES (1, 'My Brand');
INSERT INTO `IceCreamBrands` (`IceCreamBrandId`, `Name`) VALUES (2, 'Your Brand');
INSERT INTO `IceCreams` (`IceCreamId`, `IceCreamBrandId`, `Name`) VALUES (1, 1, 'Vanilla');
INSERT INTO `IceCreams` (`IceCreamId`, `IceCreamBrandId`, `Name`) VALUES (2, 2, 'Chocolate');
/* Use non-existing brand id 0: */
INSERT INTO `IceCreams` (`IceCreamId`, `IceCreamBrandId`, `Name`) VALUES (3, 0, 'Matcha');
");
// Does not use INNER JOIN. Directly uses COUNT(*) on `IceCreams`:
// SELECT COUNT(*)
// FROM `IceCreams` AS `i`
var databaseSideCount = context.IceCreams
.Include(s => s.Brand)
.Count();
// Does use INNER JOIN. Counts using Linq:
// SELECT `i`.`IceCreamId`, `i`.`IceCreamBrandId`, `i`.`Name`, `i0`.`IceCreamBrandId`, `i0`.`Name`
// FROM `IceCreams` AS `i`
// INNER JOIN `IceCreamBrands` AS `i0` ON `i`.`IceCreamBrandId` = `i0`.`IceCreamBrandId`
var clientSideCount = context.IceCreams
.Include(s => s.Brand)
.AsEnumerable() // or ToList() etc.
.Count();
Debug.Assert(databaseSideCount == 3);
Debug.Assert(clientSideCount == 2);
Debug.Assert(databaseSideCount != clientSideCount);
}
}
}

Self referencing (1:1) a table in Entity Framework code first model

I'm trying to self reference a table in my model to get a couple of details for my User entity
I have a class that looks like:
public class User
{
[Key]
[Column("recid")]
public int Id { get; set; }
[Column("givenname")]
public string FirstName { get; set; }
[Column("sn")]
public string LastName { get; set; }
[Column("mail")]
public string Email { get; set; }
[Column("managerEmail")]
public string LineManagerEmail { get; set; }
public string LineManagerFirstName { get; set; }
public string LineManagerLastName { get; set; }
}
How can I map this so that when returning a User the LineManagerFirstName and LineManagerLastName is retrieved from the same table, joined on LineManagerEmail?
for example, I anticipate the SQL to be something like:
SELECT
user.recid,
user.givenName,
user.sn,
user.mail,
user.managerEmail,
manager.givenName as lineManagerFirstName,
manager.givenName as lineManagerLastName,
FROM user
INNER JOIN user AS manager
ON user.managerEmail = manager.mail
In my Context class, I know I'll need to override the OnModelCreating method:
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
modelBuilder.Entity<User>().
}
...but being new to EF, this is where I'm getting a bit stuck!
If you use a view, you will probably not be able to update users as a view is readonly.
You should create entities according to tables and then query according to your needs and materialize a UserExtended with a query like
var q = from
u in Users
select
new UserExtended {
Id = u.Id,
/* .... */
LineManagerFirstName = u.Manager.FirstName
/* .... */
}
or a more elaborated query, in case of self join without the PK.
var q =
from u in Users
join m in Users on u.Email equals m.Email
select
new UserExtended {
Id = u.Id,
/* .... */
LineManagerFirstName = m.FirstName
/* .... */
}
will give you the following SQL (fields name do not match as I use one of my schema)
SELECT
[Extent1].[idUtilisateur] AS [idUtilisateur],
[Extent2].[Email] AS [Email]
FROM [dbo].[tableU] AS [Extent1]
INNER JOIN [dbo].[tableU] AS [Extent2] ON
([Extent1].[Email] = [Extent2].[Email])
OR (([Extent1].[Email] IS NULL) AND ([Extent2].[Email] IS NULL))

Performance issue regarding EF 4.1, code first and TPH

I'm trying to create a datamodel using EF 4.1 and code first.
My Poco classes looks like this:
public abstract class AttributeBase
{
public int Id { get; set; }
public string Name { get; set; }
public string AttributeDescription { get; set; }
public int Length { get; set; }
public bool AllowNull { get; set; }
public bool DisplayInWeb { get; set; }
}
public class StringListAttribute : AttributeBase
{
public ICollection<StringValue> Values { get; set; }
public StringListAttribute()
{
Values = new List<StringValue>();
}
}
public class StringValue
{
public int StringValueId { get; set; }
public string Value { get; set; }
}
The basic idea is that StringListAttribute inherits from the AttributeBase class (other classes also inherits from the AttributeBase class, but I didn't include them in this post)
The StringListAttribute class have a collection of StringValues. (a zero-to-many relationship). What I try to achieve in my MyContextExtention class is that whenever I want a list of AttributeBase objects from the database, I want to get all the StringListAttribute objects and to populate their StringValue collection in one go. I don't want any lazy loading. I want to retrieve as much data I can in as few queries as possible.
My Context class inherits from DbContext and I created an extension method on my BraArkivContext class
public class MyContext : DbContext
{
public DbSet<AttributeBase> Attributes { get; set; }
public MyContext()
{
Configuration.LazyLoadingEnabled = false;
}
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
modelBuilder.Entity<StringListAttribute>()
.HasMany(t=>t.Values).WithOptional().WillCascadeOnDelete();
base.OnModelCreating(modelBuilder);
}
}
public static class MyContextExtension
{
private const string Values = "Values";
public static List<AttributeBase> AttributesWithData1(this MyContext context)
{
var attributeBases = new List<AttributeBase>();
var stringListAttributes = context.Attributes.Include(Values).OfType<StringListAttribute>().ToList();
attributeBases.AddRange(stringListAttributes);
return attributeBases;
}
}
My test project (I'm using nUnit) )is nothing much to write home about and contains these methods:
public class TestHelper
{
public static StringListAttribute CreateStringListAttribute(string name, int listSize)
{
var listAttribute = new StringListAttribute();
listAttribute.Name = name;
for (int i = 0; i < listSize; i++)
{
var attr1 = new StringValue();
attr1.Value = String.Format("StringValue_{0}", i);
listAttribute.Values.Add(attr1);
}
return listAttribute;
}
}
[Test]
public void AttributeBase_GetAllAttributesWithData1()
{
//First add some data to the database
var numberOfObjects = 100;
var name = "StringListAttribute_ReadMultipleStringsListsFromDb_" + Guid.NewGuid().ToString();
var listAttributes = TestHelper.CreateStringLists(name, numberOfObjects);
using (var context = new MyContext())
{
foreach (var stringListAttribute in listAttributes)
{
context.Attributes.Add(stringListAttribute);
}
context.SaveChanges();
}
Stopwatch sw = new Stopwatch()
var attributes = new List<AttributeBase>();
using (var context = new MyContext())
{
sw.Start();
attributes = context.AttributesWithData1().ToList();
sw.Stop();
}
Debug.WriteLine(String.Format("Total time for {0} attributes is: {1}.", attributes.Count, sw.Elapsed));
Assert.IsNotNull(attributes);
}
When running the test method "AttributeBase_GetAllAttributesWithData1" The sql statement that is executed looks like this:
SELECT
[Project1].[Id] AS [Id],
[Project1].[C1] AS [C1],
[Project1].[Name] AS [Name],
[Project1].[AttributeDescription] AS [AttributeDescription],
[Project1].[Length] AS [Length],
[Project1].[AllowNull] AS [AllowNull],
[Project1].[DisplayInWeb] AS [DisplayInWeb],
[Project1].[Document_Id] AS [Document_Id],
[Project1].[C2] AS [C2],
[Project1].[StringValueId] AS [StringValueId],
[Project1].[Value] AS [Value],
[Project1].[StringListAttribute_Id] AS [StringListAttribute_Id]
FROM ( SELECT
[Extent1].[Id] AS [Id],
[Extent1].[Name] AS [Name],
[Extent1].[AttributeDescription] AS [AttributeDescription],
[Extent1].[Length] AS [Length],
[Extent1].[AllowNull] AS [AllowNull],
[Extent1].[DisplayInWeb] AS [DisplayInWeb],
[Extent1].[Document_Id] AS [Document_Id],
'0X0X' AS [C1],
[Extent2].[StringValueId] AS [StringValueId],
[Extent2].[Value] AS [Value],
[Extent2].[StringListAttribute_Id] AS [StringListAttribute_Id],
CASE WHEN ([Extent2].[StringValueId] IS NULL) THEN CAST(NULL AS int) ELSE 1 END AS [C2]
FROM [dbo].[AttributeBases] AS [Extent1]
LEFT OUTER JOIN [dbo].[StringValues] AS [Extent2] ON [Extent1].[Id] = [Extent2].[StringListAttribute_Id]
WHERE [Extent1].[Discriminator] = 'StringListAttribute'
) AS [Project1]
ORDER BY [Project1].[Id] ASC, [Project1].[C2] ASC
When I run this sql statement in SQL Server Management Studio it's quite fast, but the Entity Framework is taking a very long time to process the result and return the poco classes populated with data.
Is there any other way to model the AttributeBase, StringListAttribute and StringValue classes and the relation between them to decrease the processing time in EF?

EF Linq to entities query generating UNION for TPC CTP5 code-first entity

I have the following entities:
public class User
{
public int Id { get; set; }
public string Name { get; set; }
}
public class HappyUser : User
{
public bool IsHappy { get; set; }
}
I am configuring the entities using Table Per Concrete Type (TPC) in order to generate a User table and a HappyUser table. I want the HappyUser table to include the properties of the User class, and I don't want any relationship between the two tables.
I configure the entities as follows:
public class UserTest : DbContext
{
public DbSet<User> Users { get; set; }
public DbSet<HappyUser> HappyUsers { get; set; }
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.Entity<HappyUser>().Map(m =>
{
m.MapInheritedProperties();
m.ToTable("HappyUser");
});
}
}
The tables are generated correctly, but when I query the tables, EF generates a UNION on the User and HappyUser table. The query is as follows:
UserTest db = new UserTest();
var users = from u in db.Users
select u;
var happyUsers = from u in db.Users.OfType<HappyUser>()
select u;
The SQL for the Users generates a UNION. This is not what I expect or want. I'd like to simply retrieve the rows from the Users table.
SELECT
CASE WHEN ([UnionAll1].[C2] = 1) THEN '0X' ELSE '0X0X' END AS [C1],
[UnionAll1].[Id] AS [C2],
[UnionAll1].[Name] AS [C3],
CASE WHEN ([UnionAll1].[C2] = 1) THEN CAST(NULL AS bit) ELSE [UnionAll1].[C1] END AS [C4]
FROM (SELECT
[Extent1].[Id] AS [Id],
[Extent1].[Name] AS [Name],
CAST(NULL AS bit) AS [C1],
cast(1 as bit) AS [C2]
FROM [dbo].[User] AS [Extent1]
UNION ALL
SELECT
[Extent2].[Id] AS [Id],
[Extent2].[Name] AS [Name],
[Extent2].[IsHappy] AS [IsHappy],
cast(0 as bit) AS [C1]
FROM [dbo].[HappyUser] AS [Extent2]) AS [UnionAll1]
The SQL for the HappyUsers works as expected.
SELECT
'0X0X' AS [C1],
[Extent1].[Id] AS [Id],
[Extent1].[Name] AS [Name],
[Extent1].[IsHappy] AS [IsHappy]
FROM [dbo].[HappyUser] AS [Extent1]
Any ideas what I am doing wrong? Or is this a defect in EF?
HappyUsers are Users. Hence, db.Users should return both. The EF is correct, here.
However, the EF does have a limitation: There is no way to (in L2E, anyway) return results of only one type. Due to the Liskov Substitution Principal, you don't generally want to do this. But if you do, you can do it in ESQL, and there are (somewhat tedious) workarounds for L2E.
Bottom line: If you find yourself wanting to do this, rethink your design. Inheritance is probably not the right relationship in this case.
#Craig Stuntz is right, since both types are related, EF will keep that relation intact.
If you want to decouple both User types, then you could use an abstract base class.
public abstract class AbstractUser
{
public int Id { get; set; }
public string Name { get; set; }
}
public class User : AbstractUser
{
}
public class HappyUser : AbstractUser
{
public bool IsHappy { get; set; }
}
EF will now treat both entities as seperate and there's no need to call MapInheritedProperties() anymore.
Your select statements would then look like:
var users = db.Users.Where(...);
var happyUsers = db.HappyUsers.Where(...);

Entity Framework Code Only Relationship Not Being Read

(This looks like a long question, but it's not really, honest!)
I am trying to get a simple proof of concept working with Entity Framework 4 and the CTP 3 version of Code Only. It feels like I'm missing something really obvious and simple.
I have this following test which is failing:
[TestFixture]
public class ParentChildTests
{
[Test]
public void ChildRead_DatabaseContainsRelatedObjects_ParentIsNotNull()
{
var ctx = GetMyObjectContext();
var child = ctx.Children.Where(c => c.Id == 1).Single();
var parent = child.ParentTable;
Assert.That(parent, Is.Not.Null);
}
// GetMyObjectContext etc...
}
The read of child works fine and I get back a ChildTable whose ParentTableId value is '1' as I would expect, but the ParentTable property is NULL. I do not expect this because my POCOs have all virtual properties (see below) and EF4 has lazy loading enabled by default.
What am I missing?
Database
create table parent_table
(
parent_table_id int identity(1,1) primary key,
parent_table_name varchar(50) not null,
display_name varchar(50)
)
create table child_table
(
child_table_id int identity(1,1) primary key,
child_table_name varchar(50) not null,
parent_table_id int not null
)
alter table child_table add constraint FK_child_table__parent_table
foreign key (parent_table_id) references parent_table(parent_table_id)
POCO Entities
public class ParentTable
{
public virtual int Id { get; set; }
public virtual string Name { get; set; }
public virtual string DisplayName { get; set; }
}
public class ChildTable
{
public virtual int Id { get; set; }
public virtual string Name { get; set; }
public virtual int ParentTableId { get; set; }
public virtual ParentTable ParentTable { get; set; }
}
Entity Configurations
public class ParentTableConfiguration : EntityConfiguration<ParentTable>
{
public ParentTableConfiguration()
{
MapSingleType(pt => new
{
parent_table_id = pt.Id,
parent_table_name = pt.Name,
display_name = pt.DisplayName,
})
.ToTable("dbo.parent_table");
Property( pt => pt.Id ).IsIdentity();
Property( pt => pt.Name ).IsRequired();
}
}
public class ChildTableConfiguration : EntityConfiguration<ChildTable>
{
public ChildTableConfiguration()
{
MapSingleType(ct => new
{
child_table_id = ct.Id,
child_table_name = ct.Name,
parent_table_id = ct.ParentTableId,
})
.ToTable("dbo.child_table");
Property( ct => ct.Id ).IsIdentity();
Property( ct => ct.Name ).IsRequired();
Relationship(ct => ct.ParentTable)
.HasConstraint((ct, pt) => ct.ParentTableId == pt.Id);
}
}
(Thanks for reading this far!)
As far as understand you just do not load this navigation property.
This will result in eager loading.
var child = ctx.Children.Include("ParentTable").Where(c => c.Id == 1).Single();
Or you could enable lazy loading by setting ctx.ContextOptions.LazyLoadingEnabled = true;