Entity Framework 7 AddRange() Not Adding Foreign Entities - c#

I've been using EF Code First for awhile but this is my first time with EF7.
I have the following Model classes where Venue has a one-to-many relationship to Show:
public class Show
{
public int Id { get; set; }
public Venue Venue { get; set; }
//...
}
public class Venue
{
public int Id { get; set; }
public string Name {get; set; }
//...
public List<Show> Shows { get; set; }
}
I set up the DBContext like this:
public class NettlesContext : DbContext
{
public DbSet<Show> Shows { get; set; }
public DbSet<Venue> Venues { get; set; }
protected override void OnModelCreating(ModelBuilder builder)
{
base.OnModelCreating(builder);
new ShowConfiguration(builder.Entity<Show>());
new ImageConfiguration(builder.Entity<Image>());
}
}
public class ShowConfiguration
{
public ShowConfiguration(EntityTypeBuilder<Show> builder)
{
builder.Property(p => p.Id).IsRequired();
builder.Property(p => p.Title).IsRequired();
}
}
public class VenueConfiguration
{
public VenueConfiguration(EntityTypeBuilder<Venue> builder)
{
builder.Property(p => p.Id).IsRequired();
builder.Property(p => p.Name).IsRequired();
}
}
Then in some start up code I initialize the database like this:
private static void AddShows(NettlesContext db)
{
var shows = new List<Show>()
{
new Show()
{
Title = "Portland Country Dance Community Contra Dance",
Venue = new Venue()
{
Name = "Fulton Community Center",
},
},
new Show()
{
Title = "Portland Roadhouse Contra Dance",
Venue = new Venue()
{
Name = "Milwaukie Community Club",
},
},
};
db.Shows.AddRange(shows);
db.SaveChanges();
}
The Shows table is properly initialized except that the VenueId is null. The Venue table is entirely empty.
What's going on?

There is a second argument to DbSet.Add.
Add(TEntity entity, GraphBehavior behavior = GraphBehavior.IncludeDependents)
Although the default is to IncludeDependents (aka children entities), EF7's behavior of Add() is not identifying Venue as a child of Show. In your OnModelCreating you need to specify the relationship between Venue and Show. See Relationships in the EF7 Docs.
Example:
modelBuilder.Entity<Venue>(entityBuilder =>
{
entityBuilder
.HasMany(v => v.Shows)
.WithOne(s => s.Venue)
.HasForeignKey(s => s.VenueId);
});
Even with this however, you will still need to call .Add on the new instances of Venue because Show is not a dependent (child) of Venue.
private static void AddShows(NettlesContext db)
{
var fulton = new Venue()
{
Name = "Fulton Community Center",
};
var club = new Venue()
{
Name = "Milwaukie Community Club",
};
db.Venues.Add(fulton);
db.Venues.Add(club);
var shows = new List<Show>()
{
new Show()
{
Title = "Portland Country Dance Community Contra Dance",
Venue = fulton,
},
new Show()
{
Title = "Portland Roadhouse Contra Dance",
Venue = club
},
};
context.Shows.AddRange(shows);
}
Worth noting: this behavior of .Add() has been a source of confusion in EF7 RC1 and its behavior may be changing in EF7 RC2. See https://github.com/aspnet/EntityFramework/pull/4132

You need to update your model as this
public class Venue
{
public int Id { get; set; }
public string Name {get; set; }
//...
public List<Show> Shows { get; set; }
}
public class Show
{
public int Id { get; set; }
public int VenueId { get; set; }
[ForeignKey("VenueId")]
public Venue Venue { get; set; }
//...
}
ForegnKey attribute is used to define the foriegn key of the relation between two tables

Related

ef core: mapping value object using OwnsMany requires primary key to be defined

