C# EntityCode First : Unwanted foreign key created - c#

I have a table of "Statements" that is related to both :
a table of "Transactions" (with a foreign key on IdStatement)
a table of "SpecificTransactions". The class "SpecificTransaction" inherits from the table "Transaction"
This is the existing code First Model :
Table Statement
[Serializable]
[Table("Statement", Schema = "dbo")]
public class Statement
{
public Statement()
{
this.Transactions = new List<Transaction>();
this.SpecificTransactions = new List<SpecificTransaction>();
}
[Key, DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public int IdStatement { get; set; }
public List<Transaction> Transactions { get; set; }
public List<SpecificTransaction> SpecificTransactions { get; set; }
[... Other properties ...]
}
Table Transaction
[Serializable]
[Table("Transaction", Schema = "dbo")]
public class Transaction
{
[Key, DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public int IdTransaction { get; set; }
[ForeignKey("Statement")]
public Nullable<int> IdStatement { get; set; }
[... Other properties ...]
}
Table SpecificTransaction (inherits from Transaction)
[Serializable]
[Table("SpecificTransaction", Schema = "dbo")]
public class SpecificTransaction : Transaction
{
[StringLength(255)]
public string UniqueValue { get; set; }
[StringLength(50)]
public string Status { get; set; }
[StringLength(50)]
public string Hash { get; set; }
[... Other properties ...]
}
When the database is being created, an unwanted field (i.e column) is added on my table SpecificTransaction, called "Statement_IdStatement".
When I try to add my Statement containing SpecificTransactions, none of the following columns :
Table Transaction > Column IdStatement
Table SpecificTransaction > Column Statement_IdStatement
Is being populated by the IdStatement, I have to add the Id afterwards.
Furthermore, when I assign the "IdStatement" on my table "SpecificTransaction", only the "Statement_IdStatement" column is filled, and not the "IdStatement" of my table Statement.
Is there a way to :
avoid the creation of the column "Statement_IdStatement" and keeping my EF Code First class that way ? (or at least, have no change on the tables Statement and Transaction)
When I add my Statements containing SpecificTransactions and I saveChanges, the column "Transaction" > "IdStatement" is automatically populated ?
Thanks in advance.

You are getting the extra column Statement_IdStatement because you have declared a second relationship, directly between the Statement and SpecificTransaction model types, namely, by the property public List<SpecificTransaction> SpecificTransactions on your Statement class. This is enough to cause Entity Framework to create an extra table column to map that one-to-many relationship to, and that's why it gets filled. It doesn't need to be mapped to an extra foreign-key model property for this to occur.
You probably don't actually need that extra property on Statement. You can actually insert SpecificTransaction objects into the Transactions list property, and the mapping will be taken care of for you.
You may have declared this property in order to be able to access only the SpecificTransaction objects for your Statement. If this was your intent, you can alternatively access them using myStatement.Transactions.OfType<SpecificTransaction>().

Related

Entity Framework Core: remove a relationship but not delete the entities

How would you delete a relationship assuming you had the 2 entities, but did not have the 'relationship' entity?
Assuming the following entities...
Model classes:
public class DisplayGroup
{
[Key]
public int GroupId { get; set; }
public string Description { get; set; }
public string Name { get; set; }
public ICollection<LookUpGroupItem> LookUpGroupItems { get; set; }
}
public class DisplayItem
{
[Key]
public int ItemId { get; set; }
public string Description { get; set; }
public string FileType { get; set; }
public string FileName { get; set; }
public ICollection<LookUpGroupItem> LookUpGroupItems { get; set; }
}
public class LookUpGroupItem
{
public int ItemId { get; set; }
public DisplayItem DisplayItem { get; set; }
public int GroupId { get; set; }
public DisplayGroup DisplayGroup { get; set; }
}
Here is the code for deleting a relationship. Note: I do not want to delete the entities, they just no longer share a relationship.
public void RemoveLink(DisplayGroup g, DisplayItem d)
{
_dataContext.Remove(g.LookUpGroupItems.Single(x => x.ItemId == d.ItemId));
}
The method above causes an error:
System.ArgumentNullException occurred
Message=Value cannot be null.
It looks like this is the case because LookUpGroupItems is null, but these were called from the database. I would agree that I do not want to load all entity relationship objects whenever I do a Get from the database, but then, what is the most efficient way to do this?
Additional NOTE: this question is not about an argument null exception. It explicitly states how to delete a relationship in Entity Framework Core.
The following is not the most efficient, but is the most reliable way:
public void RemoveLink(DisplayGroup g, DisplayItem d)
{
var link = _dataContext.Find<LookUpGroupItem>(g.GroupId, d.ItemId); // or (d.ItemId, g.GroupId) depending of how the composite PK is defined
if (link != null)
_dataContext.Remove(link);
}
It's simple and straightforward. Find method is used to locate the entity in the local cache or load it the from the database. If found, the Remove method is used to mark it for deletion (which will be applied when you call SaveChanges).
It's not the most efficient because of the database roundtrip when the entity is not contained in the local cache.
The most efficient is to use "stub" entity (with only FK properties populated):
var link = new LookUpGroupItem { GroupId = g.GroupId, ItemId = d.ItemId };
_dataContext.Remove(link);
This will only issue DELETE SQL command when ApplyChanges is called. However it has the following drawbacks:
(1) If _dataContext already contains (is tracking) a LookUpGroupItem entity with the same PK, the Remove call will throw InvalidOperationException saying something like "The instance of entity type 'LookUpGroupItem' cannot be tracked because another instance with the key value 'GroupId:1, ItemId:1' is already being tracked. When attaching existing entities, ensure that only one entity instance with a given key value is attached."
(2) If database table does not contain a record with the specified composite PK, the SaveChanges will throw DbUpdateConcurrencyException saying "Database operation expected to affect 1 row(s) but actually affected 0 row(s). Data may have been modified or deleted since entities were loaded. See http://go.microsoft.com/fwlink/?LinkId=527962 for information on understanding and handling optimistic concurrency exceptions." (this behavior is actually considered a bug by many people including me, but this is how it is).
Shorty, you can use the optimized method only if you use short lived newly create DbContext just for that operation and you are absolutely sure the record with such PK exists in the database. In all other cases (and in general) you should use the first method.

Self referencing foreign key

I'm using Entity Framework Code-First to rebuild an application that used to run from an Access database. One of the requirements is that the new data schema should be auditable, that is it should show who created a record and who updated it and when etc.
I've created a base Entity class as follows:
public class Entity
{
public int Id { get; set; }
public int CreatedByUserId { get; set; }
public int? UpdatedByUserId { get; set; }
public virtual User CreatedBy { get; set; }
public virtual User UpdatedBy { get; set; }
}
Then I created a class that inherits from EntityTypeConfiguration as follows
public class BaseEntityTypeConfiguration<T> : EntityTypeConfiguration<T> where T : Entity
{
Property(e => e.Id).HasColumnName(typeof(T).Name + "Id");
HasRequired(e => e.CreatedBy).WithMany().HasForeignKey(e => e.CreatedById);
HasOptional(e => e.UpdatedBy).WithMany().HasForeignKey(e => e.UpdatedById);
}
Now I create configurations that inherit from BaseEntityTypeConfiguration for the rest of my business classes that inherit from my Entity class.
The problem comes when I try to make my User class inherit from entity as follows:
public class User : Entity
{
public string Username { get; set; }
// etc
}
I'll be adding a "ghost" user for records where the evidence isn't there to determine who created the record, but this ghost user will essentially be created by itself.
I'm getting the following error from Entity Framework when I try to add this ghost user:
Unable to determine a valid ordering for dependent operations. Dependencies may exist due to foreign key constraints, model requirements or store-generated values.
There may be problems in my domain model that could be causing this error, but my theory is that it's down to this user that's trying to create itself in this instance.
Is having a self-referencing foreign key constraint problematic?
Your PK is an identity column and you're setting the ghost user's CreatedByUser property with itself. This causes a chicken/egg scenario - you need the User.Id value as the User.CreatedById value to insert the record into the DB table, but you don't know what User.Id is until after the record is inserted.
If you can be sure of the identity's seed value (EF seems to default to 1), you can set the CreatedByUserId property to that value instead of CreatedByUser.
Otherwise, create your ghost user by executing a SQL statement allowing you to manually set the Id and CreatedByUserId fields to the same value then reseed the identity to Id + 1.
Example of the former:
public class UserWithCreatedBy
{
[Key]
[DatabaseGenerated( DatabaseGeneratedOption.Identity )]
public int Id { get; set; }
public int CreatedById { get; set; }
[ForeignKey( "CreatedById" )]
public UserWithCreatedBy CreatedBy { get; set; }
}
static void Main( string[] args )
{
using( var db = new TestContext() )
{
var u = new UserWithCreatedBy();
// doesn't work with identity
//u.CreatedBy = u;
// this will work as long as you know what the identity seed is
// (whatever the next identity value will be)
u.CreatedById = 1;
db.UsersWithCreatedBy.Add( u );
db.SaveChanges();
}
}

Updating Graph with One-to-One relation

I have a class Ricevuta which holds a collection of VoceRicevuta:
public partial class Ricevuta : GestPreBaseBusinessObject
{
[Key, DatabaseGenerated(DatabaseGeneratedOption.None)]
public long Id { get; set; }
...
[InverseProperty("Ricevuta")]
public virtual ObservableListSource<VoceRicevuta> Voci { get; set; }
}
A VoceRicevuta holds optional references to different classes, let's consider only the one to Prestazione class:
public partial class VoceRicevuta
{
[Key, DatabaseGenerated(DatabaseGeneratedOption.None)]
public long Id { get; set; }
public long IdRicevuta { get; set; }
....
public virtual Prestazione Prestazione { get; set; }
[ForeignKey("IdRicevuta")]
public virtual Ricevuta Ricevuta { get; set; }
}
Ok, the problem now is the following: I create an Instance of Prestazione (let's call it PreInst) and save it to the database. I detach PreInst from that DbContext instance.
Then, I pass PreInst to another form (another DbContext - I must do that), create a Ricevuta, a VoceRicevuta (added to the just created Ricevuta) and assign PreInst to VoceRicevuta Prestazione property. I also made some changes to PreInst.
Now I want to save to database the new Ricevuta, the new VoceRicevuta, its relation to PreInst and the changes to PreInst.
I run the following:
db.UpdateGraph(Ricevuta, map =>
map.OwnedCollection(ric => ric.Voci,
with => with
.OwnedEntity(voce => voce.Prestazione)
));
But i get the error:
"Violation of PRIMARY KEY constraint 'PK_dbo.Prestazioni'. Cannot insert duplicate key in object 'dbo.Prestazioni'. Vlue of duplicate key: (12115)"
I can't understand why! Isn't Graph diff purpose cheching if any part of the graph is already present in Db and behave consquently?
I tried to attach PreInst to DbContext before running the code above and saving. Now the error thrown is:
Multiplicity constraint violated. The role 'Prestazione_VoceRicevuta_Source' of the relationship 'Gestione_Prestazioni.Prestazione_VoceRicevuta' has multiplicity 1 or 0..1.
Any hint?

Need to add extra column for processing

I have table called Customer with
CustomerID
Name
Salary etc.
I have added Customer table object to dbml, now on top of Customer table columns I need to add IsProcessed column.
I have added it but it throws exception while retrieving data as "invalid column IsProcessed"
Do i need to create separate POCO object and add extra column
Later fill in the new list with POCO object from db list.
Any alternative solution? Please advise
You can extend class generated from DBML by creating partial class in new file :
public partial class Customer
{
public bool IsProcessed { get; set; }
}
put codes above in new class file and set it's namespace the same as your DBML generated Customer class.
This is common pattern to be able to extend generated class without worrying the extension codes overridden if DBML file regenerated.
[For Reference]
If the models get out of sync with the database and saving the EDMX file and running the “Update Model from Database…” feature doesn’t work, then consider this link
http://blog.jongallant.com/2012/08/entity-framework-manual-update.html#.Uwrul_mSzkA
public class CustomerData
{
public int CustomerID { get; set; }
public string Name { get; set; }
public double Salary { get; set; }
public bool IsProcessed { get; set; }
}
LINQ query:
List<CustomerData> GetData()
{
var data = from cus in context.Customer
select new CustomerData{
CustomerID = cus.CustomerID,
Name = cus.Name,
Salary = cus.Salary
IsProcessed = Your custom field data
};
return data.ToList();
}

Insert entity with related entity in TPH architecture

I need to insert an entity (Picture) that holds a related entity (Ad) based on TPH architecture:
Picture model:
public class Picture
{
// Primary properties
public int Id { get; set; }
public string Name { get; set; }
}
public class AdPicture : Picture
{
public Ad Ad { get; set; }
}
Ad model:
public class Ad
{
// Primary properties
public int Id { get; set; }
public string Title { get; set; }
}
public class AdCar : Ad
{
public int? CubicCapacity { get; set; }
public int? Power { get; set; }
}
I want to insert a new Picture in the AdCar, and I tried:
AdPicture picture = new AdPicture()
{
Ad = _adRepository.GetById(adId),
Filename = newFileName
};
_pictureService.CreateAdPicture(picture);
CreateAdPicture:
public void CreateAdPicture(AdPicture adPicture)
{
_adPictureRepository.Add(adPicture);
_adPictureRepository.Save();
}
But Entity Framework says
*Invalid column name 'AdCar_Id'.*
When I check the SQL command text, I can see
insert [dbo].[Pictures]([Name], [Filename], [URL], [Rank], [Discriminator], [PictureType_Id], [AdCar_Id], [Ad_Id])
values (null, #0, null, #1, #2, #3, #4, null)
It's putting AdCar_Id and Ad_Id, why? How can I insert the Picture related with the AdCar?
First up: You may need to define the key for the subclass explicitly to be Ad_Id as it is assuming the AdCar_Id (or vice-versa).
Something like this in your DbContext:
modelBuilder.Entity<AdPicture>()
.HasRequired(o => o.Ad)
.WithMany().Map(f => f.MapKey("Ad_Id"));
Second: Also, just to check - all of these classes have completely separate tables (TPH) and not tables with just the additional properties specified (TPH) I.e. an AdPicture does not have data in both a Picture and AdPicture table?
Having not done TPH (only TPT) I am unsure of if Entity Framework handles the subclass-ing differently, but I suspect it must. Someone else can hopefully answer that better if you are missing something there.
I found the problem:
I was defining the Navigation property "public IList Pictures { get; set; }" in the Ad Model that was creating an aditional field in the SQL Command "Ad_Id".
As soon as I removed it, the problem was solved and all the theory I had about TPH came togheter again :)
Thanks for your feedback.

Categories