EF Foreign Key setup - c#

I have a question regarding the setup of foreign keys in entity framework 6. Our project stores data from a few other services (to have faster access to the data) and provides the users with charts and statistics depending on the stored data. For the storage of the data we´ve setup a cronjob which runs daily at about 3 AM.
Here are 2 example database models:
public class Project {
public string Id { get; set; }
public string Title { get; set; }
}
public class Issue {
public string Id { get; set; }
public string Title { get; set; }
[ForeignKey("Project")]
public string ProjectId { get; set; }
[ForeignKey("ProjectId")]
public Project Project { get; set; }
}
The problem now is for some issues we don´t save the project it depends on but we have to save the ProjectId (because at a later point it might be possible that the project exists in our database). So when I try to save this issues it tells me that I can´t save them because the project does not exist.
Is there any way I can tell entity framework that it doesn´t matter if the project exists or not? Currently I´ve just removed the ForeignKeys but this makes it very slow when I try to get the full list of issues with their projects.
Or is there any other way to read out all issues with their projects if there are no foreign keys? Currently I´m using a foreach loop to go threw each issue and then I search for the project but with more than 10.000 issues this get´s very slow.

The navigation property you've defined is requiring the data in the Project table in order to save an Issue. This isn't Entity Framework, this is a SQL Server foreign key constraint issue. Entity Framework is doing preliminary validation to not waste a connection that will ultimately fail. While you can turn off enforcing the foreign key constraint, there is not a good way to turn this validation off in Entity Framework
Keep in mind, having a foreign key does not mean it will help with your query's performance. It's simply a way to enforce referential integrity. I suspect that your real problem is the way you've written your query. Without seeing your query and metrics around "slow", it be hard to point you in the right direction.

Related

Entity Framework Core insert parent entity and update its nested child entities

Here's my situation, I need to implement a sync system where data are created in client, and they are sent in JSON format to server then saved to a temporary place(NoSql). There is another process(written in c# dotnet core) which read the temporary records and insert them into MSSQL.
Let me show a simplified version of my entities
class Company
{
public string Name { get; set; }
public HashSet<Worker> Workers { get; set; }
}
class Worker
{
public string LastName { get; set; }
public Company Company { get; set; }
}
When I try to insert a Worker entity into MSSQL, I do not know if my Company entity has already synced.
Two possible situations:
Company exists. Insert this Worker object causes a Duplicate key exception
Company does not exist. Insert can be done but the subsequent insert operation becomes first situation.
How can I solve this if I do not manually extract out the Company entity and check its existence before insertion.
My goal is
to insert a worker with a company foreign key, if the foreign key exists, update(or ignore) it.
achieve this without manually handle the foreign key since there are dozens of entities, doing so will make the code unmanageable...
Hope I've provided information needed, if not do let me know and I will update my question. Thanks!

Customizing EF6 "code-first from database" code generation to get more meaningful navigation property names

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.....

MVC 4 and EF6 database first: issue with mapping

I am following along Pro ASP.NET MVC 4 by Adam Freeman on VS 2010 (I downloaded the MVC 4 template online). I have worked with the .edmx file before, but in Chapter 7 he does not do this. I setup a basic connection string with SQL Server in my web.config file within my WebUI project where my controllers and views are located. Also, I listed my Domain classes within my Domain project below. The problem comes when I run the application. The application is not recognizing my table in my database (dbo.Request) and instead is creating a table based on my class name in the Entities namespace (so it creates a CustRequest table) and it also creates a _Migration_History table. To prevent this I add the Data Annotation above my class [Table("MyTableName")]. I could not figure out why I had to add this Data Annotation. Also, EF made me add a [Key] above my primary key, which i can understand because i do not have an ID property, but in the book he did not do this. I was wondering if I was missing something obvious as I am pretty new to MVC. Any help would be appreciated. I am working with EF 6. Thank you.
namespace Requestor.Domain.Entities
{
[Table("Request")]
public class CustRequest
{
[Key]
public int RequestId { get; set; }
public string RequestByUserCd { get; set; }
public DateTime RequestDateTime { get; set; }
public DateTime DueDate { get; set; }
}
}
namespace Requestor.Domain.Abstract
{
public interface ICustRequestRepository
{
IQueryable<CustRequest> Request { get; }
}
}
namespace ITRequestHub.Domain.Concrete
{
public class EFDbContext : DbContext
{
public DbSet<CustRequest> Request { get; set; }
}
}
namespace ITRequestHub.Domain.Concrete
{
public class EFCustRequestRepository : ICustRequestRepository
{
private EFDbContext context = new EFDbContext(); //retrieves the data
public IQueryable<CustRequest> Request
{
get { return context.Request; }
}
}
}
Consider trying again with EF5 if you can, I experienced similar issues when trying to make EF6 work with MVC4 (I couldn' make scaffolding work either).
Or go all the way up to the latest versions for everything and try MVC5 with EF6 (this seems to work fine)
You've run into the wonderful and sometimes frustrating part of EF, its conventions. Wonderful when you're aware of the conventions as they simplify life, but frustrating when you feel that the framework is performing tasks without your explicit permission.
Firstly, additional information on EF6 conventions can be found here: http://msdn.microsoft.com/en-gb/data/jj679962.aspx
On your first point, as far as I'm aware, EF takes the name of your entity as the name of the table it will create in your DB. As you've discovered, you do have control over this via the "Table" attribute, but you can also control it's desire to want to pluralize your entity names when creating tables by means convention removal within your DbContext
modelBuilder.Conventions.Remove<PluralizingTableNameConvention>()
On your second point, I cannot imagine that you would require a "Key" attribute attached to your "RequestId" field. The convention here is that if the field name contains a suffix of ID (case-insensitive), then EF will automatically include it as a primary key and if the type of the field is either an Int or a Guid it will be automatically set as an auto-seed identity column.

Nhibernate updating version column on foreign key entity

I am using c# with Fluent NHibernate and auto mapping.
Here is some code (truncated for clarity), then I'll explain the problem.
public class Company
{
public virtual string Description { get; set; }
}
public class Stock
{
public virtual Product Product { get; set; }
public virtual Company Company { get; set; }
}
Mapping
mappings.Conventions.Add<CascadeConvention>()
.Conventions.Add<CustomForeignKeyConvention>()
.Conventions.Add<HasManyConvention>()
.Conventions.Add<VersionConvention>()
CascadeConvention just sets everything to All.
CustomForeignKeyConvention removes the _id that NHibernate usually
appends to foreign key id columns.
HasManyConvention sets all HasMany's to inverse.
VersionConvention convertion looks like this:
instance.Column("Version");
instance.Default(1);
The problem is that when I insert a new stock record, Nhibernate also updates the version number on the related Company.
If I had an IList<Stock> property on the Company then that would make sense but I don't.
I've done a lot of reading around:
NHibernate emitting extraneous update statements regardless of proper Inverse (fluent nhibernate) settings on relations
Cascade Save-Update in NHibernate and version column
NHibernate Reference - Chapter 17. Example: Parent/Child
Ayende # Rahien - NHibernate Mapping
From these, I've tried a whole bunch of things including adding .Not.OptimisticLock() all over the place. I even added an IList<Stock> property on Company so that I could specifically set it as Inverse, Not.OptimisticLock, etc. Nothing I do seems to make any difference.
We eventually sorted this by moving to a Session-per-request paradigm. Not sure why it was going wrong or why this fixed it. I wrote numerous unit tests to try and reproduce the behaviour in a more controlled environment without success.
In any case, it works now. There are good reasons session-per-request is often given as the best practice way to manage NHibernate sessions in a web application.

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