I have a class
public class Document
{
public string TranId { get; set; }
public Record Record { get; set; }
public List<Error> Errors { get; set; }
}
public class Record
{
public string TranId { get; set; }
public List<DataResult> DataResults { get; set; }
}
public class DataResult
{
public string DataSourceName { get; set; }
public List<DataField> DataFields { get; set; }
public List<CustomField> CustomFields { get; set; }
}
I want to map Record and DataResult classes as Value objects so I tried to map as
public void Configure(EntityTypeBuilder<Document> builder)
{
builder.ToTable("Document");
builder.HasKey(x => x.TranId);
builder.OwnsOne(a => a.Record, a =>
{
a.ToTable("Doc_Record");
a.Property(p => p.TranId).HasMaxLength(100)
.HasColumnName("TranID")
.HasDefaultValue("");
a.OwnsMany(x => x.DataResults, x =>
{
x.ToTable("Doc_Rec_DataResults");
x.Property(p => p.DataSourceName).HasMaxLength(150)
.HasColumnName("DataSourceName")
.HasDefaultValue("");
});
}
}
When I try to add this migration it errors with a message:
The entity type 'DataResult' requires a primary key to be defined.
And why it requires primary key cause I'm trying to map as a value
object?
Someone suggested using this link and I try to add
a.OwnsMany(x => x.DataResults, x =>
{
x.WithOwner().HasForeignKey("RecordId");
x.ToTable("Doc_Rec_DataResults");
x.Property(p => p.DataSourceName).HasMaxLength(150)
.HasColumnName("DataSourceName")
.HasDefaultValue("");
});
but this approach is not working cause WithOwner is available from .net core 3 where I'm using .net core 2 (and do I really need to add RecordId property into Record class (it's a value object).
It would be great if someone can provide an example of how to map collection of value objects with OwnsMany in EF Core 2.
In this ef core 2.2 example, we have a Company that owns a collection of Addresses, here's the implementation. note that i omitted some useful code to stick to the point, refer to the full example for further explanation. Also note that this feature OwnsMany() is not available in pre ef core 2.2
public class CompanyAddress
{
public string City { get; }
public string AddressLine1 { get; }
}
public class Company
{
private List<CompanyAddress> addresses = new List<CompanyAddress>();
public Guid Id { get; }
public string Name { get; }
public IEnumerable<CompanyAddress> Addresses { get => this.addresses; }
public void AssignAddress(CompanyAddress address)
{
var exists = this.addresses.Contains(address);
if (!exists)
{
this.addresses.Add(address);
}
}
}
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.Entity<Company>().OwnsMany<CompanyAddress>("Addresses", a =>
{
a.HasForeignKey("CompanyId");
a.Property(ca => ca.City);
a.Property(ca => ca.AddressLine1);
a.HasKey("CompanyId", "City", "AddressLine1");
});
}
here's a repo of full solution for the article's owner

EntityFrameworkCore and code first with Sqlite including multiple navigation

