EF Core running query's I did not call - sql-server

I have the following two models within my Blazor Server project:
Vergadering:
public class Vergadering
{
[Key]
public int Id { get; set; }
public string Naam { get; set; }
public DateTime DatumTijd { get; set; }
public ICollection<Bestuurslid> Aanwezigen { get; set; }
public string? Notulen { get; set; }
public ICollection<Vergadering>? HoofdVergadering { get; set; }
public ICollection<Vergadering>? GekoppeldeVergaderingen { get; set; }
public ICollection<Bestand>? Bestanden { get; set; }
public string? UserLastEditId { get; set; }
public IdentityUser? UserLastEdit { get; set; }
public DateTime? LastEdit { get; set; }
public ICollection<VergaderingAgendaItem>? vergaderingAgendaItems { get; set; }
}
VergaderingAgendaItem:
public class VergaderingAgendaItem
{
public int Id { get; set; }
public string Omschrijving { get; set; }
public bool Afgerond { get; set; }
public int? ParentId { get; set; }
public VergaderingAgendaItem? Parent { get; set; }
public int VergaderingId { get; set; }
public Vergadering Vergadering { get; set; }
public string? UserAangedragenId { get; set; }
public IdentityUser? UserAangedragen { get; set; }
}
This results in three tables:
Vergaderingen
VergaderingAgendaItems
VergaderingVergadering
In my repository I have the following update method:
public async Task ChangeAfgerondStatusAsync(VergaderingAgendaItem item)
{
using (var _db = _factory.CreateDbContext())
{
_db.VergaderingAgendaItems.Update(item);
await _db.SaveChangesAsync();
}
}
Whenever the Vergadering does not have a GekoppeldeVergadering this update method does not create any problem.
But whenever the Vergadering does have a GekoppeldeVergadering and I update a VergaderingAgendaItem of that Vergadering I get this error:
An error occurred while saving the entity changes. See the inner exception for details.
Looking at the command prompt that opens up while running the project I saw the following query and error.
Queries:
Error:
An exception occurred in the database while saving changes for context type 'AVA_ZICHT.Data.ApplicationDbContext'.
Microsoft.EntityFrameworkCore.DbUpdateException: An error occurred while saving the entity changes. See the inner exception for details.
Microsoft.Data.SqlClient.SqlException (0x80131904): Violation of PRIMARY KEY constraint 'PK_VergaderingVergadering'. Cannot insert duplicate key in object 'dbo.VergaderingVergadering'. The duplicate key value is (4, 3).
How is it that EF Core tries to update the GekoppeldeVergadering in VergaderingVergadering table. My method states VergaderingAgendaItem.Update()?

When handed a detached entity and told to Update it, EF will consider any associated entities as well. Since those references aren't tracked by the DbContext, the context will see those entities as new items to be inserted. This can result in duplicate key exceptions (as you are seeing) or inserting duplicate data with new PKs if those keys are set up as Identity columns.
One way to get around this issue is to use Automapper configured to just update the columns you expect to change:
public async Task ChangeAfgerondStatusAsync(VergaderingAgendaItem item)
{
using (var _db = _factory.CreateDbContext())
{
var existingItem = _db.VergaderingAgendaItems.Single(x => x.Id == item.Id);
Mapper.Map(item, existingItem);
await _db.SaveChangesAsync();
}
}
Alternatively this can be done manually by copying values from item to existingItem. existingItem is tracked entity so once it's updated, just call SaveChanges. The advantage of this over Update is that the resulting UPDATE SQL statement will only be for any columns that have actually changed, and it won't execute an UPDATE if nothing has actually changed.
This assumes we only want to copy fields from that entity, and none of the child/related entities. If you want to alter the collections/associations then you will need to eager load them and handle these separately. For instance changing the UserLastEdit reference, this is likely something you would want to eager-load so that it can be updated with the current User record.
My general advice is to avoid working with detached entities for concerns like this and instead use POCO view models. The trouble with using detached entities is that these are often incomplete representations of entity state, at worst, something deserialized from view state and cast into an Entity object. View Models can also be scaled down to just the data your client needs and what data is allowed to change. When it gets back to the server there is no confusion about what it is vs. what it pretends to be. Another consideration of applying updates which is important in multi-user systems is detecting stale data. Writing updates like this applies a "last in wins" approach where you should ideally check that the current DB data state concurrency token matches the token/version at the time that this user's original version was read. The attraction of using detached entities is the thought of avoiding a round-trip to the DB when performing an update, but in all honesty you should justify a round trip to ensure that the record is actually valid, the user actually can update that record, and the record hasn't been updated by someone else in the time this user was editing it.

