Where returns wrong record - c#

UPDATE
I've just had a thought that may well be relevant to this issue. I'm using a code first approach with this project. Originally my ZoneMapping class was defined as you can see below, however the database only had a single PrimaryKey field in the database. I believe because EF hadn't interpreted the data quite correctly.
At this point I made a modification to my migration SQL script output to add the additional primary key that I applied to the database. I've just updated the migration instead from:
CreateTable(
"dbo.NetC_EF_ZoneMapping",
c => new
{
PostcodeKey = c.String(nullable: false, maxLength: 128),
Zone_ID = c.Int(),
})
.PrimaryKey(t => t.PostcodeKey)
.ForeignKey("dbo.NetC_EF_Zone", t => t.Zone_ID)
.Index(t => t.Zone_ID);
I've just tried adding an additional PrimaryKey manually in the migration after the PostcodeKey one has been defined.
.PrimaryKey(t => t.Zone_ID)
Unfortunately I'm still getting my error - I'm assuming this migration isn't used to build the EF 'model' in code, but I'm wondering if it thinks that there can only be a single entry with any given PostcodeKey which may explain the situation?
I'm posting a new question based on Linq Except not functioning as expected - duplicate items because I feel that enough has been discovered that the question is invalid, and Except is not the problem at all.
The problem I have is that I have a Linq Where clause that seems to be returning the wrong data. The data in my database looks like:
The class that represents this data has a compound key:
/// <summary>
/// Represents a mapping between a postcode and a zone
/// </summary>
[Table("NetC_EF_ZoneMapping")]
public class ZoneMapping
{
/// <summary>
/// Gets or sets the postcode identifier
/// </summary>
[Key]
public String PostcodeKey { get; set; }
/// <summary>
/// Gets or sets the Zone identifier
/// </summary>
[Key]
public Zone Zone { get; set; }
}
So I'm executing the following code, which results in different IDs:
var result = this.context.ZoneMappings.Include("Zone").Where(z => z.Zone.ID == 257 && z.PostcodeKey == "2214");
var result2 = new FreightContext().ZoneMappings.Include("Zone").Where(z => z.Zone.ID == 257 && z.PostcodeKey == "2214");
if (result.First().Zone.ID != result2.First().Zone.ID)
throw new InvalidOperationException();
The SQL (or ToString() for these two items is identical). So the only difference is that one is a new context, while the other has been passed in and used for some other stuff. The code that creates the context returning the wrong result is:
// Copy the contents of the posted file to a memory stream
using (StreamReader sr = new StreamReader(fileUpload.PostedFile.InputStream))
using (FreightContext context = new FreightContext())
{
// Attempt to run the import
ZoneMappingCSVImporter importer = new ZoneMappingCSVImporter(sr, context, System.Globalization.CultureInfo.CurrentUICulture);
var items = importer.GetItems().ToList();
importer.SaveItems(items);
this.successBox.Value = "Import completed and added " + items.Count() + " zones mappings.";
}
This then registers a ClassMap in the library that I'm using where:
public ZoneMappingCSVImporter(TextReader textReader, FreightContext context, CultureInfo culture)
: base(textReader, context, culture)
{
this.reader.Configuration.RegisterClassMap(new ZoneMappingMap(this.context));
}
I do a lookup using the context:
/// <summary>
/// Initializes a new instance of the <see cref="ZoneMap"/> class.
/// </summary>
public ZoneMappingMap(FreightContext context)
{
if (context == null)
throw new ArgumentNullException("context");
Map(m => m.PostcodeKey);
Map(m => m.Zone).ConvertUsing(row =>
{
// Grab the name of the zone then go find this in the database
String name = row.GetField<String>("Zone");
return context.Zones.Where(z => String.Compare(z.Name, name, true) == 0).FirstOrDefault();
});
}
I can't see anything strange going on here, I've verified the SQL generated by Entity Framework, verified the database is the same one - I can't understand why the wrong record would be returned. Can anyone shed any light on this?

The explanation for this problem is most likely the following:
Your way to define composite primary key in your entity is incorrect. With your current model EF only considers ZoneMapping.PostcodeKey as the primary key. More on this and how to fix it below.
You get a wrong result in the case when the corresponding context already contains the entity with PostcodeKey == "2214" and Zone_ID == 256 before you run your query. (I guess you never had a wrong result2.) When EF loads entities from the database it always looks after the query if an entity with the same key already exists in the context. If yes, the queried entity is thrown away and instead the attached entity is added to the result collection. In your case you are querying for PostcodeKey == "2214" and Zone_ID == 257. After the query EF picks the values of the primary key from the result row. But because EF "thinks" the primary key is only PostcodeKey == "2214" it searches for an attached entity with that key value and finds the entity with PostcodeKey == "2214", but Zone_ID == 256 and returns that as the result to you. You never get this problem with result2 because the corresponding context is new and empty, so EF will return the result that just has been loaded, not any older attached entity.
If you want to define a composite key you must have scalar properties for both key parts. You cannot use a navigation property like ZoneMapping.Zone as a key part. It means that you must have a property for your column Zone_ID in the model class (in addition to the navigation property Zone):
public class ZoneMapping
{
public String PostcodeKey { get; set; }
public int Zone_ID { get; set; }
public Zone Zone { get; set; }
}
Then in order to define a composite key with data annotations you must use the Column(Order = n) attribute as already shown in #Jonas Jämtberg's answer. You should also apply the ForeignKey attribute to Zone_ID because the underscore makes the property name "unconventional" so that EF won't detect it as FK by convention:
public class ZoneMapping
{
[Key, Column(Order = 0)]
public String PostcodeKey { get; set; }
[Key, ForeignKey("Zone"), Column(Order = 1)]
public int Zone_ID { get; set; }
public Zone Zone { get; set; }
}
The migration class that is generated by EF with this mapping should have a primary key definition that looks like this:
.PrimaryKey(t => new { t.PostcodeKey, t.Zone_ID })
...not like .PrimaryKey(t => t.PostcodeKey).PrimaryKey(t => t.Zone_ID) that you tried unsuccessfully to fix the problem.

