Entity Framework infers relationship - c#

I have some code that is working, but I don't know how. I'm asking this question in hopes that someone can explain how it is working, as any searching I do online gives me information that's not quite specific to what I'm seeing.
I have two entities.
EntityA, EntityB.
EntityA has this:
public partial class EntityA
{
...
public Guid EntityBGuid { get; set; }
public virtual EntityB EntityB { get; set; }
}
EntityB has this:
public partial class EntityB
{
...
public virtual ICollection<EntityA> EntityAs{ get; set; }
}
Nowhere in DatabaseEntities.cs > OnModelCreating() is a relationship defined for either entity.
Nowhere in either table in SQL is there a FK defined for the relationship.
So when I do something like this:
context.LettingProjects.Where(query).Select(s => s.Call).ToList(); //Crash.
But with a .Include, I get data:
context.LettingProjects.Include(i => i.Call).Where(query).Select(s => s.Call).ToList(); //Results.
My question is how does this even work? I don't know how EF handles this in the background, and it seems like a gotcha waiting to happen.

When you are writing this:
context.LettingProjects.Where(query).Select(s => s.Call).ToList();
EF will simply query for a single table.
However, as you probably configured the relationships in your models to contain other Tables too (ICollection<EntityA>), they will contain a null value because EF will not send a query asking for their data.
My question is, how does this even work? I don't know how EF handles this in the background, and it seems like a gotcha waiting to happen.
The answer to your question now should be simple:
when you write .Include(s => s.OtherTable) you are asking EF to join your current table data with the one you specified.
EF will use the Foreign Key relationships and will give you back a collection of items with the data for the related tables, too (not null version). That's why it will not fail.
This is very similar to using the LINQ to Entities Join method, but in this case, it's requiring you to write much less code unless you want to control the join operation by yourself.

Related

Direct access to EF junction table

