I am working on a .NET Core 3.1 Web API. Suppose I have the following model classes:
This is a model that points to an underlying database table called TableA:
[Table("TableA")]
public class TableA
{
public string ColumnA { get; set; }
}
This is a model that inherits from TableA but gets extra columns from a view in the database:
[Table("ViewTableA")]
public class ViewTableA : TableA
{
public string ColumnA { get; set; }
other properties from view ...
}
When reading from the database I will query using the ViewTableA model. Since the ViewTableA has the attribute [Table("ViewTableA")] entity framework with generate the query to pull data from the ViewTableA view.
My question is if I had the following:
ViewTableA objA = new ViewTableA(){ populate properties here};
TableA objB = (TableA)objA;
If I were to add objB to the DBContext and save the changes which table/view would it hit? The TableA table since objB is of type TableA which has an attribute of [Table("TableA")] or would it still hit the view called ViewTableA?
objB won't be change tracked until you call db.Set<T>().Add(objB); If you call db.Set<ViewTableA>().Add(objB), all is well. If you call db.Set<TableA>().Add(objB) EF might insert into TableA or ViewTableA, or fail. I'm not sure which, and you shouldn't rely on the behavior.
Related
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?
Can I use Entity Framework to save changes to a view?
I have an entity which is mapped to a View.
[Table("MyView")]
public class MyEntity
{
public long MyEntityId { get; set; }
public string Name { get; set; }
}
The View itself is like this:
CREATE VIEW MyView AS
SELECT
t.MyEntityId,
t.Name,
FROM
MyTable t
Would I be able to use Entity Framework change tracking to save changes to this View? So is something like this possible:
var record = Context.MyEntity.Where(e => e.MyEntityId == 150).FirstOrDefault();
record.Name = "New Name";
Context.SaveChanges();
Looks like Entity Framework does not care if the Entity is mapped to a View or Table... it would just create the same update script. For the example above EF generates the following script:
UPDATE [MyView] SET [Name]=#gp1 WHERE [MyEntityId] = 150
-- #gp1: 'New Name' (Type = String, IsNullable = false, Size = 8)
So EF does not introduce any additional limitation for updating a View... but we still have the RDBMS specific limitations for updating a View... as an example, in SQL Server a view can be updated subject to the following limitations:
If the view contains joins between multiple tables, you can only insert and update one table in the view, and you can't delete rows.
You can't directly modify data in views based on union queries. You can't modify data in views that use GROUP BY or DISTINCT statements.
All columns being modified are subject to the same restrictions as if the statements were being executed directly against the base
table.
Text and image columns can't be modified through views.
There is no checking of view criteria. For example, if the view selects all customers who live in Paris, and data is modified to
either add or edit a row that does not have City = 'Paris', the data
will be modified in the base table but not shown in the view, unless
WITH CHECK OPTION is used when defining the view.
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?
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);
I am using Entity Framework 4.2 in a class library project. The database already exists, and I cannot modify it in any way whatsoever.
I have two model/domain classes that model two database tables. The tables both expose an Id column value, which I will refer to as ThingsId. Lets call the tables TableOfThings1 and TableOfThings2. Here are my classes:
public class TableOfThings1
{
public string ThingId { get; set; }
public virtual Thing Thing { get; set; }
}
public class TableOfThings2 //qse
{
public Int64? ThingId {get; set;}
public string ThingName { get; set; }
}
The problem is that the TableOfThings1 exposes ThingsId as a nullable varchar(64), while TableOfThings2 exposes ThingsId as a non-nullable bigint.
How can I tell the Entity Framework to join on these two keys? I have tried using this:
HasOptional(things1 => things1.thing).WithMany().HasForeignKey(t => t.ThingId);
in the EntityTypeConfiguration forTableOfThings1.
I have also tried casting in the middle of that statement, which does not work. Using the setup shown above gets me this error message currently:
"The types of all properties in the Dependent Role of a referential
constraint must be the same as the corresponding property types in the
Principal Role".
Does anyone know for sure whether/how this is possible?
This is not possible with EF. You can not even create a foreign key in the database if the column types are different. Possible workaround would be to create a view of TableOfThings1 with ThingId column type matching the TableOfThings2s column type.