Related

Microsoft.EntityFrameworkCore.DbUpdateConcurrencyException: 'Database operation expected to affect 1 row(s) but actually affected 2 row(s)

I received this error once I try to update table with the same value (carNumber), my condition is to update where the actual return date field is null.
For some reason it's look like the query return 2 rows, but actually there is only one. I'm using EF. This is the function:
the error - print screen
Code:
public void updateStatus(int carNumber1, string actualDate1)
{
DateTime accReturn = DateTime.Parse(actualDate1);
var orderCar1 = db.CarRentalFields.FirstOrDefault(carNum =>
(carNum.CarNumber == carNumber1 && carNum.ActualReturnDate == null));
orderCar1.ActualReturnDate = accReturn;
db.SaveChanges();
}
The error happens when trying to call db.saveChanges().
the table from the db, the car number is 1000 - print screen
modelBuilder.Entity pic
Please let me know how can I solve this issue.
problem solved by add a new column to the car_rental_fields table, id column that include Identity.
as I understand from here and from the web, there is an issue with complicated pk.
in my solution the id isn't a primary key, but it's make the logic for linq to update the correct column.
thanks' for all the people that involved in this issue.
This error occurs when EF cannot resolve the PK for your entity. In most cases for simple entities, EF conventions can work out the PK, but in your case you are using a composite key so this needs to be configured. Depending on how you are mapping your entities you can do this either in:
an EDMX
in the DbContext.OnModelCreating
using an EntityTypeConfiguration declaration
using attributes within the entity itself
Since we don't know how your entities are configured, you can verify this as the cause by using the attribute approach within your entity as a test. If you are using an EDMX the entity classes will be generated so you will want to replace this with configuration within the EDMX. (Cannot really help you there because I don't use the dang things :D )
You will probably have something like:
public class CarRentalFields
{
[Column("start_day")]
public DateTime StartDay { get; set; }
[Column("return_date")]
public DateTime ReturnDate { get; set; }
[Column("user_id")]
public int UserId { get; set; }
[Column("car_number")]
public DateTime CarNumber { get; set; }
// ... more columns...
}
You may even have a [Key] attribute on one of these fields, such as CarNumber. If there is a PK mapped in the entity the issue is that it isn't specific enough to uniquely identify the row. When EF goes to update one entity, it is checking for, and expecting to update only one row in the table. It's finding more than one row will be affected so it fails.
Append the attributes for the [Key] with the column order so it is recognized as a composite key.
public class CarRentalFields
{
[Key, Column(Name="start_day", Order=1)]
public DateTime StartDay { get; set; }
[Key, Column(Name="return_date", Order=2)]
public DateTime ReturnDate { get; set; }
[Key, Column(Name="user_id", Order=3)]
public int UserId { get; set; }
[Key, Column(Name="car_number", Order=4)]
public DateTime CarNumber { get; set; }
// ... more columns...
}
Provided these 4 columns are guaranteed to be a unique constraint on the table, EF will be satisfied when only one row is updated when it builds it's UPDATE SQL statement.
Note again that if this works and you are using an EDMX, you will need to review and modify your EDMX mapping to make the appropriate changes since that entity class could be regenerated, losing your extra attributes. (I believe the generated entity classes from an EDMX have a comment header warning you that it is a generated class, so that is an indicator to watch out for.)
Update:
My primary suspect in this would be that the table does not actually have a matching PK defined, either running a different PK combination, or more likely no PK given the nature of those fields. EF can operated on tables that have no PK defined, but it does require a Key definition that ensures records can be uniquely identified. The error you are seeing happens when that key definition is not unique enough. (I.e. if you are updating car 1, and selecting a row that has:
car_number = 1, start_day = 2021-11-21, return_day = 2021-11-22, user_id = 0 The issue is that more than one row has that combination in the DB. If the DB you are checking doesn't have more than one matching row then your application is almost certainly pointing at a different database than you are checking.
Things you can do to verify this:
get the runtime connection string and see if it matches the DB you are checking:
Before you run your query, add the following:
// EF6
var connectionString = db.Database.Connection.ConnectionString;
// EF Core 5
var connectionString = db.Database.GetConnectionString();
Have a look at the data you are actually querying:
.
var cars = db.CarRentalFields.Where(carNum =>
(carNum.CarNumber == carNumber1 && carNum.ActualReturnDate == null)).ToList();
While this query might return only 1 record, that is not the cause of the problem. What you want is the CarNumber, StartDate, ReturnDate, and UserId for this record:
var car = db.CarRentalFields
.Where(carNum => carNum.CarNumber == carNumber1
&& carNum.ActualReturnDate == null)
.Select(x => new
{
x.CarNumber,
x.StartDay,
x.ReturnDate,
x.UserId
}).Single(); // Expect our 1 record here...
var cars = db.CarRentalFields
.Where(x => x.CarNumber == car.CarNumber
&& x.StartDay == car.StartDay
&& x.ReturnDate == car.ReturnDate
&& x.UserId == car.UserId)
.ToList(); // Get rows that match our returned Key fields.
These queries select the assumed PK values for the car record you mean to update, then search cars for matching records with the expected Key fields. My money would be on that while the top query returns 1 record, the bottom query returns two rows, meaning while only 1 record has a #null ActualReturnDate value, your Key is not unique enough for the contents of this table.

Wrong column name weird issue in Entity Framework

I have been facing this problem some time, and to be honest I am myself confused with it so please excuse me if i don't succeed explaining it as I should.
I am trying to insert some data into a Table called CommunicationAttachment which is related as One to Many relationship with Communication; every communication could have many attachments.
The thing is that I get:
UpdateException: Invalid Column Name: "Communication_CommunicationId
when I try to insert list of attachments.
And please note that I am using the repository pattern but I even tried the normal way and the issue wasn't fixed.
I tried tracing the transaction that happens on the database and I figured out that it sends Communication_CommunicationId with the Insert statement, yet there is no such column. I am pretty sure I didn't send such a column.
Here is my code (this is happening when adding new Communication); first of all I call CasefileAttachments to make copies from them, and Communications are related to CaseFiles:
public List<CorrespondenceAttachment> GetCaseFileAttachments(List<Guid> CorrespondenceAttachmentIds)
{
List<CorrespondenceAttachment> originalAttachments = new List<CorrespondenceAttachment>();
foreach (var item in CorrespondenceAttachmentIds)
{
var attachment = QueryData.Query<CorrespondenceAttachment>().Where(att => att.CorrespondenceAttachmentID == item).FirstOrDefault();
originalAttachments.Add(attachment);
}
return originalAttachments;
}
Then I copy the CaseFileAttachments and create new objects of CommunicationAttachments :
public List<CommunicationAttachment> CopyCaseFileAttachmentsToCommunication(List<CorrespondenceAttachment> originalAttachments,Guid communicationId)
{
var communicationAttachments = new List<CommunicationAttachment>();
if (originalAttachments.Any())
{
foreach (var attachmentRef in originalAttachments)
{
var CommunicationAttachmentId = Guid.NewGuid();
communicationAttachments.Add(new CommunicationAttachment()
{
CommunicationAttachmentId = CommunicationAttachmentId,
DmsFileId = CommunicationAttachmentId,
CommunicationId = communicationId,
AttachmentTitle = attachmentRef.AttachmentTitle,
MimeType = attachmentRef.MimeType,
NewVersionID = null,
UploadDate = DateTime.Now,
Size = attachmentRef.Size,
Version = "0001",
AttachmentsGroupId = attachmentRef.AttachmentsGroupId,
DocumentId = attachmentRef.DocumentId,
RelativePath = attachmentRef.RelativePath,
Extension = attachmentRef.Extension,
AttachmentSubject = attachmentRef?.AttachmentSubject,
ExternalContactID = attachmentRef?.ExternalContactID,
AttachmentNumber = string.IsNullOrEmpty(attachmentRef?.AttachmentNumber) ? null : attachmentRef.AttachmentNumber,
TemplatedmsId = attachmentRef.TemplatedmsId,
State = eSense.Framework.Data.ObjectState.Added,
});
}
}
return communicationAttachments;
}
and the methods above are called something like this way:
public void AddNewCommunication(CommunicationDto communicationDto)
{
var communication = communicationDto
if (communicationDto.CommunicationAttachmentIdList.Any())
{
caseFileAttachments = GetCaseFileAttachments(communicationDto.CommunicationAttachmentIdList);
if (caseFileAttachments.Any())
{
commAttachments = CopyCaseFileAttachmentsToCommunication(caseFileAttachments, communication.CommunicationId);
}
}
communication.Attachments = commAttachments;
Save(communication)
}
So what could be the problem that I get a wrong column name?
Here is the relation between Communication and CommunicationAttachment
Note I added only the Important fields so don't bother if the declaring does not match the entity
Communication Entity:
public class Communication : BaseEntity
{
public Communication()
{
Attachments = new HashSet<CommunicationAttachment>();
}
[Key]
public Guid CommunicationId { get; set; }
public string Subject { get; set; }
public string CommunicationNumber { get; set; }
public virtual ICollection<CommunicationAttachment> Attachments { get; set; }
public DateTime DateCreated { get; set; }
public Guid? PreviousCommunicationId { get; set; }
[ForeignKey("PreviousCommunicationId")]
public virtual Communication PreviousCommunication { get; set; }
}
CommunicationAttachment Entity:
public class CommunicationAttachment : AttachmentBaseWithDelegation<Guid>
{
public override Guid PrimaryId
{
get
{
return this.CommunicationAttachmentId;
}
}
public CommunicationAttachment()
{
}
[Key]
public Guid CommunicationAttachmentId { get; set; }
private string _attachmentNumber;
public string AttachmentNumber { get; set; }
[ForeignKey("NewVersionID")]
public virtual CommunicationAttachment CaseFileAttachmentNewerVersion { get; set; }
public Guid CommunicationId { get; set; }
[ForeignKey("CommunicationId")]
public virtual Communication Communication { get; set; }
}
Sorry if you found it hard to understand my question I myself is confused!
Thanks in advance.
This is typically a case where a relationship between entities is not set up correctly. It would appear that EF should be resolving this relationship by convention if Communication's PK is "CommunicationId".
I notice that you've commented out a line to set the CommunicationId on the new entity:
//CommunicationId = communicationId,
What fields are in the CommunicationAttachment? is there a CommunicationId? Is there a Communication navigation property? What configuration settings are you are using?
For example, with fluent configuration I would have something like:
(CommunicationEntityConfiguration)
If CommunicationAttachment has a navigation property back to Communication and a FK field called CommunicationId...
HasMany(x => x.CommunicationAttachments)
.WithRequired(x => x.Communication)
.HasForeignKey(x => x.CommunicationId);
If the attachment entity has a navigation property without a mapped FK in the entity...
HasMany(x => x.CommunicationAttachments)
.WithRequired(x => x.Communication)
.Map(x => x.MapKey("CommunicationId"));
If the attachment entity does not have a navigation property, but has a FK in the entity...
HasMany(x => x.CommunicationAttachments)
.WithRequired()
.HasForeignKey(x => x.CommunicationId);
Or lastly if the attachment entity does not have a navigation property nor a mapped FK...
If the attachment entity does not have a navigation property, but has a FK in the entity...
HasMany(x => x.CommunicationAttachments)
.WithRequired()
.Map(x => x.MapKey("CommunicationId"));
I am a big fan of explicit mapping over convention as it is very clear as to what maps to what, and how, in order to resolve potential mapping conflicts. If the rest of the similar relations seem to be working and just this one is playing up, I'd be looking for possible typos in the field names. With a mapped collection like above, setting a Communcation.CommunicationAttachments.Add(attachment) should be setting the FK / related entity on the attachment without having to explicitly set the FK or related entity manually.
One additional note:
From your example I see you are setting Primary Keys manually client-side using Guid.NewGuid(). It is generally better to allow the database to manage PK generation and let EF manage FK assignment to ensure that related entities get the FKs to newly inserted rows automatically. Rather than SQL's NewId() or using Guid.NewGuid(), it is advisable to use sequential UUIDs. In SQL Server this is NewSequentialId(). For client-side setting, you can reproduce the sequential UUID pattern either with a system DLL call to get the ID, or a simple re-hash of the Guid bytes. see: Is there a .NET equalent to SQL Servers newsequentialid()
The GUIDs still carry the same uniqueness, the bytes are simply arranged to be more sequential and practical for database indexing to reduce page fragmentation. The downside is that IDs are more predictable. Depending on your database engine you might want to customize the algorithm based on whether the database is optimized for indexing on the lower-order or high-order bytes.
When using GUIDs for database, sequential or otherwise, you should ensure you have a scheduled index maintenance job on the database. With sequential IDs this job will run faster and keep the index tables more compact.

How do I make Entity Framework 6 (DB First) explicitly insert a Guid/UniqueIdentifier primary key?

I am using Entity Framework 6 DB First with SQL Server tables that each have a uniqueidentifier primary key. The tables have a default on the primary key column that sets it to newid(). I have accordingly updated my .edmx to set the StoreGeneratedPattern for these columns to Identity. So I can create new records, add them to my database context and the IDs are generated automatically. But now I need to save a new record with a specific ID. I've read this article which says you have to execute SET IDENTITY_INSERT dbo.[TableName] ON before saving when using an int identity PK column. Since mine are Guid and not actually an identity column, that's essentially already done. Yet even though in my C# I set the ID to the correct Guid, that value is not even passed as a parameter to the generated SQL insert and a new ID is generated by the SQL Server for the primary key.
I need to be able to both :
insert a new record and let the ID be automatically created for it,
insert a new record with a specified ID.
I have # 1. How can I insert a new record with a specific primary key?
Edit:
Save code excerpt (Note accountMemberSpec.ID is the specific Guid value I want to be the AccountMember's primary key):
IDbContextScopeFactory dbContextFactory = new DbContextScopeFactory();
using (var dbContextScope = dbContextFactory.Create())
{
//Save the Account
dbAccountMember = CRMEntity<AccountMember>.GetOrCreate(accountMemberSpec.ID);
dbAccountMember.fk_AccountID = accountMemberSpec.AccountID;
dbAccountMember.fk_PersonID = accountMemberSpec.PersonID;
dbContextScope.SaveChanges();
}
--
public class CRMEntity<T> where T : CrmEntityBase, IGuid
{
public static T GetOrCreate(Guid id)
{
T entity;
CRMEntityAccess<T> entities = new CRMEntityAccess<T>();
//Get or create the address
entity = (id == Guid.Empty) ? null : entities.GetSingle(id, null);
if (entity == null)
{
entity = Activator.CreateInstance<T>();
entity.ID = id;
entity = new CRMEntityAccess<T>().AddNew(entity);
}
return entity;
}
}
--
public class CRMEntityAccess<T> where T : class, ICrmEntity, IGuid
{
public virtual T AddNew(T newEntity)
{
return DBContext.Set<T>().Add(newEntity);
}
}
And here is the logged, generated SQL for this:
DECLARE #generated_keys table([pk_AccountMemberID] uniqueidentifier)
INSERT[dbo].[AccountMembers]
([fk_PersonID], [fk_AccountID], [fk_FacilityID])
OUTPUT inserted.[pk_AccountMemberID] INTO #generated_keys
VALUES(#0, #1, #2)
SELECT t.[pk_AccountMemberID], t.[CreatedDate], t.[LastModifiedDate]
FROM #generated_keys AS g JOIN [dbo].[AccountMembers] AS t ON g.[pk_AccountMemberID] = t.[pk_AccountMemberID]
WHERE ##ROWCOUNT > 0
-- #0: '731e680c-1fd6-42d7-9fb3-ff5d36ab80d0' (Type = Guid)
-- #1: 'f6626a39-5de0-48e2-a82a-3cc31c59d4b9' (Type = Guid)
-- #2: '127527c0-42a6-40ee-aebd-88355f7ffa05' (Type = Guid)
A solution could be to override DbContext SaveChanges. In this function find all added entries of the DbSets of which you want to specify the Id.
If the Id is not specified yet, specify one, if it is already specified: use the specified one.
Override all SaveChanges:
public override void SaveChanges()
{
GenerateIds();
return base.SaveChanges();
}
public override async Task<int> SaveChangesAsync()
{
GenerateIds();
return await base.SaveChangesAsync();
}
public override async Task<int> SaveChangesAsync(System.Threading CancellationToken token)
{
GenerateIds();
return await base.SaveChangesAsync(token);
}
GenerateIds should check if you already provided an Id for your added entries or not. If not, provide one.
I'm not sure if all DbSets should have the requested feature, or only some. To check whether the primary key is already filled, I need to know the identifier of the primary key.
I see in your class CRMEntity that you know that every T has an Id, this is because this Id is in CRMEntityBase, or in IGuid, let's assume it is in IGuid. If it is in CRMEntityBase change the following accordingly.
The following is in small steps; if desired you can create one big LINQ.
private void GenerateIds()
{
// fetch all added entries that have IGuid
IEnumerable<IGuid> addedIGuidEntries = this.ChangeTracker.Entries()
.Where(entry => entry.State == EntityState.Added)
.OfType<IGuid>()
// if IGuid.Id is default: generate a new Id, otherwise leave it
foreach (IGuid entry in addedIGuidEntries)
{
if (entry.Id == default(Guid)
// no value provided yet: provide it now
entry.Id = GenerateGuidId() // TODO: implement function
// else: Id already provided; use this Id.
}
}
That is all. Because all your IGuid objects now have a non-default ID (either pre-defined, or generated inside GenerateId) EF will use that Id.
Addition: HasDatabaseGeneratedOption
As xr280xr pointed out in one of the comments, I forgot that you have to tell entity framework that entity framework should not (always) generate an Id.
As an example I do the same with a simple database with Blogs and Posts. A one-to-many relation between Blogs and Posts. To show that the idea does not depend on GUID, the primary key is a long.
// If an entity class is derived from ISelfGeneratedId,
// entity framework should not generate Ids
interface ISelfGeneratedId
{
public long Id {get; set;}
}
class Blog : ISelfGeneratedId
{
public long Id {get; set;} // Primary key
// a Blog has zero or more Posts:
public virtual ICollection><Post> Posts {get; set;}
public string Author {get; set;}
...
}
class Post : ISelfGeneratedId
{
public long Id {get; set;} // Primary Key
// every Post belongs to one Blog:
public long BlogId {get; set;}
public virtual Blog Blog {get; set;}
public string Title {get; set;}
...
}
Now the interesting part: The fluent API that informs Entity Framework that the values for primary keys are already generated.
I prefer fluent API avobe the use of attributes, because the use of fluent API allows me to re-use the entity classes in different database models, simply by rewriting Dbcontext.OnModelCreating.
For example, in some databases I like my DateTime objects a DateTime2, and in some I need them to be simple DateTime. Sometimes I want self generated Ids, sometimes (like in unit tests) I don't need that.
class MyDbContext : Dbcontext
{
public DbSet<Blog> Blogs {get; set;}
public DbSet<Post> Posts {get; set;}
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
// Entity framework should not generate Id for Blogs:
modelBuilder.Entity<Blog>()
.Property(blog => blog.Id)
.HasDatabaseGeneratedOption(DatabaseGeneratedOption.None);
// Entity framework should not generate Id for Posts:
modelBuilder.Entity<Blog>()
.Property(blog => blog.Id)
.HasDatabaseGeneratedOption(DatabaseGeneratedOption.None);
... // other fluent API
}
SaveChanges is similar as I wrote above. GenerateIds is slightly different. In this example I have not the problem that sometimes the Id is already filled. Every added element that implements ISelfGeneratedId should generate an Id
private void GenerateIds()
{
// fetch all added entries that implement ISelfGeneratedId
var addedIdEntries = this.ChangeTracker.Entries()
.Where(entry => entry.State == EntityState.Added)
.OfType<ISelfGeneratedId>()
foreach (ISelfGeneratedId entry in addedIdEntries)
{
entry.Id = this.GenerateId() ;// TODO: implement function
// now you see why I need the interface:
// I need to know the primary key
}
}
For those who are looking for a neat Id generator: I often use the same generator as Twitter uses, one that can handle several servers, without the problem that everyone can guess from the primary key how many items are added.
It's in Nuget IdGen package
I see 2 challenges:
Making your Id field an identity with auto generated value will prevent you from specifying your own GUID.
Removing the auto generated option may create duplicate key exceptions if the user forgets to explicitly create a new id.
Simplest solution:
Remove auto generated value
Ensure Id is a PK and is required
Generate a new Guid for your Id in the default constructor of your models.
Example Model
public class Person
{
public Person()
{
this.Id = Guid.NewGuid();
}
public Guid Id { get; set; }
}
Usage
// "Auto id"
var person1 = new Person();
// Manual
var person2 = new Person
{
Id = new Guid("5d7aead1-e8de-4099-a035-4d17abb794b7")
}
This will satisfy both of your needs while keeping the db safe. The only down side of this is you have to do this for all models.
If you go with this approach, I'd rather see a factory method on the model which will give me the object with default values (Id populated) and eliminate the default constructor. IMHO, hiding default value setters in the default constructor is never a good thing. I'd rather have my factory method do that for me and know that the new object is populated with default values (with intention).
public class Person
{
public Guid Id { get; set; }
public static Person Create()
{
return new Person { Id = Guid.NewGuid() };
}
}
Usage
// New person with default values (new Id)
var person1 = Person.Create();
// Empty Guid Id
var person2 = new Person();
// Manually populated Id
var person3 = new Person { Id = Guid.NewGuid() };
I don't think there is a real answer for this one...
As said here How can I force entity framework to insert identity columns? you can enable the mode #2, but it'll break #1.
using (var dataContext = new DataModelContainer())
using (var transaction = dataContext.Database.BeginTransaction())
{
var user = new User()
{
ID = id,
Name = "John"
};
dataContext.Database.ExecuteSqlCommand("SET IDENTITY_INSERT [dbo].[User] ON");
dataContext.User.Add(user);
dataContext.SaveChanges();
dataContext.Database.ExecuteSqlCommand("SET IDENTITY_INSERT [dbo].[User] OFF");
transaction.Commit();
}
you should change value of StoreGeneratedPattern property of identity column from Identity to None in model designer.
Note, changing of StoreGeneratedPattern to None will fail inserting of object without specified id
As you can see, you're no longer able to insert without setting by yourself an ID.
But, if you look on the bright side : Guid.NewGuid() will allow you to make a new GUID without the DB generation function.
The solution is: write your own insert query. I've put together a quick project to test this, so the example has nothing to do with your domain, but you'll ge the ideea.
using (var ctx = new Model())
{
var ent = new MyEntity
{
Id = Guid.Empty,
Name = "Test"
};
try
{
var result = ctx.Database.ExecuteSqlCommand("INSERT INTO MyEntities (Id, Name) VALUES ( #p0, #p1 )", ent.Id, ent.Name);
}
catch (SqlException e)
{
Console.WriteLine("id already exists");
}
}
The ExecuteSqlCommand returns "rows affected" (in this case 1), or throws exception for duplicate key.

Entity Framework with Nullable Guid? causing Validation Error

I am trying to use Nullable Guid in Entity Framework but when i tried to commit the change i am getting validation message due to foreign key is null, I have below domain class, DomainType Class has child DomainCustomField :-
DomainType.cs
public class DomainType
{
public Guid? Id
{
get;
set;
}
private IList<DomainCustomField> _domainCustomFields;
public string Name
{
get;
set;
}
public Guid? AccountId
{
get;
set;
}
public virtual IList<DomainCustomField> DomainCustomFields
{
get
{
return this._domainCustomFields;
}
set
{
this._domainCustomFields = value;
}
}
}
DomainCustomField.cs
public class DomainCustomField
{
public Guid? Id
{
get;
set;
}
public string Name
{
get;
set;
}
public Guid? DomainTypeId
{
get;
set;
}
}
below is mapping :-
modelBuilder.Domain<DomainCustomField>().HasKey((DomainCustomField p) => new
{
p.Id,
p.DomainTypeId
});
modelBuilder.Domain<DomainCustomField>()
.Property(p => p.Id)
.HasDatabaseGeneratedOption(DatabaseGeneratedOption.Identity);
modelBuilder.Domain<DomainType>()
.HasMany(p => p.DomainCustomFields)
.WithRequired()
.HasForeignKey<Guid?>(x => x.DomainTypeId);
below is sample code which i tried to insert.
DomainType domainType = new DomainType()
{
AccountId = customer.Id
};
domainType.DomainCustomFields.Add(new DomainCustomField()
{
Name = "Test"
});
Workspace.Add<DomainType>(domainType);
Workspace.CommitChanges();
Error :- validation error for DomainTypeId is required
in the same code if i change Guid? to Guid in ID variable then its work correctly. I don't know how to handle nullable Guid. do i need to manually set parent key in child reference? then how it works fine in case of Guid?
Note :- I am using using Guid because i am working with distributed database, where id can generate from multiple source system.
I am using Guid? because by default Guid generate 00000000-0000-0000-0000-000000000000 for empty Guid which can grow database size.
The issue has nothing to do with Guid type (the same will happen with int), but the discrepancy between the nullability of the property type used in the entity and the actual nullability of the property implied by the model.
First, EF requires PKs to be non nullable. Hence DomainType.Id, DomainCustomField.Id and DomainCustomField.DomainTypeId are effectively non nulable (it's unclear why you have include DomainTypeId in the PK of the DomainCustomField which already have unique database generated Id, but that's another story).
Second, DomainCustomField.DomainTypeId is effectively non nullable because you have specified that - the WithRequired() part of the FK relationship configuration.
Now, EF does its best to handle these discrepancies. If you look at the generated migration, you will see that the corresponding database table columns of all the fields in question are non nullable. However (nothing is perfect, you know) some part of the EF code is failing to handle that discrepancy and hence the exception.
With that being said, always keep the model properties nullability in sync with their actual nullability. Which in you case is to make both 3 properties non nullable (i.e. of type Guid).
P.S. What about the note I am using Guid? because by default Guid generate 00000000-0000-0000-0000-000000000000 for empty Guid which can grow database size., I have no idea what do you mean by that. The nullablitity of the class property has nothing to do with the database size, and as I already mentioned, the corresponding database table columns for such setup are non nullable anyway.
Also note that in your example you forget to set the Id of the new DomainType, which you should because it's not auto generated.
I'll take a slightly different approach in my answer and try and tell you what to change in order to get a working example;
Like someone has pointed out, unique identifiers must not be nullable so the Id field in DomainType should a Guid. Also you will need to change the Id field in DomainCustomField. These may need an empty constructor just to create a new Guid.
Secondly, the foreign key in your other object Guid? DomainTypeId is perfectly fine, this can stay, but if it is staying you will need to change your configuration.
In this block you have specified that the property is required?
modelBuilder.Domain<DomainType>()
.HasMany(p => p.DomainCustomFields)
.WithRequired()
.HasForeignKey<Guid?>(x => x.DomainTypeId);
So simply make it optional;
modelBuilder.Domain<DomainType>()
.HasMany(p => p.DomainCustomFields)
.WithOptional()
.HasForeignKey<Guid?>(x => x.DomainTypeId);
That should solve your issues. Any questions let me know :)
You can use a standard identifier for the database. GUID? stored as a field in the table.

Error occurred while saving entities that do not expose foreign key properties / datetime [duplicate]

I have a simple code in Entity Framework (EF) v4.1 code first:
PasmISOContext db = new PasmISOContext();
var user = new User();
user.CreationDate = DateTime.Now;
user.LastActivityDate = DateTime.Now;
user.LastLoginDate = DateTime.Now;
db.Users.Add(user);
db.SaveChanges();
user.Avatar = new Avatar() { Link = new Uri("http://myUrl/%2E%2E/%2E%2E") };
db.SaveChanges();
db.Users.Add(new User() { Avatar = new Avatar() { Link = new Uri("http://myUrl/%2E%2E/%2E%2E") } });
db.SaveChanges();
The problem is that I get an error
An error occurred while saving entities that do not expose foreign key
properties for their relationships. The EntityEntries property will
return null because a single entity cannot be identified as the source
of the exception. Handling of exceptions while saving can be made
easier by exposing foreign key properties in your entity types. See
the InnerException for details.
at
db.Users.Add(new User() { Avatar = new Avatar() { Link = new Uri("http://myUrl/%2E%2E/%2E%2E") } });
db.SaveChanges();
I don't understand why the similar operation works. Is there something wrong with my model, or with ef-code-first?
public class Avatar
{
[Key]
public int Id { get; set; }
[Required]
public string LinkInString { get; set; }
[NotMapped]
public Uri Link
{
get { return new Uri(LinkInString); }
set { LinkInString = value.AbsoluteUri; }
}
}
public class User
{
[Key]
public int Id { get; set; }
public string UserName { get; set; }
public string Email { get; set; }
public string Password { get; set; }
public Avatar Avatar { get; set; }
public virtual ICollection<Question> Questions { get; set; }
public virtual ICollection<Achievement> Achievements { get; set; }
public DateTime CreationDate { get; set; }
public DateTime LastLoginDate { get; set; }
public DateTime LastActivityDate { get; set; }
}
For those of you who would still have this error with all keys properly defined, have a look at your entities and make sure you don't leave a datetime field with a null value.
This error message can be thrown for any kind of reason. The 'InnerException' property (or its InnerException, or the InnerException of that, etc) contains the actual primary cause of the problem.
It would of course be useful to know something about where the problem occurred - which object(s) in the unit of work is causing the problem? The exception message would normally tell you in the 'EntityEntries' property, but in this case, for some reason, that can't be done. This diagnostic complication - of the 'EntityEntries' property being empty - is apparently because some Entities 'do not expose foreign key properties for their relationships.'
Even if the OP gets the error because of failing to initialize DateTimes for the second instance of User, they get the diagnostic complication - 'EntityEntries' being empty, and a confusing top-level message ... because one of their Entity's doesn't 'expose foreign key properties'. To fix this, Avatar should have a public virtual ICollection<User> Users { get; set; } property defined.
The issue was resolved by adding an FK property.
In my case the following situation was giving me the same Exception:
Imagine a code first EF model where you have a Garage entity that has a collection of Car entities. I needed to remove a car from the garage so I ended up with code that looked like this:
garageEntity.Cars.Remove(carEntity);
Instead, it should've been looked like this:
context.Cars.Remove(carEntity);
Just for others who might have similar problems. I had the same error, but for a different reason. In one of the child objects I defined the [Key] as being a value which was the same for different saves. A stupid mistake on my part, but the error message does not instantly lead you to the problem.
In my case the exeception was thrown because EF had created a migration incorrectly.
It missed setting the identity: true on the second table. So go into the migrations which created the relevant tables and check if it missed to add identity.
CreateTable(
"dbo.LogEmailAddressStats",
c => new
{
Id = c.Int(nullable: false, identity: true),
EmailAddress = c.String(),
})
.PrimaryKey(t => t.Id);
CreateTable(
"dbo.LogEmailAddressStatsFails",
c => new
{
Id = c.Int(nullable: false), // EF missed to set identity: true!!
Timestamp = c.DateTime(nullable: false),
})
.PrimaryKey(t => t.Id)
.ForeignKey("dbo.LogEmailAddressStats", t => t.Id)
.Index(t => t.Id);
An Id column should have identity (i.e. auto-incrementing!) so this must be a EF bug.
You could add identity manually with SQL directly to the database but I prefer using Entity Framework.
If you run in to the same problem I see two easy solutions:
Alt 1
reverse the incorrectly created migration with
update-database -target:{insert the name of the previous migration}
Then add the identity: true manually to the migration code and then update-database again.
Alt 2
you create a new migration that adds identity. If you have no changes in the models and you run
add-migration identity_fix
it will create an empty migration. Then just add this
public partial class identity_fix : DbMigration
{
public override void Up()
{
AlterColumn("dbo.LogEmailAddressStatsFails", "Id", c => c.Int(nullable: false, identity: true));
}
public override void Down()
{
AlterColumn("dbo.LogEmailAddressStatsFails", "Id", c => c.Int(nullable: false));
}
}
This problem can also arise from reversed key declarations. If you're using fluent to configure the relationship, make sure the left and right keys are mapped to the correct entity.
I hade same probleme. in my case, it was due to datetime field with a null value. I had to passe a value to datetime and evrythings went fine
Another answer:
I used this:
public List<EdiSegment> EdiSegments { get; set; }
instead of this:
public virtual ICollection<EdiSegment> EdiSegments { get; set; }
and got the error message noted above.
I had the same error and in my case the problem was that I added a relationship object which had already been loaded "AsNoTracking". I had to reload the relation property.
BTW, Some suggest using "Attach" for relations that already exist in db, I haven't tried that option though.
In my case, the problem was that I renamed a column improperly, so the migration made two columns, one called "TeamId" and one called "TeamID". C# cares, SQL doesn't.
Yet another different case here.
A query was cast to a list and while doing that, it created entities by their constructor for comparison in the linq expression right after the ToList(). This created entities that gotten into the deleted state after the linq expression finished.
However! There was a small adjustment that created another entity in the constructor, so that this new entity got linked to an entity that was marked as Deleted.
Some code to illustrate:
query.Except(_context.MyEntitySetSet()
.Include(b => b.SomeEntity)
.Where(p => Condition)
.ToList() // This right here calls the constructor for the remaining entities after the where
.Where(p => p.Collection.First(b => Condition).Value == 0)
.ToList();
The constructor of MyEntity:
public partial class MyEntity
{
protected MyEntity()
{
// This makes the entities connected though, this instance of MyEntity will be deleted afterwards, the instance of MyEntityResult will not.
MyEntityResult = new MyEntityResult(this);
}
}
My solution was to make sure the entire expression was done inside the IQueryable so that there won't be any objects created.
I'm not entirely sure that it's going to help in your case because I'm setting up my tables using Fluent API, however, as far I can tell, the issue arises regardless whether the schema is set up using data annotations (attributes) or Fluent API (configuration).
There seems to be a bug in EF (v. 6.1.3) as it omits certain changes to the schema when updating the DB to the next migration. The quickest route around it is (during the development stage) to remove all the tables from the DB and runt migrations from init stage again.
If you're already in production, the quickest solution I've found was to manually change the schema in the DB or, if you want to have version control of the changes, manually manipulate the methods Up() and Down() in your migration.
Today I faced this issue and tried the possible solutions posted above but none of them helped me. I had UnitOfWork pattern implemented and system was committing the data in last after adding all the records.
In my case system was combining the two models and querying the DB
Invalid object name 'dbo.RoleModelUserModel'.
where these were two different models actually.
I fixed this by reordering the insert statements and adding the parent entity first. In this case added the user first and issue resolved.
After a bit of investigation I found that whilst .Net supports a minimum date (DateTime.MinValue) of 01/01/0001 00:00:00 and a maximum (DateTime.MaxValue) of 31/12/9999 23:59:59 in SQL Server Compact Edition minimum date is 01/01/1753 00:00:00.
When I entered a date greater than 01/01/1753 00:00:00, this error disappeared.
Is your application or website being accessed from some third party application when this error is coming? If yes, then please check the access rights of the account which is sending the request to your application.
In our case, it was ServiceNow MID server service which was the culprit. It is a Windows service. If you want to know more about it then please read this link. So basically, you need to check two things:
Under the context of which account the calling service should run to access your application?
What all access rights are needed for the service's log on account to do all allowed operations in your application?
As per this article of ServiceNow we had to give Log on as a service right to the MID Server service's log on account. You can do it via in Local Security Policies console (Refer screenshot).
After we gave the proper access rights to the logon account, the Entity Framework issue went away. Please remember that the access rights and the log on account to be used will be specific to your application.

Categories

Resources