I would need direct access to this table. Yes, I know there are questions like this on SO and they all end up showing people not yet accustomed to EF navigation why they don't need this. But I happen to know how to use EF normally and I still need this, because I need to backup and restore an EF-driven database to and from an external format.
I can get all entries in the junction table like this:
from foo in Foo
from bar in foo.Bar
select new FooBar(foo.id, bar.id);
where FooBar is an appropriate helper class storing the two keys making up the relationship. I can export this list to a format of my choice easily. However, during restore, I would need to populate an emptied Foo_Bar junction table with these original entries coming from the backup (tracking and foreign key constraints are disabled during the restore operation, the incoming data is known to be good, input validation isn't important here). Because the database is not in a consistent state in the midst of a restore operation, using the traditional way of creating and attaching all entities is not possible.
One idea I found somewhere, but unfortunately, without any details of how to do it, was to create a secondary context consisting only of the junction tables and populating that directly. That would be a very efficient and nice solution to my problem but how can that be done?
Update:
By the suggestions, I tried to add this to the model:
public virtual DbSet<Foo_Bar> Foo_Bar { get; set; }
[Table("Foo_Bar")]
public class Foo_Bar {
[Key, Column(Order = 1)]
public long foo_id { get; set; }
[Key, Column(Order = 2)]
public long bar_id { get; set; }
}
Specifying the many-to-many relationship as:
modelBuilder.Entity<Bar>()
.HasMany(x => x.Foo)
.WithMany(x => x.Bar)
.Map(config => {
config.MapLeftKey("foo_id");
config.MapRightKey("bar_id");
config.ToTable("Foo_Bar");
});
This results in an error message of: EntitySet 'FooBar' with schema 'dbo' and table 'Foo_Bar' was already defined. Each EntitySet must refer to a unique schema and table. Actually, it wasn't, at least not intentionally. I have no FooBar in the model, just Foo, Bar and the junction table Foo_Bar I want to define now. It's probably something automatic but I can't see what causes it. My modelBuilder call definitely maps it to the underscored name.
OK, I don't yet know how to solve it within the model as Gert suggested (without getting error messages) but a secondary context solved it all right:
public class JunctionModel : DbContext {
public virtual DbSet<Foo_Bar> Foo_Bar { get; set; }
public JunctionModel()
: base("name=DatabaseConnection") {
}
}
This and my own experimentation seem to suggest that there is no other, more direct way without radically changing the model which might not be easily possible in some cases.

allowing a user to define a table at runtime using entity framework

is it possible to dynamically build entities in entity framework 6 using code first?
is it also possible to add properties to those entities at runtime?
Can those entities reference each other using foreign keys?
If someone could point me in the right direction if this is possible, i would really appreicate it.
EDIT
i have changed the title to reflect more of what i would like, so u can see why i asked the questions i asked.
Basically i would like to create a system where a user can define the objects(tables) they want, with the properties on that object (columns in table) as well as the references to each object (relationships between tables).
Now each object would have an Id property/column. i was thinking of atoring these definitions in an eav like table structure but i have other ibjects that are defined at design time and i like to use linq queries on these objects. I would also like to be able to build linq queries on these objects as users will want to report on this data. Building the linq queries should be fine as could use dynamic linq for this (Maybe)?
Currently to create such a system i have created a table that has many text fields, many number fields, many relationship fields and users can use which ones they want. But this is just 1 table and i think this is going to bite me in the bottom in the end, thus why i would like to take it to the next level and build separate tables for each object.
if anyone knows of a better way or maybe experienced something similar, glad to hear opinions.
Ive been interested in this topic since ef4.
Have a real world solution that could use it.
I dont...
Rowan Miller is one of the EF developers. He wrote a blog on this topic.
Dynamic Model creation Blog Explaining how you might do it.
A github example buybackoff/Ractor.CLR see OnCreating Showing how.
It would appear ok, but has a many practical restrictions that make
a recompile with generated code or hand code more practical.
It is also why i have not down voted others. :-)
Would it be fun watching someone dig their own grave with a teaspoon?
Consider the runtime implications.
These approach still rely on the POCO type discovery.
the an assembly can be generated from code on the fly.
And then there is SO POCO and runtime
During context creation the initializer runs. You adjust the model.
The auto migrate then adds the news properties and tables.
So during that period of time NO other contexts can be instantiated.
Only non EF access possible during the automatic migration.
So you still have a logical outage.
You are now using previously unknown pocos, in unknown tables.
What repository pattern are you using...
eg I used this type of pattern....
private IRepositoryBase<TPoco> InstantiateRepository(BaseDbContext context, Type repType, params Type[] args) {
Type repGenericType = repType.MakeGenericType(args);
object repInstance = Activator.CreateInstance(repGenericType, context);
return (IRepositoryBase<TPoco>)repInstance;
}
and cast this against IRepository after making a dynamic call to the factory.
But i was unable to avoid dynamic calls. Sticky situation.
good luck....
Edit / After thought
I read a blog about ef 7
There are some very interesting comments from Rowan about potentially not needing CLR types. That makes the dynamic game a bunch easier.
You could try the ef7 beta if brave.
You can create an EF model dynamically using reflection:
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
var entityMethod = typeof(DbModelBuilder).GetMethod("Entity");
foreach (Type type in ...)
{
entityMethod.MakeGenericMethod(type)
.Invoke(modelBuilder, new object[] { });
}
base.OnModelCreating(modelBuilder);
}
it possible to dynamically build entities
The short answer is no, one cannot dynamically build new or existing entities once EF has defined them during design time.
is it also possible to add properties to those entities at runtime
But that does not mean one has to live with those entities as they are... To achieve a dynamism one can extend the entities via Partial class to the entity. Then one can have any number of new properties/methods which could achieve what the runtime aspect which possibly you are looking for past a generated entity.
Can those entities reference each other using foreign keys?
Not really, but it is not clear what you mean.
If the entity was generated in design time and during runtime a new FK constraint was added to the database, then an entity could be saved if it does not know about the FK, but if the FK requires a value then the process of saving would fail. Extraction from the database would not fail.
Q: is it possible to dynamically build entities in
entity framework 6 using code first?
A: No
Q: is it also possible to add properties to those entities at runtime?
A:No
Q: Can those entities reference each other using foreign keys?
A: Unless you've defined the entity beforehand, no.
Maybe you've confused things with what's called CodeFirst, where you write your domain / business models in C#, define their relationships with other entities, and then generate a database based on your C# models...
Here's an overly simplistic example that you can get started with if that's what you're trying to achieve.
First make sure you have EntitiyFramework installed... you can get it from NuGet...
pm> Install-Package EntityFramework
Then copy the code below to your project
public class User
{
public User()
{
this.Id = Guid.NewGuid().ToString();
}
public string Id { get; set; }
public string Name { get; set; }
public virtual ICollection<Post> Posts {get;set;}
}
public class Post
{
public Post()
{
this.Id = Guid.NewGuid().ToString();
}
public string Id { get; set; }
public string UserId { get; set; }
public virtual User Author {get;set;}
public string Title { get; set; }
public string Body { get; set; }
}
public class UserConfiguration : EntityTypeConfiguration<User>
{
public UserConfiguration()
{
this.ToTable("Users");
this.HasKey(user => user.Id);
this.Property(user => user.Id).IsRequired().HasDatabaseGeneratedOption(DatabaseGeneratedOption.None);
this.Property(user => user.Name).IsRequired();
this.HasMany(user => user.Posts).WithRequired(post => post.Author).HasForeignKey(post => post.UserId);
}
}
public class PostConfiguration : EntityTypeConfiguration<Post>
{
public PostConfiguration()
{
this.ToTable("Posts");
this.HasKey(post => post.Id);
this.Property(post => post.Id).IsRequired().HasDatabaseGeneratedOption(DatabaseGeneratedOption.None);
this.Property(post => post.UserId).IsRequired();
this.Property(post => post.Title).IsRequired();
this.Property(post => post.Body).IsRequired();
this.HasRequired(post => post.Author).WithMany(user => user.Posts).HasForeignKey(post => post.UserId);
}
}
public class ExampleContext : DbContext
{
public ExampleContext() : base("DefaultConnection")
// Ensure you have a connection string in App.config / Web.Config
// named DefaultConnection with a connection string
{
}
public DbSet<Post> Posts { get; set; }
public DbSet<User> Users { get; set; }
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
modelBuilder.Configurations.Add(new PostConfiguration());
modelBuilder.Configurations.Add(new UserConfiguration());
}
}
Once you've done that... Open the package manager console and type in the following commands...
pm> Enable-Migrations
pm> Add-Migration InitialMigration
pm> Update-Database
And you should then have your database generated for you from that