back in vs2015 i used an external program to create / modify my database and update it in vs with the ef designer.
It is also possible with vs2017 to use the designer with some third party tools, but i want to switch to code first. in this context i want to switch my class library to .net standard.
But i have trouble to get my databases working.
Here is an example, what i see when i open the edmx file.
The ID is not incremented, it is provided by an external service. every user of the service has an unique id and i store it there.
And now my (very simple) code first models:
public class Users {
public int UserID { get; set; }
public string Name { get; set; }
public Currency Currency { get; set; }
public TimeWatched TimeWatched { get; set; }
}
public class Currency {
public int UserID { get; set; }
public int CurrencyValue { get; set; }
public Users Users { get; set; }
}
public class TimeWatched {
public int UserID { get; set; }
public long Time { get; set; }
public Users Users { get; set; }
}
My DbContext looks like this:
using Microsoft.Data.Sqlite;
using Microsoft.EntityFrameworkCore;
public class context : DbContext {
public DbSet<Users> Users { get; set; }
public DbSet<Currency> Currency { get; set; }
public DbSet<TimeWatched> TimeWatched { get; set; }
protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder) {
var connectionStringBuilder = new SqliteConnectionStringBuilder { DataSource = "test.db" };
var connectionString = connectionStringBuilder.ToString();
var connection = new SqliteConnection(connectionString);
optionsBuilder.UseSqlite(connection);
}
protected override void OnModelCreating(ModelBuilder modelBuilder) {
base.OnModelCreating(modelBuilder);
modelBuilder.Entity<Users>()
.HasKey(c => c.UserID);
modelBuilder.Entity<Users>()
.Property(c => c.UserID)
.ValueGeneratedNever();
modelBuilder.Entity<Users>()
.HasOne(a => a.Currency)
.WithOne(b => b.Users)
.HasForeignKey<Currency>(b => b.UserID);
modelBuilder.Entity<Users>()
.HasOne(a => a.TimeWatched)
.WithOne(b => b.Users)
.HasForeignKey<TimeWatched>(b => b.UserID);
modelBuilder.Entity<Currency>()
.HasKey(k => k.UserID);
modelBuilder.Entity<TimeWatched>()
.HasKey(k => k.UserID);
}
}
It test it like this:
using(var context = new testStandard.context()) {
context.Database.EnsureCreated();
var user = new Users {
Name = "test",
TwitchUser = 123456,
Currency = new Currency {
TwitchUser = 123456,
CurrencyValue = 999
},
TimeWatched = new TimeWatched {
TwitchUser = 123456,
Time = 500
}
};
context.Users.Add(user);
context.SaveChanges();
}
using(var context = new context()) {
var user = context.Users.First();
// Currency and TimeWatched are always null
}
I dont know why currency and timewatched are always null.
what do i wrong?
Thanks Jamie!
I found a solution:
using(var context = new context()) {
var user = context.Users
.Include(c => c.Currency)
.Include(tw => tw.TimeWatched)
.First();
}

Issue when inserting new entity with relationships to existing entities (Entity Framework Core 1.1.0 )

