Entity framework - inserting by ID - entity-framework

I'm in a situation where I'm importing lots of "link" records from an XML file, and I want to insert them in my SQL link table using Entity Framework. My link table is literally just 2 columns, both of which are FKs and constitute the PK:
[UserAssessmentId] [int] NOT NULL
[AnswerId] [int] NOT NULL
The way I'm used to doing inserts involves the following:
Get the UserAssessment entity from the DB for userAssessmentId.
Get the Answer entity from the DB for answerId.
Add the Answer entity to the UserAssessment entity's Answers collection.
Repeat 2 and 3 for each answerId to add.
Call context.SaveChanges().
The trouble is that this is extremely DB intensive when adding hundreds of answers; EF has to get the record for each answer it is adding to the link table! I just want to insert a record with a given userAssessmentId, and a given answerId, and not go through the trouble of getting the entity first. EF needn't worry about whether the IDs I'm inserting are valid; just assume they are. Is there a way to get EF to do this or do I need to just use plain SQL?

The simplest option would probably be to create a separate context and a simple entity to represent your link table.
[Table("Name of the link table")]
public class UserAssessmentAnswer
{
public int UserAssessmentId { get; set; }
public int AnswerId { get; set; }
}
public class UserAssessmentAnswerContext : DbContext
{
public UserAssessmentAnswerContext()
: base("Connection string for the real context")
{
}
public IDbSet<UserAssessmentAnswer> UserAssessmentAnswers
{
get { return Set<UserAssessmentAnswer>(); }
}
}
Then you can use the new context and entity to insert your data:
using (var context = new UserAssessmentAnswerContext())
{
context.UserAssessmentAnswers.Add(new UserAssessmentAnswer
{
UserAssessmentId = ...,
AnswerId = ...
});
...
context.SaveChanges();
}
EDIT
You'll need to turn off database initialization for the new context. In your configuration file, add:
<entityFramework>
<contexts>
<context
type="YourNamespace.UserAssessmentAnswerContext, YourAssembly"
disableDatabaseInitialization="true"
/>
</contexts>
</entityFramework>
Or, you can add the following code to your startup:
Database.SetInitializer<UserAssessmentAnswerContext>(null);

Related

Entity Framework navigation with only foreign key

Following the guide lines from Domain Driven Design, I try to avoid having one aggregate referencing a different aggregate. Instead, an aggregate should reference another aggregate using the other aggregate's id, for example:
public class Addiction
{
private Addiction(){} //Needed for EF to populate non-simple types
//DrugType belongs to the aggregate,
//inflate when retrieving the Addiction from the db
//EF does not need DrugId for navigation
Drug Drug{get;set;}
//The supplier is not part of the aggregate,
//aggregates only reference eachother using Ids
int SupplierId{get;set;}
//Other properties
}
public class AddictionConfiguration : IEntityTypeConfiguration<Addiction>
{
builder.HasOne(addiction => addiction.Drug); //Works
builder.HasOne("SupplierId") //Does not work.
}
In this (not very realistic) example, Drug is part of the Addiction's aggregate. When loading this entity from the database using EF, it will also inflate the Drug property without me having to specify the DrugId as the foreign key.
However, now I need to get a list of all Addictions and their suppliers by mapping the relevant properties to a Dto. I try to achieve this by using AutoMapper's ProjectTo functionality, e.g.
_mapper.ProjectTo<AddictionDto>(_dbContext.Addictions.Where(x => x.Id > 1));
where AddictionDto is defined as
public class AddictionDto
{
DrugDto Drug {get;set;}
SupplierDto Supplier {get;set;}
//other properties
}
And
public class SupplierDto
{
public int Id {get;set;}
public string Name {get;set;}
}
Automapper correctly loads the Addiction and also the Drug, but I cannot get it to load the Supplier. I've tried all the options of the IEntityTypeConfiguration to tell EF that there is a navigation property, but I cannot get it to work. Does anyone know if is even possible to do what I described above?

EF Core 2.0/2.1 - How to efficiently handle large, infrequently accessed columns?