Issue with many-to-many relationship + TPH inhertitance in Entity Framework 6

I am running into an issue with EF6, though I'm fairly sure that this applies to previous versions that support this type of mapping. I fear I know the answer to the question at hand, but I hope that I am doing something wrong, or there is a better workaround than what I present here. All classes are gutted for clarity.
So I have
public abstract class SoftwareFirmware
{
public long Id { get; private set; }
public ICollection<DeviceType> DeviceTypes { get; private set; }
public SoftwareFirmware()
{
DeviceTypes=new HashSet<DeviceType>();
}
}
and
public class DeviceType
{
public long Id { get; set; }
public virtual ICollection<Firmware> AvailableFirmwareVerions { get; private set; }
public virtual ICollection<Software> AvailableSoftwareVerions { get; private set; }
public DeviceType()
{
AvailableFirmwareVerions = new HashSet<Firmware>();
AvailableSoftwareVerions = new HashSet<Software>();
}
}
which as you can see have a many to many relationship defined. I've defined two classes which derive from SoftwareFirmware, the aptly named
public class Firmware : SoftwareFirmware {}
and
public class Software : SoftwareFirmware {}
I'm using Table Per Hierarchy inheritance, so Software and Firmware are stored in the same table with a discriminator column. Finally, I've mapped the relationships in the derived DbContext's OnModelCreating method with
modelBuilder.Entity<DeviceType>().HasMany(d => d.AvailableFirmwareVerions).WithMany(firmware=>firmware.DeviceTypes);
modelBuilder.Entity<DeviceType>().HasMany(d => d.AvailableSoftwareVerions).WithMany(sofware=>sofware.DeviceTypes);
The problem at hand is that Entity Framework does not seem to support inheritance with this mapping, as I receive the following when EF tries to generate the database:
DeviceTypes: FromRole: NavigationProperty 'DeviceTypes' is not valid.
Type 'Software' of FromRole
'DeviceType_AvailableSoftwareVerions_Target' in AssociationType
'DeviceType_AvailableSoftwareVerions' must exactly match with the type
'SoftwareFirmware' on which this NavigationProperty is declared on.
From this I gather that a type that inherits from SoftwareFirmware is not good enough for the NavigationProperty, it must be a SoftwareFirmware type. If I tear the DeviceType collection out of the SoftwareFirmware base class and duplicate it in each of the derived classes, things work, but that's certainly less than ideal.
So finally, my question is- is there another way to configure this so that I can keep my navigation property in my base class? If not, is there a cleaner workaround than what I've described?
UPDATE: so it would seem that SQL Server Management Studio did me wrong, as I had diagrammed out the database previously without the overloaded version of WithMany that takes an expression and it did not include the junction tables. It seems like SSMS doesn't play nice with schema changes in terms adding new diagramming even when the database has been dropped and recreated- it must be restarted. Major pain, but I digress...
As a last ditch effort, I reverted to the parameterless version of WithMany for the mappings, deleted and recreated the database by restarting the application, restarted SSMS, and lo! The junction tables were created. All I needed to do is add an Ignore for the base SoftwareFirmware class's DeviceTypes property and everything generated cleanly. So my FluentAPI mapping code looks like this:
modelBuilder.Entity<DeviceType>().HasMany(d => d.AvailableFirmwareVerions).WithMany();
modelBuilder.Entity<DeviceType>().HasMany(d => d.AvailableSoftwareVerions).WithMany();
modelBuilder.Entity<SoftwareFirmware>().Ignore(s => s.DeviceTypes);
which generates this schema- pretty much exactly the schema I wanted (ignore the extra properties):
However, since the parameterless call to WithMany only hooks up a navigation property on one side, updates to Software.DeviceTypes and Firmware.DeviceTypes aren't tracked by EF so I'm back where I started.
The issue is that you have a single SoftwareFirmware.DeviceTypes property but you are then trying to use it as part of two separate relationships. SoftwareFirmware.DeviceTypes can't be the inverse of both DeviceType.AvailableFirmwareVerions and DeviceType.AvailableSoftwareVerions.
What you're trying to model is a bit strange because you're kind of treating them as distinct relationships, but also not. There are two options here...
Option 1: It's two separate relationships
Remove SoftwareFirmware.DeviceTypes and add a DeviceTypes property on Firmware and Software.
This is actually what you are doing when you put Ignore on the SoftwareFirmware.DeviceTypes property and use the empty overload of WithMany - which is why it works. You're telling EF that there are two relationships (one Software -> DeviceType and the other Firmware -> DeviceType) and that there is no navigation property that points back the other way. Since you ignored SoftwareFirmware.DeviceTypes it's just not part of your model.
Option 2: It's one relationship
Remove the two navigation properties on DeviceType and replace them with a single navigation to the SoftwareFirmware base class. You can always add some façade properties that filter the contents to Software and Firmware (as shown below)
public class DeviceType
{
public long Id { get; set; }
public virtual ICollection<SoftwareFirmware> AvailableVerions { get; private set; }
public virtual IEnumerable<Firmware> AvailableFirmwareVerions
{
get
{
return this.AvailableVerions.OfType<Firmware>();
}
}
public virtual IEnumerable<Software> AvailableSoftwareVerions
{
get
{
return this.AvailableVerions.OfType<Software>();
}
}
public DeviceType()
{
AvailableVerions = new HashSet<SoftwareFirmware>();
}
}
This problem sounds familiar. (checking my email ...yep it was over a year ago!) I had someone send me a sample where a fluent api relationship was failing. They did not have a many to many but I think it's the same problem. I spent a long time looking at it and asked Rowan Miller (on the team) and he said that the fluent api can't comprehend the property coming from the base type.
i.e. the fluent API can't see the DEVICETYPE property when it's looking at AvailableSoftwareVerions or at AvailableFirmwareVersions. (I can't tell you WHY this is. You'd think it could find it via reflection but maybe it just wasn't designed with this scenario in mind.)
This still didn't make sense to me so he explained further (and I'll update his explanation with your types which was a little confusing since you have extra levels of inheritance and things are named a bit inconsistently ...but I
Conceptually the classes don’t really make sense, because a DeviceType
can have many Software(s) or Firmware(s)… but the inverse navigation property is
defined on SoftwareFirmware. So what happens when something that isn’t
a Firmware or Software has a DeviceType? It’s inverse is configured as > DeviceType.AvailableSoftwareVersions
but that can’t work. Even taking EF out of the picture the correct way
to model that is to have the Project property be on Report.
That was with EF5. If my memory is correct and it's the same problem, then maybe it hasn't changed for EF6. Perhaps we should look to see if there's an issue in there for solving this problem. However, his further explanation suggests that it's not a bug but a protection.
(I'm going to ping him to verify that I'm inferring the previous problem to this one correctly).
In that email, Rowan also suggested using getter logic instead of a navigation properties as a workaround.

Entity Framework deletes valid content without permission in a non-deterministic way?

I'm having troubles with storing data to the database with EF. Usually all CRUD operations works fine for the rest of the program, but recently I have noticed, that in case of m:n relationship, the tricky part comes.
I'm using EF4.1 with code first approach. The interesting parts of my classes look as follows:
public class Publication : IItem, IDataErrorInfo {
...
[InverseProperty("Publications")]
public virtual ICollection<Group> Groups{ get; set; }
}
public class Group : IItem, IDataErrorInfo {
...
[InverseProperty("Groups")]
public virtual ICollection<Publication> Groups{ get; set; }
}
The database is created as follows:
public PublicationsDB() : base("PublicationDB") {
this.Configuration.AutoDetectChangesEnabled = true;
}
public DbSet<Publication> Publications { get; set; }
public DbSet<Software> Softwares { get; set; }
public DbSet<Group> Group{ get; set; }
The intention is to create m:n relationship between Publications and Groups.
When lately I'm importing data from XML, everything works great. Either Publications and Groups have their ICollection stored after the SaveChanges() is called. In the same method a few lines below I'm getting data from database (just for check) and again both entities have their ICollection filled.
And here comes the trouble:
When executing a different function for data manipulation, I dig data out of the database and:
The collection of Publications is fine, ale of the entities have proper ICollection<Group> filled from xml
But the collection of Groups is quite messed up. Most of them have ICollection<Publication> set to null.
What the problem might be? Such behavior is really weird to me. Last but not least the deletion of Collections from DBSet<Groups> is not deterministic. I mean if we considere Group A and Group B, than in one run of the program A.ICollection = null and B.ICollection.Count =1 (which is by the way wrong) whereas in other run is different, i.e. A.ICollection = null and B.ICollection = null (which is also wrong)
Any ideas?
Messed model when building code first DB? framework error or weird internal framework optimization? or am I just an arsehole? :)
I was also thinking of changing the relationship by placing a new entity inside.
i.e to shift Publication (m):(n) Group to something like Publication (m):(1) NewEntityRelation (n):1 Group, but I would rather prefer a different solution if there is such.
Thank you for your replies.
Unless I'm mistaken, the InverseProperty attribute helps EF pair up one side of a relationship with the other side, but it doesn't tell EF anything about what kind of relationship it is. I'm assuming that when you say you want to create an "m:n" relationship what you mean is "many to many", and I've found EF doesn't always figure it out correctly. I've made it a habit to specify the mapping manually for many-to-many relations by overriding the OnModelCreating method. The following is a sample code snippet:
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
modelBuilder.Entity<User>()
.HasMany(a => a.Roles)
.WithMany(r => r.Users)
.Map(config =>
{
config.ToTable("UserRoles");
config.MapLeftKey("UserId");
config.MapRightKey("RoleId");
});
}
The above snippet is taken from a project of mine where it's used to define a many-to-many relation between users and their roles in the system. This way EF doesn't get "confused" about what kind of relationship, the name of the intermediary table in the database, or the keys used to map the one side to the other. I'm not sure why you're seeing the problems on only one side of the relation, but I'd be willing to bet it's because EF hasn't been able to quite figure out what you're trying to do.