I have encountered an issue when inserting (Add method of EF API) or updating (Update method of EF API) entities holding reference properties to existing entities (I call existing entity an entity that already exists in the database, and has its PK properly set).
The model consists in Place, Person, Address, and Status :
A person has many addresses.
A place has several persons, and also
several addresses.
Places, Persons and Addresses have statuses.
All entities have an Id, Name, Created date and Modified date (these fields are all defined in an abstract BaseEntity)
If I create a whole graph for a "Place", with new Persons and new Addresses, and save it in one step, everything is fine.
If I create a Place with Addreses then save it, it is still ok.
But at last when I add an existing person and resave the Place, I have an exception: EF actually tries to insert the existing person, and SQL Server throws an error because EF tried to insert a row with a provided Id (PK are set to be generated by SQL Server).
That means that by default, EF Core 1.1.0 looks like being unable to properly traverse relationships and discover which enitites should be added, and which one should be ignored or updated. It tries to insert an entity which already has its PK set to a positive value.
After doing some research, I discovered the new DbContext.ChangeTracker.Track() method of the EF Core 1.1.0 API, and it allows one to execute a callback method on all the entities discovered by traversing the relationships of the root entity.
Thanks to this, I have set up the appropriate State, according to the value of the primary key.
Without this code (in DbRepository.ApplyStates()), none of my insert would work, as long as they would refer a relation to an existing entity.
Note that with EF7 and the DNX CLI, this scenario would work, even without the DbRepository.ApplyStates() thing.
Source to reproduce
everything is in there: models, DbContext, Repository and test code.
using System;
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations.Schema;
using System.Linq;
using Microsoft.EntityFrameworkCore;
namespace EF110CoreTest
{
public class Program
{
public static void Main(string[] args)
{
Seed();
}
private static void Seed()
{
// Repo
var statusRepo = new DbRepository<Status>();
var personRepo = new DbRepository<Person>();
var addressRepo = new DbRepository<Address>();
var placeRepo = new DbRepository<Place>();
// Status
if (!statusRepo.GetAll().Any())
{
statusRepo.InsertOrUpdate(new Status() { Name = "Active" });
statusRepo.InsertOrUpdate(new Status() { Name = "Archive" });
statusRepo.SaveChanges();
}
var statusActive = statusRepo.GetSingle(1);
var statusArchive = statusRepo.GetSingle(2);
// Delete the non static data
foreach(var address in addressRepo.GetAll()) addressRepo.Delete(address);
addressRepo.SaveChanges();
foreach (var place in placeRepo.GetAll()) placeRepo.Delete(place);
placeRepo.SaveChanges();
foreach (var person in personRepo.GetAll()) personRepo.Delete(person);
personRepo.SaveChanges();
Console.WriteLine("Cleared any existing data");
/***********************************************************************/
// Step 1 : a person with status and addresses is saved
var personWithAddresses = new Person()
{
Name = "Jon SNOW",
Status = statusActive,
AddressCollection = new List<Address>()
{
new Address() { City = "Castleblack", Status = statusActive },
new Address() { City = "Winterfel", Status = statusArchive }
}
};
personRepo.InsertOrUpdate(personWithAddresses);
personRepo.SaveChanges();
Console.WriteLine("Step 1 ok");
System.Threading.Thread.Sleep(1000);
/***********************************************************************/
// Step 2 : Create a place with addresses
var placeWithAddress = new Place()
{
Name = "Castleblack",
Status = statusActive
};
placeWithAddress.AddressCollection.Add(new Address() { City = "Castleblack", Status = statusActive });
placeRepo.InsertOrUpdate(placeWithAddress);
placeRepo.SaveChanges();
Console.WriteLine("Step 2 ok");
System.Threading.Thread.Sleep(1000);
/***********************************************************************/
// Step 3 : add person to this place
placeWithAddress.PersonCollection.Add(personWithAddresses);
placeRepo.InsertOrUpdate(placeWithAddress);
placeRepo.SaveChanges();
Console.WriteLine("Step 3 ok");
System.Threading.Thread.Sleep(1000);
}
}
public class DbRepository<T> where T : BaseEntity
{
protected readonly MyContext _context;
public DbRepository() { _context = new MyContext(); }
public T GetSingle(int id) => _context.Set<T>().FirstOrDefault(e => e.Id == id);
public IEnumerable<T> GetAll() => _context.Set<T>().AsEnumerable();
public void Insert(T entity)
{
ApplyStates(entity);
_context.Add(entity);
}
public void Update(T entity)
{
ApplyStates(entity);
_context.Update(entity);
}
public void Delete(T entity)
{
_context.Remove(entity);
}
private void ApplyStates(T entity)
{
_context.ChangeTracker.TrackGraph(entity, node =>
{
var entry = node.Entry;
var childEntity = (BaseEntity)entry.Entity;
entry.State = childEntity.IsNew ? EntityState.Added : EntityState.Modified;
});
}
public void InsertOrUpdate(T entity)
{
if (entity.IsNew) Insert(entity); else Update(entity);
}
public void SaveChanges()
{
var pendingChanges = _context.ChangeTracker.Entries<T>()
.Where(entry => entry.State == EntityState.Added || entry.State == EntityState.Modified)
.Select(e => e.Entity)
.ToList();
foreach (var entity in pendingChanges)
{
entity.Modified = DateTime.Now;
if (entity.Created == null) entity.Created = DateTime.Now;
}
_context.SaveChanges();
}
}
#region Models
public abstract class BaseEntity
{
public int Id { get; set; }
public string Name { get; set; }
public DateTime? Created { get; set; }
public DateTime? Modified { get; set; }
[NotMapped]
public bool IsNew => Id <= 0;
}
public class Person : BaseEntity
{
public int? StatusId { get; set; }
public Status Status { get; set; }
public List<Address> AddressCollection { get; set; } = new List<Address>();
}
public class Address : BaseEntity
{
public string Zip { get; set; }
public string City { get; set; }
public int? StatusId { get; set; }
public Status Status { get; set; }
public int? PersonId { get; set; }
public Person Person { get; set; }
public int? PlaceId { get; set; }
public Place Place { get; set; }
}
public class Place : BaseEntity
{
public int? StatusId { get; set; }
public Status Status { get; set; }
public List<Person> PersonCollection { get; set; } = new List<Person>();
public List<Address> AddressCollection { get; set; } = new List<Address>();
}
public class Status : BaseEntity { }
#endregion
#region Context
public class MyContext : DbContext
{
public DbSet<Status> StatusCollection { get; set; }
public DbSet<Person> PersonCollection { get; set; }
public DbSet<Address> AddressCollection { get; set; }
public DbSet<Place> PlaceCollection { get; set; }
protected override void OnModelCreating(ModelBuilder builder)
{
// Basic event fire of model creation
base.OnModelCreating(builder);
// Status
builder.Entity<Status>().ToTable("Status", "Shared");
// Person
builder.Entity<Person>().ToTable("Person", "Shared");
builder.Entity<Person>()
.HasMany(p => p.AddressCollection)
.WithOne(a => a.Person);
// Address
builder.Entity<Address>().ToTable("Address", "Shared");
builder.Entity<Address>()
.HasOne(p => p.Person)
.WithMany(a => a.AddressCollection);
// Place
builder.Entity<Place>().ToTable("Place", "Shared");
builder.Entity<Place>()
.HasMany(p => p.AddressCollection)
.WithOne(p => p.Place);
}
protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
{
optionsBuilder.UseSqlServer(#"Server=(localdb)\mssqllocaldb;Database=EF110CoreTest;Trusted_Connection=True;");
}
}
#endregion
}
Project.json file
{
"version": "1.0.0-*",
"buildOptions": {
"emitEntryPoint": true
},
"dependencies": {
"Microsoft.EntityFrameworkCore": "1.1.0",
"Microsoft.EntityFrameworkCore.SqlServer": "1.1.0",
"Microsoft.EntityFrameworkCore.Tools": "1.1.0-preview4-final"
},
"frameworks": {
"net461": {}
},
"tools": {
"Microsoft.EntityFrameworkCore.Tools.DotNet": "1.0.0-preview3-final"
}
}
Exception details
Microsoft.EntityFrameworkCore.DbUpdateException: An error occurred while updating the entries. See the inner exception for details. ---> System.Data.SqlClient.SqlException: Cannot insert explicit value for identity column in table 'Person' when IDENTITY_INSERT is set to OFF.
I modified some code, please review it.
In class DbRepository, added another constructor, to make sure there is the same DbContext in different DbRepository.
public DbRepository(MyContext myContext)
{
_context = myContext;
}
In class Person added 2 properties, to ensure the relation between Person and Place.
public int? PlaceId { get; set; }
public Place Place { get; set; }
In function Seed, modified some code with above modifications.
Firstly, in the part of initialize repository.
// Repo
var myContext = new MyContext();
var statusRepo = new DbRepository<Status>(myContext);
var personRepo = new DbRepository<Person>(myContext);
var addressRepo = new DbRepository<Address>(myContext);
var placeRepo = new DbRepository<Place>(myContext);
This will make all repository use same database connection.
Secondly, due to those changes, the clear process should change the orders, too.
// Delete the non static data
foreach (var address in addressRepo.GetAll()) addressRepo.Delete(address);
addressRepo.SaveChanges();
foreach (var person in personRepo.GetAll()) personRepo.Delete(person);
personRepo.SaveChanges();
foreach (var place in placeRepo.GetAll()) placeRepo.Delete(place);
placeRepo.SaveChanges();
In your Step 1, I extract the address with CatsleBlack, because I guess the one in Person and the other one in Place should be the same.
So, when you initialize a new Person, it will be
var castleBlack = new Address {City = "Castleblack", Status = statusActive};
var personWithAddresses = new Person()
{
Name = "Jon SNOW",
Status = statusActive,
AddressCollection = new List<Address>()
{
castleBlack,
new Address() { City = "Winterfel",
Status = statusArchive }
}
};
Initialize the Place
var placeWithAddress = new Place()
{
Name = "Castleblack",
Status = statusActive
};
placeWithAddress.AddressCollection.Add(castleBlack);
Those are what I have done, can save successfully. The Person record in db also has its PlaceId.

Insert records in many to many not working with Entity Framework

I want to insert 2 new records in their tables and also to create the relation many to many.
There are a lot of questions with this topic, I tried many of the answers and now I have no idea why my code is not working.
Please help me!
This is the code:
class Program
{
static void Main(string[] args)
{
MyContext db = new MyContext();
var st1 = new Student() { Name = "Joe" };
var c1 = new Course() { Name = "Math" };
db.Courses.Attach(c1);
st1.Courses.Add(c1);
db.Students.Add(st1);
db.SaveChanges();
}
}
public class Student
{
public Student()
{
Courses = new HashSet<Course>();
}
public int Id { get; set; }
public string Name { get; set; }
public virtual ICollection<Course> Courses { get; set; }
}
public class Course
{
public int Id { get; set; }
public string Name { get; set; }
public virtual ICollection<Student> Students { get; set; }
}
public class MyContext : DbContext
{
public DbSet<Student> Students { get; set; }
public DbSet<Course> Courses { get; set; }
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
modelBuilder.Entity<Student>()
.HasMany(p => p.Courses)
.WithMany(d => d.Students)
.Map(t =>
{
t.MapLeftKey("studentId");
t.MapRightKey("courseid");
t.ToTable("StudentCourse");
});
base.OnModelCreating(modelBuilder);
}
}
Edit: Like sugested, I initialized the Courses:
public Student()
{
Courses = new HashSet<Course>();
}
and now I get this error on db.SaveChanges();
An error occurred while saving entities that do not expose foreign key properties for their relationships. The EntityEntries property will return null because a single entity cannot be identified as the source of the exception. Handling of exceptions while saving can be made easier by exposing foreign key properties in your entity types. See the InnerException for details.
You're apparently trying to add a new Student to the database and associate it to an existing Course. The problem is that you attach a new Course entity to the context without a proper primary key.
It certainly is a good idea to use a so-called stub entity here, because it saves a roundtrip to the database to fetch an existing Course, but the primary key is required for EF to create a correct association record. It's even the only property you need to set in this Course stub:
var st1 = new Student() { Name = "Joe" };
var c1 = new Course() { CourseId = 123 };
db.Courses.Attach(c1);
st1.Courses.Add(c1);
db.Students.Add(st1);
If you want to add a new course and a new student, you should Add both of them:
db.Courses.Add(c1);
st1.Courses.Add(c1);
db.Students.Add(st1);
Your code isn't initialising the ICollection object in either class.
public class Student
{
private Student()
{
Courses = new List<Course>();
}
public int Id { get; set; }
public string Name { get; set; }
public virtual ICollection<Course> Courses { get; set; }
}
Edit
Try changing your modelBuilder code to the following
modelBuilder.Entity<Student>()
.HasMany<Course>(s => s.Courses)
.WithMany(c => c.Students)
.Map(cs =>
{
cs.MapLeftKey("StudentRefId");
cs.MapRightKey("CourseRefId");
cs.ToTable("StudentCourse");
});
Code sample taken directly from:
http://www.entityframeworktutorial.net/code-first/configure-many-to-many-relationship-in-code-first.aspx

