Using ASPNET Boilerplate (.NET Framework) 3.6.1
I have successfully used the AsyncCrudAppService on a number of entities without issue, but I've hit an issue with one particular entity type that I can't insert, as Entity Framework seems to think it's being provided an explicit ID.
My Entity:
public class Attendance : FullAuditedEntity<int>
{
[Required]
public Guid Guid { get; set; }
public long? UserId { get; set; }
[Required]
public int EventId { get; set; }
public int StatusId { get; set; }
[Required]
[MaxLength(100)]
public string InviteEmail { get; set; }
[Required]
[MaxLength(20)]
public string InviteCode { get; set; }
public bool InviteAccepted { get; set; }
public DateTime? InviteAcceptedDate { get; set; }
public string InviteSource { get; set; }
public long? InvitedByUserId { get; set; }
[ForeignKey("UserId")]
public User User { get; set; }
[ForeignKey("InvitedByUserId")]
public User InvitedByUser { get; set; }
[ForeignKey("EventId")]
public Event Event { get; set; }
[ForeignKey("StatusId")]
public AttendanceStatus Status { get; set; }
}
My CreateDto
[AutoMapTo(typeof(Attendance))]
public class CreateAttendanceDto
{
public long? UserId { get; set; }
[Required]
public int EventId { get; set; }
public int StatusId { get; set; }
[Required]
[MaxLength(100)]
public string InviteEmail { get; set; }
public bool InviteAccepted { get; set; }
public DateTime? InviteAcceptedDate { get; set; }
public string InviteSource { get; set; }
public long? InvitedByUserId { get; set; }
}
My AppService Create method:
public override async Task<AttendanceDto> Create(CreateAttendanceDto input)
{
var attendance = ObjectMapper.Map<Attendance>(input);
attendance.InviteCode = "TEST";
// Create GUID
attendance.Guid = Guid.NewGuid();
await _attRepository.InsertAndGetIdAsync(attendance);
return MapToEntityDto(attendance);
}
Debugging the entity shows that is is sending id:0 to the insert method. This is exactly the same as another entity insert which is working fine.
Tracing the two different entity insert queries in SQL Profiler I can see that the working one has removed the ID from the insert query, where the one above that isn't working is trying to insert id=0 into the database, which is clearly a problem.
The error returned by the InsertAndGetIdAsync call as follows:
ERROR 2018-07-10 17:29:06,249 [108 ] nHandling.AbpApiExceptionFilterAttribute - An error occurred while updating the entries. See the inner exception for details.
System.Data.Entity.Infrastructure.DbUpdateException: An error occurred while updating the entries. See the inner exception for details. ---> System.Data.Entity.Core.UpdateException: An error occurred while updating the entries. See the inner exception for details. ---> System.Data.SqlClient.SqlException: Cannot insert explicit value for identity column in table 'Attendance' when IDENTITY_INSERT is set to OFF.
I've checked and double-checked for differences between these two appservice Creates and their related entities and dtos, and I can't find any differences. Even got a colleague to verify.
Any idea what might be going on here?
You need to specify a mapping for the entity Attendance so that it does not try to insert a value in the ID column. In simple terms, you need to makes EF aware that the ID column is configured for IDENTITY. The Exception message states this very clearly.
System.Data.SqlClient.SqlException: Cannot insert explicit value for identity column in table 'Attendance' when IDENTITY_INSERT is set to OFF.
Using attributes:
public class Attendance {
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public long AttendanceId { get; set; }
}
or in fluent mapping in the DbContext type
protected override void OnModelCreating(DbModelBuilder modelBuilder) {
modelBuilder.Entity<Attendance>().Property(t => t.AttendanceId).HasDatabaseGeneratedOption(DatabaseGeneratedOption.Identity);
}
I found the issue, which was a little hidden.
Inside my DbContext I had previously attempted to give the Guid field a default value (which I now realise was also making it an identity column in the eyes of EF). In the end this didn't work but I'd neglected to tidy it up:
modelBuilder.Entity<Attendance>()
.Property(x => x.Guid)
.HasDatabaseGeneratedOption(DatabaseGeneratedOption.Identity);
The database did not have any identity column on the Guid column, and the error related to the Id column, but clearly this was confusing EF's handling of the id value in the resulting query.
Removed the above code, rebuilt and all is working as it should.
Related
I am trying to use a simple where clause to retrieve data from a SQL Server. However the generated query is incorrect.This query worked perfectly with EF Core 2.2 but with EF Core 3 it throws an exception.
public async Task<List<CharacterReplacements>> GetReplacementsAsync(int? replacementSetId)
{
var replacementQuery = _context.CharacterReplacements.AsQueryable();
if (replacementSetId.HasValue)
{
replacementQuery = replacementQuery.Where(r => r.CharacterReplacementSetID == replacementSetId.Value); // .AsQueryable();
}
var replacementList = await replacementQuery.ToListAsync();
return replacementList;
}
[Serializable]
[Table("CharacterReplacementSets", Schema = "SYSTEM")]
public class CharacterReplacementSets
{
[NavigationPropertyKey]
[Key]
public int CharacterReplacementSetID { get; set; }
public string Name { get; set; }
public string Description { get; set; }
public ICollection<CharacterReplacements> CharacterReplacements { get; set; }
public ICollection<FormatField> FormatFields { get; set; }
public string CreatedBy { get; set; }
public DateTime CreatedOn { get; set; }
public string UpdatedBy { get; set; }
public DateTime? UpdatedOn { get; set; }
public string DefaultEncoding { get; set; } // Default character set
public string DefaultCharacter { get; set; }
public CharacterReplacementSets()
{
CharacterReplacements = new List<CharacterReplacements>();
}
}
[Serializable]
[Table("CharacterReplacements", Schema = "SYSTEM")]
public class CharacterReplacements
{
[NavigationPropertyKey]
[Key]
public int CharacterReplacementID { get; set; }
public char OriginalCharacter { get; set; }
public string ReplacementCharacter { get; set; }
public string CreatedBy { get; set; }
public DateTime CreatedOn { get; set; }
public string UpdatedBy { get; set; }
public DateTime? UpdatedOn { get; set; }
[ForeignKey("CharacterReplacementSets")]
public int CharacterReplacementSetID { get; set; }
}
Expected result- Retrieve all CharacterReplacements where the replacementSetId equals the provided replacementSetId.
Actual result-
Microsoft.Data.SqlClient.SqlException: 'Invalid column name 'CharacterReplacementSetsCharacterReplacementSetID'.
Could someone kindly help me out with this?
The problem is not the specific query, but the model mapping.
First, the ForeignKey attribute here
[ForeignKey("CharacterReplacementSets")]
public int CharacterReplacementSetID { get; set; }
has no effect. When applied to navigation property, it's supposed to specify the FK property name. And when applied on FK property as here, it's supposed to specify the navigation property name. CharacterReplacements has no navigation property called CharacterReplacementSets, so the attribute is simply ignored. It would be good if EF Core generates runtime error to indicate a mapping problem, but it doesn't.
The attribute has been ignored in EF Core 1.x / 2.x as well. However it worked because the name of the property CharacterReplacementSetID matches the name of the PK of CharacterReplacementSets. This is no more true for EF Core 3.0 due to the following breaking change - The foreign key property convention no longer matches same name as the principal property.
So remove the incorrect and misleading ForeignKey attribute, and configure the FK property by either HasForeignKey fluent API (my preferred):
modelBuilder.Entity<CharacterReplacementSets>()
.HasMany(e => e.CharacterReplacements)
.WithOne()
.HasForeignKey(e => e.CharacterReplacementSetID);
or with ForegnKey attribute on navigation property (or inverse navigation property when there is no navigation property as here):
[ForeignKey("CharacterReplacementSetID")]
public ICollection<CharacterReplacements> CharacterReplacements { get; set; }
Note that you might have similar problem with FormatField and other entities using similar named FKs w/o navigation properties.
Another way to avoid this issue is to use singular entity class names like CharacterReplacementSet, CharacterReplacement etc. because the [entity name] + ID still matches EF Core conventions. And in general singular class names are better/preferable, even just for readability.
I am attempting to modify a single boolean property on my Invoice entity.
This is my class:
public class Invoice : SoftDeletableEntity, IIsActive
{
public Invoice()
{
IsEditable = true;
}
[ForeignKey("Booking")]
public int BookingId { get; set; }
public Booking Booking { get; set; }
[ForeignKey("Customer")]
public int CustomerId { get; set; }
public Customer Customer { get; set; }
public string OurReference { get; set; }
public string CustomerReference { get; set; }
public DateTime InvoiceDate { get; set; }
public string InvoiceNumber { get; set; }
[ForeignKey("AccountInformation")]
public int AccountInformationId { get; set; }
public AccountInformation AccountInformation { get; set; }
public string Summary { get; set; }
public List<InvoiceInformation> ImportantInformation { get; set; }
[ForeignKey("InvoiceItem")]
public List<int> InvoiceItemIds { get; set; }
public List<InvoiceLineItem> InvoiceLineItems { get; set; }
[ForeignKey("InvoiceDocument")]
public List<int> InvoiceDocumentIds { get; set; }
public List<InvoiceDocument> InvoiceDocuments { get; set; }
public string CreatedBy { get; set; }
public string Terms { get; set; }
public bool IsReversal { get; set; }
public bool IsEditable { get; set; }
public int? ParentInvoiceId { get; set; }
}
These classes are exported as a formatted PDF. That process works quite well, and then I have this code that checks whether or not the PDF was constructed correctly, then if the export succeeds, the code sets the invoice as ineditable like so:
var doc = pdf.CreateInvoicePdf(resultModel, user);
var bytes = GetPdfBytes(doc);
if (bytes != null)
{
result.IsEditable = false;
}
unitOfWork.Commit();
When I call this "unitOfWork.Commit();" I get the following exception:
System.Data.Entity.Infrastructure.DbUpdateException: 'An error occurred while saving entities that do not expose foreign key properties for their relationships.
The inner exception:
Message "Store update, insert, or delete statement affected an unexpected number of rows (0). Entities may have been modified or deleted since entities were loaded. See http://go.microsoft.com/fwlink/?LinkId=472540 for information on understanding and handling optimistic concurrency exceptions."
The most recently added foreign key reference was for CustomerId, but it is set up just like all of the other foreign keys. I have checked the database for FK = 0 for all foreign key entities and there are no missing references. Also this is running on my local, so I am certain the entity was not modified by sources other than my browser.
Any help is greatly appreciated.
Here are some possible reasons which might cause such problem:
You trying to update entity with primary key Id == 0 or another default value (Guid.Empty or string.Empty). So, verify that Id is set properly.
When you try to create a new object and tell EF that it's modified using the EntityState.Modified. Then it will throw this error as it doesn't yet exist in the database. So, try to use EntityState.Added.
You tried to update or delete a row but the row doesn't exist.
As part of an assignment, I must create a web app using Entity Framework. I am following the tutorial at https://learn.microsoft.com/en-us/aspnet/mvc/overview/getting-started/getting-started-with-ef-using-mvc/creating-an-entity-framework-data-model-for-an-asp-net-mvc-application.
There are four tables: Branch, Department, Employee and Title.
After creating a method to seed the data I have run the application in debugging mode and got the following error message:
System.Data.Entity.Infrastructure.DbUpdateException: ‘An error occurred while updating the entries. See the inner exception for details.’ SqlException: The INSERT statement conflicted with the FOREIGN KEY constraint "FK_dbo.Employee_dbo.Branch_BranchId". The conflict occurred in database “HumanResourcesWebApp1”, table “dbo.Branch”,column ‘ID’. The statement has been terminated.
I have researched this message but I am not following the answers which I have found. I would be grateful for some direction. I am also adding the structure of the classes if it helps. I understand that the issue is between the primary key of the Branch table when seen as a foreign key by the Employee table but I do not know where to make changes.
Branch:
public int ID { get; set; }
public string Address { get; set; }
public int DepartmentID { get; set; }
public virtual ICollection<Employee> Employees { get; set; }
Department:
public int ID { get; set; }
public string DepartmentName { get; set; }
public string DepartmentFunction { get; set; }
public virtual ICollection<Branch> Branches { get; set; }
Employee:
public int ID { get; set; }
public string FirstName { get; set; }
public string SecondName { get; set; }
public double Salary { get; set; }
public DateTime StartingDate { get; set; }
public int BranchId { get; set; }
public int TitleId { get; set; }
Title:
public int ID { get; set; }
public string TitleName { get; set; }
public virtual ICollection<Employee> Employees { get; set; }
Thanks in advance
You're attempting to save employee details that reference a branch that doesn't exist. This is either because you haven't set the branch id at all, or you have set it to a number that is not present in the branch table Set the employees branch id to a branch that already exists. If no branches exist, create one. You should be able to create a branch and an employee simultaneously in code so long as the employee correctly refers to the branch. Entity framework should save the branch first so that it exists at the point it comes to save the employee
I have created Entity Data Model in Visual Studio. Now I have file with SQL queries and C# classes generated from Model.
Question:
Classes are generated without annotations or code behind (Fluent API). Is it OK? I tried to run my application but exception was thrown:
Unable to determine the principal end of an association between the types 'Runnection.Models.Address' and 'Runnection.Models.User'. The principal end of this association must be explicitly configured using either the relationship fluent API or data annotations.
I read that I can not use Fluent API with "Model First". So what can I do?
Code:
User
public partial class User
{
public User()
{
this.Events = new HashSet<Event>();
this.CreatedEvents = new HashSet<Event>();
}
public int Id { get; set; }
public string FirstName { get; set; }
public string LastName { get; set; }
public string Photo { get; set; }
public int EventId { get; set; }
public string Nickname { get; set; }
public OwnerType OwnerType { get; set; }
public NetworkPlaceType PlaceType { get; set; }
public virtual ICollection<Event> Events { get; set; }
public virtual Address Address { get; set; }
public virtual ICollection<Event> CreatedEvents { get; set; }
public virtual Owner Owner { get; set; }
}
Address
public partial class Address
{
public int Id { get; set; }
public string Street { get; set; }
public string StreetNumber { get; set; }
public string City { get; set; }
public string ZipCode { get; set; }
public string Country { get; set; }
public virtual User User { get; set; }
}
Context
//Model First does not use this method
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
modelBuilder.Entity<Address>().HasRequired(address => address.User)
.WithRequiredDependent();
modelBuilder.Entity<User>().HasRequired(user => user.Address)
.WithRequiredPrincipal();
base.OnModelCreating(modelBuilder);
}
You have to specify the principal in a one-to-one relationship.
public partial class Address
{
[Key, ForeignKey("User")]
public int Id { get; set; }
public string Street { get; set; }
public string StreetNumber { get; set; }
public string City { get; set; }
public string ZipCode { get; set; }
public string Country { get; set; }
public virtual User User { get; set; }
}
By specifying a FK constraint, EF knows the User must exists first (the principal) and the Address follows.
Further reading at MSDN.
Also, see this SO answer.
Updated from comments
In the designer, select the association (line between Users & Address). On the properties window, hit the button with the [...] on Referential Constraint (or double click the line). Set the Principal as User.
Error:
Had same error of "Unable to determine the principal end of an association between the types 'Providence.Common.Data.Batch' and 'Providence.Common.Data.Batch'. The principal end of this association must be explicitly configured using either the relationship fluent API or data annotations.".
HOWEVER, note that this is the SAME table.
Cause: My database was MS SQL Server. Unfortunately when MS SQL Server's Management Studio adds foreign keys, it adds the default foreign key as Batch ID column of Batch table linking back to itself. You as developer are suppose to pick another table and id to truly foreign key to, but if you fail to it will still allow entry of the self referencing FK.
Solution:
Solution was to delete the default FK.
Cause 2: Another situation is that the current table may be fixed but the old historical image of the table when the EF's edmx was done had the default FK.
Solution 2: is to delete the table from the Model Browser's Entity Types list and click "yes" and then "Update Model from the Database" again.
I am working with an existing database with no FK constraints and I am using .NET MVC4 template with WebApi. I am trying to create a model like this
namespace My.Models {
[Table("Risk")]
public class Risk {
[Key]
[Column("RiskId")]
public long Id { get; set; }
public string Description { get; set; }
public int Severity { get; set; }
public string Status { get; set; }
public int Occurrence { get; set; }
public int Grade { get; set; }
[Column("CreatedBy")]
public UserProfile CreatedBy { get; set; }
public DateTime CreatedDate { get; set; }
}
}
The UserProfile is the Account model from the template including UserId, Email and Password.
[Table("UserProfile")]
public class UserProfile {
[Key]
[DatabaseGeneratedAttribute(DatabaseGeneratedOption.Identity)]
public int UserId { get; set; }
public string Email { get; set; }
}
When I try and run the following query -
var risks = (from h in db.Risks
//where x.SomeField == SomeValue
orderby h.CreatedDate descending
select h).Skip((page - 1) * pageSize).Take(pageSize);
I get an error: System.Data.SqlClient.SqlException: Invalid column name 'UserProfile_UserId'.
I thought my Column annotation had already flagged to EF what it should be looking for. It seems to keep appending "_UserId" to whatever I say the column name is.
CreatedBy is a navigation property. You can't apply the [Column] attribute to navigation properties. In order to configure the name of the foreign key column in the database you must use Fluent API. With your model it is not possible with data annotations alone:
modelBuilder.Entity<Risk>()
.HasOptional(r => r.CreatedBy) // or HasRequired
.WithMany()
.Map(m => m.MapKey("CreatedBy")); // this is the FK column name
Alternatively you can introduce the foreign key as property into your model and then use annotations:
[Table("Risk")]
public class Risk {
[Key]
[Column("RiskId")]
public long Id { get; set; }
//...
[Column("CreatedBy")]
public int? CreatedById { get; set; } // or int if CreatedBy is required
[ForeignKey("CreatedById")]
public UserProfile CreatedBy { get; set; }
}