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.
Related
I have a SQL Server based ASP.NET MVC 5 app, and I'm using Entity Framework 6 to talk to the database.
We're using a "hybrid" approach - we manage all the database structure with classic SQL scripts which we deploy onto our DB server, and then we generate the "code-first" classes from that SQL Server database. This works reasonably well, for the most part.
One thing that bugs me is the if a given table has multiple FK link to another table, the naming convention used by the EF6 code generation is pretty lame....
Assume I have a table (and therefore entity) Site which represents a site somewhere, and this site has three links to a Contact table for various roles - the "main" contact, the "support" contact, and a "sales" contact. So my table in SQL Server looks something like this:
CREATE TABLE dbo.Site
(
SiteID INT NOT NULL
CONSTRAINT PK_Site PRIMARY KEY CLUSTERED,
.... some other properties, of no interest or relevance here .....
MainContactId INT NOT NULL
CONSTRAINT FK_Site_MainContact FOREIGN KEY REFERENCES dbo.Contact(ContactId),
SalesContactId INT NOT NULL
CONSTRAINT FK_Site_SalesContact FOREIGN KEY REFERENCES dbo.Contact(ContactId),
SupportContactId INT NOT NULL
CONSTRAINT FK_Site_SupportContact FOREIGN KEY REFERENCES dbo.Contact(ContactId)
)
I had been hoping that the EF6 code-first from existing database generation would be smart enough to read those column names and come up with meaningful names for the navigation properties on the entity - but alas, this is what I get instead:
[Table("Site")]
public partial class Site
{
[DatabaseGenerated(DatabaseGeneratedOption.None)]
public int SiteID { get; set; }
public int MainContactId { get; set; }
public int SalesContactId { get; set; }
public int SupportContactId { get; set; }
public virtual Contact Contact { get; set; }
public virtual Contact Contact1 { get; set; }
public virtual Contact Contact2 { get; set; }
}
While the actual FK columns are OK - the "deduced" navigation properties are horrible - Contact, Contact1 and Contact2 - seriously, is this the best naming??? I think not... I would much prefer to called them "MainContact", "SalesContact", "SupportContact" - wouldn't that make a lot more sense? And be clearer for later use?
I installed the custom T4 templates (Nuget package "EntityFramework.CodeTemplates.CSharp"), and I see that there are a few interesting and potentially very useful helper classes being used (CSharpCodeHelper, EdmHelper, EntitySet, DbModel from the Microsoft.Data.Entity.Design and System.Data.Entity.Infrastructure namespaces) - unfortunately, most of them are sparsely documented, and also often their constructors are internal - so I cannot really build my own tool based on those ready-made classes.
Any other approach? I'd really like to teach the code generation a few smarts - this right now is just not up to usual standards and requires me to make a lot of manual changes to generated files - a labor in vain, flushed down the digital toilet each time I need to re-generate the classes from the database.....
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.
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
I used EF6 Database First tools to generate C# classes for 2 tables from my database, then (as advised in the blog post that helped me through the steps to do that) copied the resulting .cs files into a new project. I made a few edits to the classes to support sensible names in my C# code. Here's a snippet of one of the classes with "LongTableName" replacing a strangely long name used in the database.
namespace RidesData
{
[Table("LongTableName")]
public partial class PhoneData
{
[Key]
[Column("LongTableNameID")]
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public int ID { get; set; }
[Column("LongTableNameAccountID")]
public int AccountID { get; set; }
// more fields
}
}
I am not in control of the table names, nor the fact that the many of the column names have the table name as prefixes. But the Code First ideas in EF6 should, I thought, let me use reasonable class and field names despite that. (The Database First code generator did a good job of adding code to OnModelCreating to specify that none of the columns corresponding to C# string data used Unicode.)
My model (generated by the EF6 tools and that inherits from DbContext) includes (after some renaming by me)
public virtual DbSet<PhoneData> PhoneRecs { get; set; }
and I thought all would be fine when I created an instance of PhoneData, populated it, and did
Model.PhoneRecs.Add(phoneData);
but the first thing that happened when I ran the code -- well before any call to SaveChanges() -- was that EF generated CREATE TABLE statements for the two tables; the table corresponding to the snippet above was named PhoneDatas (not using the specified table name) and the column names were the same as the field names in the class (not what was specified in the Column(...) attributes).
Of course the table I had specified did not need to be created. EF just had to grok that I wanted to use the table and column names I had specified via attributes.
I did not expect this failure of explicit Code First attributes. Does anyone have a clue why this isn't doing what I want, or how to fix it? (Do I have to do something to specify the table & column names in OnModelCreating as well as -- or instead of -- the attributes?)
Note that the project that I copied these classes into had never "seen" the database before. There are no vestiges of any "models" left over from tooling having looked at the database. Also, I hope it does not matter that I've tried to keep things on .Net 4.0 (avoiding going to 4.5 in this code).
Any assistance would be appreciated.
I'm not a big fan of DataAnotations either. Use EntityTypeConfiguration. It gives you the naming flexibility I think you are looking for.
Example.
public class PhoneData
{
public int ID {get;set;}
public string SomeProperty {get;set;}
}
public class PhoneDataMap : EntityTypeConfiguration<PhoneData>
{
public PhoneDataMap()
{
ToTable("WhatEverYou_Want_to_call_this");
HasKey(m => m.Id);
Property(m => m.SomeProperty).HasColumnName("whatever").IsRequired();
//etc.
}
}
Then in your on ModelCreating you add
modelBuilder.Configuration.Add(new PhoneDataMap());
On a side note, if you are having trouble with pluralization of your table names you can add this to OnModelCreating as well
modelBuilder.Conventions.Remove<PluralizingTableNameConvention>();
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.