How to avoid N+1 in EF generated queries

I used to generate all the helper tables like UsergroupUsers for many-to-many relations or relational Ids in POCO myself but now I want EF to take care of them. Now I don't think it's such a good idea after all.
Problem
When I try to get all UsergroupDynamicField for particular user it generates N+1 query for every usergroup user is in.
Here I overcommed this problem by simply stating that Usergroups will be IQUeriable instead of IEnumerable. Now I cannot do that because EF won't map it, it has to be ICollection.
Code
public class User
{
...
public virtual ICollection<Usergroup> Usergroups { get; set; }
public IEnumerable<UserField> Fields
{
get
{
var fields = this.Usergroups.SelectMany(x => x.UsergroupDynamicFields); // N + 1 for every Usergroup
foreach (var field in fields)
{
yield return new UserField
{
Name = field.Name
};
}
}
}
}
Database
Here I overcommed this problem by simply stating that Usergroups will be IQUeriable instead of IEnumerable. Now I cannot do that because EF won't map it, it has to be ICollection.
But the class that ends up implementing ICollection is EntityCollection<T>. This collection has a CreateSourceQuery() function that you can use:
var usergroupsQuery = ((EntityCollection<UserGroup>)this.Usergroups).CreateSourceQuery();
var fields = usergroupsQuery.SelectMany(x => x.UsergroupDynamicFields);
Update: as pointed out in the comments, ICollection<T> will only be implemented using EntityCollection<T> when change tracking is possible and enabled (non-sealed classes, and all relevant properties virtual). You can create a query another way:
var usergroupsQuery = db.Entry(this).Collection(u => u.Usergroups).Query();
var fields = usergroupsQuery.SelectMany(x => x.UsergroupDynamicFields);
Note that this requires that you have access to db somehow.
I try with something like
var res = c.Users.Include("Groups.DynFields").First().Groups.SelectMany(x => x.DynFields).ToList();
and it seems to be ok. I use EF5.
Of course... this is not a method in the User class. It requires to be able to invoke Include method on a DbSet object.
I hope this may help.
full solution
public class User {
public Int32 Id { get; set; }
public String Name { get; set; }
public virtual ICollection<UserGroup> Groups { get; set; }
}
public class UserGroup {
public Int32 Id { get; set; }
public String Name { get; set; }
public virtual ICollection<User> Users { get; set; }
public virtual ICollection<UserGroupDynamicField> DynFields { get; set; }
}
public class UserGroupDynamicField {
public Int32 Id { get; set; }
public String Name { get; set; }
public virtual UserGroup Group { get; set; }
}
public class UserGroupDynFEFCFConfiguration : EntityTypeConfiguration<UserGroupDynamicField > {
public UserGroupDynFEFCFConfiguration()
: base() {
HasRequired(x => x.Group);
}
}
public class UserGroupEFCFConfiguration : EntityTypeConfiguration<UserGroup> {
public UserGroupEFCFConfiguration()
: base() {
HasMany(x => x.Users).WithMany(y => y.Groups);
}
}
public class TestEFContext : DbContext {
public IDbSet<User> Users { get; set; }
public IDbSet<UserGroup> Groups { get; set; }
public TestEFContext(String cs)
: base(cs) {
Database.SetInitializer<TestEFContext>(new DropCreateDatabaseAlways<TestEFContext>());
}
protected override void OnModelCreating(DbModelBuilder modelBuilder) {
base.OnModelCreating(modelBuilder);
modelBuilder.Configurations.Add(new UserGroupDynFEFCFConfiguration());
modelBuilder.Configurations.Add(new UserGroupEFCFConfiguration());
}
}
class Program {
static void Main(String[] args) {
String cs = #"Data Source=ALIASTVALK;Initial Catalog=TestEF;Integrated Security=True; MultipleActiveResultSets=True";
using (TestEFContext c = new TestEFContext(cs)) {
UserGroup g1 = new UserGroup {
Name = "G1",
DynFields = new List<UserGroupDynamicField> {
new UserGroupDynamicField { Name = "DF11"},
new UserGroupDynamicField { Name = "DF12"}
}
};
c.Groups.Add(g1);
UserGroup g2 = new UserGroup {
Name = "G2",
DynFields = new List<UserGroupDynamicField> {
new UserGroupDynamicField { Name = "DF21"},
new UserGroupDynamicField { Name = "DF22"}
}
};
c.Groups.Add(g2);
c.Users.Add(new User {
Name = "U1",
Groups = new List<UserGroup> { g1, g2 }
});
c.SaveChanges();
}
using (TestEFContext c = new TestEFContext(cs)) {
var res = c.Users.Include("Groups.DynFields").First().Groups.SelectMany(x => x.DynFields).ToList();
foreach (var v in res) {
Console.WriteLine(v.Name);
}
}
}
}

Categories

Resources