I have a table as follows:
CREATE TABLE MyTable
(
ID INT NOT NULL PRIMARY KEY,
NAME VARCHAR(50) NOT NULL,
LARGEBLOB VARBINARY(MAX) NULL
)
With an Entity defined as:
public class Entity
{
public int Id {get;set;}
public string Name {get;set;}
public virtual byte[] LargeBlob {get;set;}
}
99% of my use cases involve displaying ID and NAME only.
1% of the time I need LARGEBLOB.
Is there any way I can mark LargeBlob as Lazily Loaded so to avoid
huge wasted data transfers? Alternatively, are there other ways of
achieving the same outcome?
I tried splitting into 2 tables with a 1->[0|1] relationship as follows:
CREATE TABLE MyTable
(
ID INT NOT NULL PRIMARY KEY,
NAME VARCHAR(50) NOT NULL,
LARGEBLOBID INT NULL
)
CREATE TABLE MySubTable
(
ID INT PRIMARY KEY,
LARGEBLOB VARBINARY(MAX) NOT NULL
)
with entities
public class Entity
{
public int Id { get; set; }
public string Name { get; set; }
public virtual LargeBlob LargeBlob { get; set; }
}
public class LargeBlob
{
public int Id { get; set; }
public virtual byte[] Blob { get; set; }
}
That did work in so far as lazy loading was concerned, but I tried all manner of inverse relationship / foreign key tags, HasOne, OwnsOne, OnDelete(Cascade) in all kinds of combinations, but I couldn't achieve what I wanted to achieve. Just to recap, that would be:
Blob is loaded only when the LargeBlob property is actually derefenced.
If entity.LargeBlob property gets set to a new LargeBlob, the (now "orphaned" ) old LargeBlob gets deleted from the database.
If the entity gets deleted, the related large blob gets deleted.
Quick Update re: versions &c
Note: I'm using VS 2017 15.6.2, .net core 2.0, with EF core 2.1 (to get at least the possibility of some lazy loading). Nuget packages:
<PackageReference Include="Microsoft.AspNetCore.All" Version="2.0.6" />
<PackageReference Include="Microsoft.EntityFrameworkCore.Proxies" Version="2.1.0-preview1-final" />
<PackageReference Include="Microsoft.EntityFrameworkCore.Tools" Version="2.1.0-preview1-final" PrivateAssets="All" />
I tried splitting into 2 tables with a 1->[0|1] relationship as follows
But by putting the FK in the Entity you actually did the opposite - [0|1]->1 relationship.
To get the desired relationship, the FK must be at LargeBlog. It could be a separate property (column), but the most appropriate is to use the Id property as both PK and FK (the so called shared PK association). You can do it with the following fluent configuration:
modelBuilder.Entity<Entity>()
.HasOne(e => e.LargeBlob)
.WithOne()
.HasForeignKey<LargeBlob>(e => e.Id);
Once you do that, since the whole purpose of doing it was to get separate controllable (eager, explicit or lazy when available) load behavior, it can be seen that the separate table is not really needed - the "entity" containing the blob data can be embedded inside the same table using the table splitting which is achieved by simply adding the following to the above configuration:
modelBuilder.Entity<Entity>().ToTable("MyTable");
modelBuilder.Entity<LargeBlob>().ToTable("MyTable");
Note that while the most logical choice seems to be owned type, unfortunately currently owned types are always loaded (similar to EF6 complex types), so they cannot be used to achieve controllable load behavior.
You should only select the columns you need to save bandwidth:
var entity = await dbContext.Entities
.Where(...)
.Select(e => new
{
Id = e.Id,
Name = e.Name,
LargeBlob = null,
})
.FirstOrDefaultAsync();
and whenever you really need the LargeBlob column, load it manually
entity.LargeBlob = await dbContext.Entities
.Where(e => e.Id == entity.Id)
.Select(e => e.LargeBlob)
.SingleOrDefaultAsync();
You can delete an entity without loading the whole entity, just the Id (and the concurrency token, if present on the entity) suffices
var entity = new Entity { Id = removeEntityId };
dbContext.Entities.Remove(entity);
await dbContext.SaveChangesAsync();

Is it Possible to Use EF Core in an MVC site Using Just One Table?

Say I have a table in the database with data already populated (imported from excel).
I am building an MVC website using .NET Core and EF Core (both v.1.1.2)
What I'm wanting to do create a series of models, whose data is derived from one original "source data" table. The source data table has 150 columns, and although I don't need them all right now, I do want to retain all columns so that I might be able to use them at a later time if needed.
Using the "dotnet ef dbcontext scaffold" command, I was able to generate a:
Model - with all public get/set properties for each column
DbContext Class - with DbSet & Fluent API statements to reference the table and columns
With this setup, is it possible to setup a sort of "virtual" relational data model with EF Core by simply creating various Model classes (many-to-many relationships) and their properties equal to the properties already defined in the original "source" entity?
What I'm hoping to avoid is having to maintain an actual relational data structure in the DB (independent tables linked by PKs, FKs, join tables, etc.)
Reason being... I'll only be looking to do a nightly update of the source data table from bulk import of Excel worksheet and will not have to persist or track any changes to the data (read only). So, I'd like to not have to deal with the additional overhead/setup/maintenance involved with mapping the source data to relational tables, columns, keys, etc. during the import.
Can I simply create various models, then in the DbContext, override the OnModelCreating method passing an instance of modelBuilder to map those DbSet entities to the columns in the source table?
namespace VTracker.Contexts
{
public partial class DataContext : DbContext
{
public DataContext(DbContextOptions options) : base(options)
{
}
public DbSet<SourceData> Data { get; set; }
public DbSet<SomeModel1> Model1 { get; set; }
public DbSet<SomeModel2> Model2 { get; set; }
public DbSet<SomeModel3> Model3 { get; set; }
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.Entity<SourceData>(entity =>
{
// some key must be defined?
entity.HasKey(e => e.SourceIDColumn);
entity.ToTable("SourceDataTable");
entity.Property(e => e.SourceIDColumn)
entity.Property(e => e.SourceColumn1)
.HasColumnName("SourceColumn1")
// ...... continue mapping .....
// Some Model 1 Entity Builder
entity.ToTable("SourceDataTable");
// ...... continue mapping .....
// Some Model 2 Entity Builder
entity.ToTable("SourceDataTable");
// .... and so on for other models......
}
}
}
}
Or would it be better to just use the one entity for everything and just build out SQL / LINQ queries to retrieve/join the data as needed?