Related

Filtering AutoQuery Results to Only Display Table Rows that Match Data in the Users Session

I'm working on a project that want's to control data access in a multi-tenant system. I've got a table set up which has a row on it that says what tenant the object applies to. Let's call this property
ClientObject.ClientOrgId
I want to set something up so that anytime this table is accessed the only results that are returned are results that match some piece of data in the users session. I.e.
ClientObject.ClientOrgId == UserSession.ClientOrgId
and I ideally want to do this restriction on the table model instead of re-implementing it for every query created.
I've found the Autofilter attribute in the service stack documentation, and it looks like the thing that I want to use, but I've been unable to get it working. An example of my code is below, and I'm not seeing any filtering whenever I set the user sessions ClientOrgID to anything different.
[Authenticate]
[Route("/clientObject", HttpMethods.Post)]
[Api("Creates a Client Object")]
public class CreateClientObject : ICreateDb<ClientObjectTableModel>, IReturn<ClientObjectMutationResponse>
{
[ValidateNotEmpty]
public string ClientName{ get; set; }
[ValidateNotEmpty]
public string ClientLocation { get; set; }
[ValidateNotEmpty]
[ValidateNotNull]
public Guid? ClientOrgId { get; set; }
}
[AutoFilter(QueryTerm.Ensure, nameof(ClientObjectTableModel.ClientOrgId), Eval= "userSession.ClientOrgId")]
public class ClientObjectTableModel : AuditBase
{
[AutoId]
public Guid Id { get; set; }
[Required]
public string ClientName { get; set; }
[Required]
public string ClientLocation { get; set; }
[Required]
public Guid ClientOrgId { get; set; }
}
I even went off the rails and tried something like
[AutoFilter(QueryTerm.Ensure, nameof(ClientObjectTableModel.ClientLocation), Value = "The Fourth Moon Of Mars")]
with the expectation that nothing would get returned, and yet I'm still seeing results.
All AutoQuery CRUD Attribute like [AutoFilter] should be applied to the AutoQuery Request DTO, not the data model.
Have a look at how to populate Tenant Ids with AutoPopulate and how it's later used to filter results with [AutoFilter].

Another efcore violation of primary key constraint reference table problem