Entity Framework: Where the heck is it getting these columns from?

We are trying to get Entity framework working at our shop with an existing database (and therefore, changing the database schema is NOT an option), and the unit tests we created to test things are showing some really strange behavior.
This is the SQL it spits out for a specific object we have:
SELECT
[Extent1].[CommentTypeId] AS [CommentTypeId],
[Extent1].[DataPartId] AS [DataPartId],
[Extent1].[CommentId] AS [CommentId],
[Extent1].[CreatedTime] AS [CreatedTime],
[Extent1].[Message] AS [Message],
[Extent1].[From] AS [From],
[Extent1].[Likes] AS [Likes],
[Extent1].[SourceTypeId] AS [SourceTypeId],
[Extent1].[StatusMessage_DataPartId] AS [StatusMessage_DataPartId],
[Extent1].[Album_DataPartId] AS [Album_DataPartId]
FROM [dbo].[Comments] AS [Extent1]
The last two columns requested, as you might notice, are not like the others. That's because they don't actually exist, and we have no idea why Entity is requesting them! Neither our configuration files nor our POCOs make any mention of them at all. In fact, as far as our database goes, they're completely separate concepts and aren't directly related at all.
Where is it getting these columns from, and how do I tell it to cut it out?
EDIT: To respond to some of the questions below,
1) We are using Entity Framework 4.2. We are using fluent mapping.
2) The POCO itself looks like this, with the equality mess cut out for the sake of brevity:
public long DataPartId { get; set; }
public string CommentId { get; set; }
public DateTime? CreatedTime { get; set; }
public string Message { get; set; }
public string From { get; set; }
public int? Likes { get; set; }
public string SourceTypeId { get; set; }
public int CommentTypeId { get; set; }
public virtual DataPart DataPart { get; set; }
public virtual CommentType CommentType { get; set; }
3) We are not using edmx. We have a custom DbContext. There are not too many lines that are terribly interesting. These two are probably of interest:
Configuration.LazyLoadingEnabled = true;
Configuration.ProxyCreationEnabled = true;
Beyond that, the Context file is a lot of
modelBuilder.Configurations.Add(new WhateverConfiguration())
and
public IDbSet<WhateverPoco> PocoDatabaseTableAccessor { get; set; }
4) We started with db-first, but that didn't work, so we're currently doing code-first.
5) This is the guts of the config for that specific POCO:
HasRequired (x => x.DataPart)
.WithRequiredDependent (x => x.Comment);
HasRequired (x => x.CommentType)
.WithMany (x => x.Comments)
.HasForeignKey (x => x.CommentTypeId);
HasKey (x => x.DataPartId);
ToTable ("Comments", "dbo");
The problem is not in the mapping or class you showed. Check your Album and StatusMessage classes. Are they entities? Are they mapped? Do they have collection navigation properties to comments? If yes EF expects that Comment must have FK to these tables. If the table doesn't have such column you cannot have these navigation properties mapped in those entities.
Btw. Shouldn't the id in Comments table be CommentId instead of DataPartId?
Entity Framework, like MVC, uses a lot of convention over configuration. That means it assumes certain things unless you tell it not to.
However, something is really strange here based on the information you supplied. According to the SQL query, this is coming from the Comments table, however your fluent mapping says that DataPartId is the primary key. Do you have additional primary key fluent mappings? If not, your mappings may be wrong. Have you checked the actual database generated to see if the data model matches what you are trying to do?
My guess is that your StatusMessage and Album classes have navigational properties to Comment, but since you have only defined DataPartId as your primary key, that is the value it is using to look up the comments, not CommentId.
Open the .edmx in a XML-Editor and search for these columns. They must be somewhere in your model.
EDIT: your original question didn't mention that you are using code first. I wonder what your trouble was with Database first, that usually works fine well. With code first or model first, you normally create the database after creating the model (using generated SQL scripts).
You declared the last two properties as virtual, that's why the generated SQL looks different. From the code you are showing us we cannot see where the reference to Album comes from.
Because you have the database, I would generate the .edmx from the model in one project. Then you can use a POCO code generator or a Self-tracking entity generator to generate the entities and store them in a different project. Or you can write them manually as you already have. The property names must correspond with the columns in the database.

Categories