Access underlying DbContext (or run stored procedure) from Entity Framework POCO method

Is it possible to access the underlying DbContext (the DbContext that has populated this object/has this object in its cache/is tracking this object) from inside a model object, and if so, how?
The best answer I have found so far is this blog post which is five years old. Is it still the best solution available?
I’m using the latest version of Entity Framework if that matters.
Here's a sample to clarify my question:
I have a hierarchical tree. Let’s say it is categories that could have sub-categories. The model object would be something like this:
class Category
{
string CategoryId { get; set; }
string Name { get; set; }
virtual Category Parent { get; set; }
virtual ICollection<Category> Children { get; set; }
}
Now, if I want to access all descendants of a category (not just its immediate children) I can use a recursive query like this:
class Category
{
//...
IEnumerable<Category> Descendants
{
get
{
return Children.Union(Children.SelectMany(q => q.Descendants));
}
}
}
which works, but has bad performance (due to multiple database queries it needs to perform).
But suppose I have an optimized query that I can run to find descendent (maybe I store my primary key in a way that already contains path, or maybe I’m using SQL Server data type hierarchyid, etc.). How can I run such a query, which needs access to the whole table/database and not just the records available through model object’s navigational properties?
This can be either done by running a stored procedure/SQL command on the database, or a query like this:
class Category
{
//...
IEnumerable<Category> Descendants
{
get
{
// this won't work, because underlying DbContext is not available in this context!
return myDbContext.Categories.Where(q => q.CategoryId.StartsWith(this.CategoryId));
}
}
}
Is there a way at all to implement such a method?

One-To-One relationship with fluent api. A Hacky way?

EF 4.3.1. I have defined User and Box entities. Each box may or may not be assigned to a user.
What I'd like to achieve is to have a OwnBox property in User class, and an Owner property in Box class.
in Database, I have defined OwnerId foreignkey in Boxes (Boxes.OwnerId has relation with Users.UserId).
To define the relationship with fluent api, I have defined the following classes:
public partial class User
{
public int UserId {get; set;}
public virtual Box OwnBox { get; set; }
}
public partial class Box
{
public int? OwnerId { get; set; }
public virtual User User { get; set; }
}
Then in my Mapping class for Box, I have defined the relations as follows:
this.HasOptional(t => t.User).WithOptionalDependent(d => d.OwnBox).
Map(m => m.MapKey("OwnerId")).WillCascadeOnDelete(true);
But by firing up the project, I got the error:
Schema specified is not valid. Errors: (56,6) : error 0019: Each
property name in a type must be unique. Property name 'OwnerId' was
already defined.
So I had to tell EF to forget about the OwnerId column first:
this.Ignore(t => t.OwnerId);
Now the project works fine. But I'm still doubtful if this is a good approach and will everything work fine on CRUD operations with foreign key associations.
First of all, this is not one-to-one relationship. In one-to-one relationship the foreign key must be a primary key.
I believe in your scenario the situation can happen:
User = { UserID = 2 }
Box1 = { UserID = 2 }
Box2 = { UserID = 2 }
Nothing stops you from doing that, but which box should be returned when you do that:
User.OwnBox, Box1 or Box2?
EF can deal with that using Independent Association. It will create foreign key, hidden from your POCO class. You can specify the name of the column using MapKey as you did. However, because you also created a property called OnwerID, just as the column used with MapKey, the EF has a problem as two properties are mapped to the same column.
When you use ignore, the POCO OwnerID property is ignored by EF so that fixes the problem of two properties, however, the OwnderID value never gets saved or read to the database. Because EF just ignores it.
Thanks for your question, I have learnt a lot thanks to this.