Assume I've read and googled, and I still don't know what I'm doing incorrectly. Whenever I try to execute
_dbContext.Set<T>().Add(aMediaObjectWithAssociatedProvider);
_dbContext.SaveChanges();
I get the dreaded efcore violation of primary key constraint reference table
I have a class as such:
public class Media : BaseModel
{
public virtual string Title { get; set; }
public virtual string? Description { get; set; }
public virtual string Source { get; set; }
public virtual Guid? MediaTypeId { get; set; }
public virtual Guid? ProviderId { get; set; }
public virtual DateTime? StartDate { get; set; }
public virtual DateTime? EndDate { get; set; }
public virtual Provider? Provider { get; set; }
}
The BaseModel class is
public abstract class BaseModel : IBaseModel
{
public virtual Guid Id { get; set; }
}
The Provider class is as such:
public class Provider : BaseModel
{
public virtual string Name { get; set; }
public virtual string? ApiUsername { get; set; }
public virtual string? ConfigurationSection{ get; set; }
}
My DBContext has the following:
protected override void OnModelCreating(ModelBuilder mb)
{
mb.Entity<Media>().HasKey(x => x.Id);
mb.Entity<Media>().HasOne(p => p.Provider).WithOne().HasForeignKey<Media>(x => x.ProviderId);
}
The code for inserting a new object is as follows:
public T Insert(T oneObject)
{
try
{
// Ensure the entity has an ID
if (oneObject.Id == Guid.Empty)
{
oneObject.Id = Guid.NewGuid();
}
_dbContext.Set<T>().Add(oneObject);
_dbContext.SaveChanges();
}
catch (Exception error)
{
_logger.LogError(error.Message, error);
}
return oneObject;
}
Assume that providers are static, in a sense that they already exist in their table, and I don't want to add new providers when I save media... Media just needs to have a provider.
I know exactly what is happening (the model, after travelling through json back through the api to the server is losing context, but I'm also trying to build a repository type of system where I don't have to build complex save logic for every object. (hence why i'm hand wringing over adding code that loads existing providers).
This problem specifically began rearing its head when I was saving new Media objects into the database with existing Providers. I am still mulling over how to look up children dynamically, but i'm not quite there yet.
I've been at this for so long, i'm about ready to give up on efcore relations and just rebuild the models as single objects, and handle all of the manipulation in javascript. And I don't like this idea.
I know for a fact that there will be questions for more code, but please let me know what. Again, I'm just stepping into .net core / ef core so this code-first is a little confusing for me. Thanks
You may have 2 options to try. Do backup your whole project and database beforehand. Clone your database to another database name. Try these either one option using new cloned database for testing.
No.1
Set "newid()" without quotes in your ID's default value in sql server. So you don't need to use Guid.NewGuid() in code every insert. newid() will auto generate GUID.
No. 2
How about removing primary key from ID (GUID) and then creating new column "UID" (running number) and set UID (running number) as primary key and enable its identity? You need to change all other tables too. And re-link UID each other if you use relationship. This way, your UID will not have existing number when insert.

How to debug Entity Framework Code First database deployment error?

How do I debug an error with my Entity Framework Code First database deployment?
I'm using EF 6.1.3. I'm trying to deploy a database to Azure that includes the following model:
public class DriverLog
{
public int ID { get; set; }
[Required]
public DateTime LogDate { get; set; }
public int RouteID { get; set; }
public virtual Route Route { get; set; }
public int DriverID { get; set; }
[ForeignKey("DriverID")]
public virtual Employee Driver { get; set; }
public int DispatcherID { get; set; }
[ForeignKey("DispatcherID")]
public virtual Employee Dispatcher { get; set; }
}
The deployment succeeds, but when I try to access the database, I get the following error:
{"Message":"An error has occurred."}
If I remove the last two fields -- DispatcherID and Dispatcher -- I do not get this error.
For some reason, EF does not like my including these two fields, but I don't know why, and I don't know how to go about debugging this.
I was able to resolve the problem like this:
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
modelBuilder.Entity<DriverLog>()
.HasRequired(o => o.Dispatcher)
.WithMany()
.WillCascadeOnDelete(false);
}
Credit for this solution goes to: Introducing FOREIGN KEY constraint may cause cycles or multiple cascade paths - why?
Do check primary key constraint for 'DispatcherID', basicaly the order its creating the table, as you are using 'DropCreateDatabaseIfModelChanges'.
Also inject some custom error catch, to have detail error information.

One-to-one relationship in EF

My models look the following:
public class Job
{
public int Id { get; set; }
public virtual JobResult Result { get; set; }
}
public class JobResult
{
[Key, ForeignKey("Job")]
public int JobId { get; set; }
[Required]
public virtual Job Job { get; set; }
}
As you see the relationship is a required one-to-one.
When I try to create and save a job:
Job job = new Job();
job.Result = new Result();
context.Jobs.add(job);
context.SaveChanges();
I get the following error:
Unable to determine a valid ordering for dependent operations.
Dependencies may exist due to foreign key constraints, model
requirements, or store-generated values.
I don't quite understand the error message. What does it mean and what could be causing this error?
Your case is not valid beacose this is a circular reference. What must be done EF:
INSERT INTO JobResults(....., Job_Id) VALUES (....., #JobId);
EF can not do this becase Job not inserted and does not have Id.
INSERT INTO Jobs(....., JobResult_Id) VALUES (....., #JobResultId);
EF as well can not do this becase JobResult not inserted and does not have Id.
This is a vicious cycle.
You may revise your model. Get rid of one of the links. Example:
public class Job
{
public int Id { get; set; }
public virtual JobResult Result { get; set; }
}
public class JobResult
{
public int Id { get; set; }
public virtual Job Job { get; set; }
}
class JobResultConfiguration : EntityTypeConfiguration<JobResult>
{
public JobResultConfiguration()
{
HasRequired(e => e.Job).WithRequiredPrincipal(e => e.Result); // one-to-one
}
}
public class MyContext : DbContext
{
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
modelBuilder.Configurations.Add(new JobResultConfiguration());
}
}
This will allow you to do:
Job job = new Job();
job.Result = new Result();
context.Jobs.add(job);
context.SaveChanges();
EF make one-to-one relationship: two entity with two entity with identical primary key.
More detail about Fluent Api: https://msdn.microsoft.com/en-us/data/jj591620.aspx
Or your place revised model domain. As you circular links, i would try to get rid of the one link (or from Job or from JobResult)
Your [Required] field is on the wrong element for the type of operation you are performing. Because you are Requiring a JobResult to have a Job, you can Create a JobResult and then create the Dependent Job (which has no requirements), but not the other way around. If you try to create it from the Job end, the Job isn't attached to the JobResult until it is created, but it can't be created until the JobResult is, creating a circular reference.

Has EF6+ / 7 added any ways that I can add update child tables?

I have two tables:
public AdminTest()
{
this.AdminTestQuestions = new List<AdminTestQuestion>();
}
public int AdminTestId { get; set; }
public string Title { get; set; }
public virtual ICollection<AdminTestQuestion> AdminTestQuestions { get; set; }
}
public partial class AdminTestQuestion
{
public int AdminTestQuestionId { get; set; }
public int AdminTestId { get; set; }
public System.Guid QuestionUId { get; set; }
public virtual AdminTest AdminTest { get; set; }
}
I am using the following EF6 code to add a new adminTest (with its adminTestQuestions) to the
database:
public async Task<IHttpActionResult> Post([FromBody]AdminTest adminTest)
{
db.AdminTests.Add(adminTest);
foreach (AdminTestQuestion adminTestQuestion in adminTest.AdminTestQuestions)
{
db.AdminTestQuestions.Add(adminTestQuestion);
}
await db.SaveChangesAsync(User, DateTime.UtcNow);
return Ok(adminTest);
}
I have similar but more complicated code to deal with the case where questions are added or removed from the adminTest. All my code works but it would be very good if EF was able to do what I needed rather than my having to add many lines of code.
Can anyone tell me if there have been any changes to EF6 or if any changes are planned to EF7 that will allow it
has noted on the ef7 github they seams to have added some neat code that add primary key entity.
but it is still not clear as to if it will be a common thing for children collection in an entity.
Git hub Entity Framework Design Meeting Notes
but for EF6 you could use a Generic Repository to make all the work for you. (since you can't extend DbContext directly)
assuming db is a DbContext
you could use this -> : Accessing a Collection Through Reflection
then find all Property from a class T that contains ICollection<> and do a foreach on the item of the ICollection Property then do db.Set.Add(proprietyChild) on it
that would eliminate the need for always repeating the same add child to entity code.
some people already did implement a solution thou : Automated updates of a graph of deached entities

Resources