EF6 ignoring [Table] and [Column] in entity classes - c#

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>();

Related

Entity Framework's "Index" decoration is not creating an Index

Here is the super simple class I'm trying to create.
public class Company
{
public int ID { get; set; }
[Column(TypeName = "VARCHAR(254)")]
[Index]
public string Name { get; set; }
[Index]
public int stupidField { get; set; }
}
My goal was to force Name to be unique, so I added the decoration [Index(IsUnique = true)]. But no unique index was created, so I figured I'll first try to solve the simpler problem of creating any index. Because I read here that indices cannot be created for columns of type varchar(max), I limited the length of the Name field. Still no luck. I even tried a few different syntaxes for limiting the length of the field, but still no index.
To see if something other than string length was at play, I created the integer field stupidField, but I can't index that field either. So now I'm completely out of ideas as to what could be wrong. Please help me!
Check out this screenshot from MS SQL Server Management Studio that shows that my fields are being created but not the indices.
Note: I'm certain migrations are not the issue.
Some of the people I've read about on SO were updating their classes, but those changes were not reflected in the database because of problems with their migrations. That is not relevant here. I delete the database and recreate it every time I make a change. (I even make silly changes like renaming my fields, just to make sure that I can still affect the database.)
Turns out I'm actually using Entity Framework Core, not Entity Framework. In Entity Framework Core, indices cannot be created using attributes, although they can be created using fluent API. See Microsoft's documentation.

Entity Framework Invalid Column

I've looked at lots of other questions on SO but can't get the answer. I have a column in a table called: Sex - Male
I would like to get my hands on whoever named it as it's giving me problems with EF. If I use this:
[Column("Sex - Male")]
public bool Sex { get;set; }
This gives me the error of being incompatible with the model as the field "Sex" could not be found. So I changed to this:
[Column("[Sex - Male]")]
public bool Sex { get;set; }
I then get the message Invalid Column Name [Sex - Male]. Does EF rename columns with spaces in some way as the field does exist and is not any kind of FK?
EDIT
I have found that doing this in the modelBuilder:
modelBuilder.Entity<Student>().Property(x => x.Sex).HasColumnName("Sex - Male");
Causes the same error to appear saying it's incompatible as there is no column called Sex with the same name! I've noticed it occurs on anything I use the Column data annotation for not just this field!
EDIT 2
I created a new application and used a Model Designer to see how it interpreted the column and showed it in the designer as "Sex___Male", however, changing the class to this even with []'s around it still gives me could not find column Sex___Male??
EDIT 3
It appears the error isn't quite what I thought, I found the mapping config works fine when I just use db.Students; and the column is there as expected.
It turns out the area going wrong is this line:
var students = (db as IObjectContextAdapter).ObjectContext.ExecuteStoreQuery<Student>(sql);
So it's clearly the ExecuteStoreQuery that I'm guessing won't use the same mapping configuration therefore sees the column as missing. Not sure why putting the Column annotation on the property in the class doesn't work though??
I have recreated your situation in a test configuration. I was able to succesfully insert and query data using the following configuration
SQL Server 2012
Visual Studio 2013
Entity Framework 6.0.1
If you are using an older version of Entity Framework I would consider updating; that's most likely the cause, however I'm not able to reproduce your environment so this answer is only a guess. I used this code:
Created a table:
create table MyTable2 (
[pk] int not null identity primary key,
[Sex - Male] bit not null);
Class:
public class MyTable2
{
public int pk { get; set; }
public bool Sex { get; set; }
}
Mapping configuration:
this.HasKey(t => t.pk);
this.Property(t => t.Sex).HasColumnName("Sex - Male");
It appears that Entity Framework itself had no issue mapping this column regarding it's normal use however the issue I had is where I was using the ExecuteStoreQuery method to map the model.
It turns out using this means anything you map it to has to have the same names regardless of any data annotations you add on for column (they appear to just get ignored). What I did instead was make a small class with just the fields I needed and changed the sql of the query to Select StudentID As ID, [Sex - Male] As Sex, ...other fields FROM ...etc i.e.
public class StudentReadOnly
{
public int ID { get; set; }
public bool Sex { get;set; }
... other properties
}
And then changed line to:
var students = (db as IObjectContextAdapter).ObjectContext.ExecuteStoreQuery<StudentReadOnly>(sql);
And had no problems. I also found that any properties you put in the class MUST exist in the sql query unlike a usual ef query.

SQLBulk Copy With Related Entities (Entity Framework)

Say I have the below entities. (Heavily Simplified for brevity, but the key properties are included)
public class Crime
{
[Key]
public int CrimeId {get;set;}
public virtual ICollection<Victim> Victims {get;set;}
public virtual CrimeDetail Detail {get;set}
}
public class Victim
{
[Key]
public int VictimId {get;set;}
public string VictimCategory {get;set;}
}
public class CrimeDetail
{
[Key]
public int id {get;set;}
public string DetailText {get;set;}
}
I have 600,000+ of these records to insert into SQL Server 2008 Express R2, which takes quite some time using Entity Framework 4.4.
Ideally I'd like to use SQLBulkCopy to batch insert these records, but since that doesn't support complex types (At least not out of the box), then I'm trying to find some other potential solutions.
I'm open to changing my model if necessary.
EDIT: would the AsDataReader Extension method from MSDN help in this instance?
When having the same issue we end up having code-first generated database with EF and strongly typed generated datasets to be used for SQLBulkCopy.
(We never really coded those classes, they were generated using xsd util from xsd definition of 1-10gb xml file. I'm trying to recall right now when we havent generated typed datasets from the same xsd, but that seems irrelevant to your issue.)
Depending on how you are getting those 600k+ records you either can change the code to use generated strongly-typed datasets or use some object-to-object mapper to map your EF POCO objects to datasets as properties going to be named the same.
Here is a link on generating strongly typed datasets.
Here is an example how to use SqlBulkInsert.

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