Deleting Child Entries when deleting parent - c#

I've read around various posts but none seem to match my issue.
I need to delete child entries linked to a foreignkey when the parent is deleted.
Currently I have this code:
public async Task UpdateLineItemByOrderLineId(int orderLineId, int newQuantity)
{
var clientBasket = await GetBasketAsync();
var lineItem = clientBasket.OrderLines.FirstOrDefault(x => x.OrderLineId == orderLineId);
if (newQuantity == 0)
{
_orderLinesRepository.Delete(lineItem);
_orderLinesRepository.Save();
}
and this linked to the following repository
public class OrderLinesRepository : GenericRepository<OrderLine>, IOrderLinesRepository
{
public OrderLinesRepository(IDbContextFactory dbContextFactory)
: base(dbContextFactory)
{
}
}
Posts seem to mention entity framework etc and being as I'm learning C# from a solution handed to me to bring to completion I don't see matching code that reflects EF.
I don't necessarily need to delete the child elements but simply set the ForeignKey to null. One thing to note is that I can have multiple child entries linked to the ForeignKey.
What is the correct implementation to achieve the above?
Edit: Foreign Key Assignment
namespace TSW.Ecommerce.Data.Api
{
public class OrderDelegate
{
[Key]
public int OrderDelegateId { get; set; }
[ForeignKey("OrderLineId")]
public virtual OrderLine OrderLine { get; set; }
public int? OrderLineId { get; set; }
public string FirstName { get; set; }
public string LastName { get; set; }
public string Email { get; set; }
}
}

Correct solution is:
foreach (var m in lineItem.DelegatesList.Where(f=>f.OrderLineId == orderLineId))
{
lineItem.DelegatesList.Remove(m);
}

Related

Add child entities to database and assign them to parent object in database Entity Framework Core

I have a datamodel named "Document" which looks like this:
public class Document
{
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public int Id { get; set; }
//[ForeignKey("Id")]
public List<DocumentLine> Lines { get; set; }
public DateTime PurchaseDate { get; set; }
public double TotalValue { get; set; }
}
It contains a list of the child object "DocumentLines" which looks like this:
public class DocumentLine
{
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public int Id { get; set; }
public int LineId { get; set; }
public int DocumentId { get; set; }
//public virtual Document ParentDocument { get; set; }
public string ItemName { get; set; }
public double ItemPrice { get; set; }
public double ItemId { get; set; }
public int Quantity { get; set; }
}
I created a repository for each DataModel in my solution, so I have a documentRepository and a documentLinesRepository.
Currently I am facing several errors while creating a new document which contains a list of its child documentLines.
If i want to create a new document in the database the following code is executed first:
public async Task<Document> CreateNewAsync()
{
Document document = new Document()
{
Lines = null,
PurchaseDate = DateTime.Now,
};
var newDocument = await databaseContext.Documents.AddAsync(document);
await databaseContext.SaveChangesAsync();
return newDocument.Entity;
}
And this code works as it should, but now if I want to create a new object of type DocumentLine and add it to the database with the following code:
public async Task<DocumentLine> CreateNewAsync()
{
try
{
DocumentLine documentLine = new DocumentLine();
var newDocumentLine = await databaseContext.DocumentLines.AddAsync(documentLine);
await databaseContext.SaveChangesAsync();
return newDocumentLine.Entity;
}
catch(Exception ex)
{
return null;
}
}
I get the following error in the line where the "documentLine" should be added to the database:
"SQLite Error 19: 'FOREIGN KEY constraint failed'."
First of all I don't see where a foreign key is set for the instance of type "DocumentLine".
I tried several other things, as well as assigning a instance of the parent of type "Document" in each DocumentLine, but this got me an error as well.
How it should work
My question is how I could properly implement the behavior that a document is saved in the database and each line got saved seperatly (from the documentLinesRepository) and only at the end when all lines are set, the document will be updated in the database containing a full list of DocumentLines objects.
Sorry in advance if this is a beginner question, but I tried to solve this error for a few days until now and I didn't found the right solution with google atm.
"First of all I don't see where a foreign key is set for the instance of type "DocumentLine"."
Well, then first of all - check that. In the actual database created by EF. You're most probably gonna find an FK on DocumentId in your DocumentLines table.
Your Document and DocumentLines are in a 1toMany relationship - a DocumentLine makes no sense without a Document as a parent. So you can't insert an orphaned DocumentLine to the database. Uncomment the
public virtual Document ParentDocument { get; set; }
and assign the parent before inserting:
public async Task<DocumentLine> CreateNewAsync(Document parent)
{
try
{
DocumentLine documentLine = new DocumentLine();
documentLine.ParentDocument = parent;
var newDocumentLine = await databaseContext.DocumentLines.AddAsync(documentLine);
await databaseContext.SaveChangesAsync();
return newDocumentLine.Entity;
}
catch(Exception ex)
{
return null;
}
}
This tells EntityFramework "Hey, these two are related." EF then will figure out correct relationship values in the database by itself.
Tell me if it fixes the issue, as all is dependent on the initial guess of FK on DocumentId.

Return children using a Where clause

I'm using the SQL-NET Extensions in my Xamarin project. I am trying to return children emelments of my model using a where clause. Using the example models on the website:
public class Stock
{
[PrimaryKey, AutoIncrement]
public int Id { get; set; }
[MaxLength(8)]
public string Symbol { get; set; }
[OneToMany(CascadeOperations = CascadeOperation.All)] // One to many relationship with Valuation
public List<Valuation> Valuations { get; set; }
}
public class Valuation
{
[PrimaryKey, AutoIncrement]
public int Id { get; set; }
[ForeignKey(typeof(Stock))] // Specify the foreign key
public int StockId { get; set; }
public DateTime Time { get; set; }
public decimal Price { get; set; }
[ManyToOne] // Many to one relationship with Stock
public Stock Stock { get; set; }
}
I can succesfully return a specific item with the children populated using:
var storedValuation = db.GetWithChildren<Valuation>(valuation.Id);
However I can't work out how to do it using a Where clause instead of Get. I have tried:
var results = db.Table<Valuation>().Where(x=>x.Price > 5.0m).ToList();
This returns with all the Stock parameters being null. I could then loop through each result and set them, but I assume there must be a better way to do it in the original query?
You can obtain the relationships for any object calling GetChildren method:
var results = db.Table<Valuation>().Where(x=>x.Price > 5.0m).ToList();
foreach (var element in results) {
conn.GetChildren(element);
}
There's also a convenience method for querying the database called GetAllWithChildren that performs the same operation in a less verbose way:
var results = conn.GetAllWithChildren<Valuation>(x => x.Price > 5.0m).ToList();
Please take into account that you cannot access relationships in this query as they would require a JOIN that is not being performed. For simple queries like this it should work as expected.

Recursive Entity Update

I have an entity which holds a list of entities (same as root entity) to represent a Folder structure:
public class SopFolder
{
public int Id { get; set; }
public string Name { get; set; }
public DateTime? LastUpdated { get; set; }
public int Status { get; set; }
public virtual ICollection<SopField> SopFields { get; set; }
public virtual ICollection<SopFolder> SopFolderChildrens { get; set; }
public virtual ICollection<SopBlock> Blocks { get; set; }
public virtual ICollection<SopReview> Reviews { get; set; }
}
This entity is stored in my DB using Code-First Approach which is working fine. I then print the entity to a KendoUI Treeview, let the user modify it and on "save" post it back to the Server to an Action as IEnumerable<TreeViewItemModel> items.
I then look for the ROOT entity with all it's children (there is only one root) and convert it back into an SopFolder object.
To get the full object updated in the database I do the following:
List<SopFolder> sopfolderlist = ConvertTree(items.First());
SopFolder sopfolder = sopfolderlist[0];
if (ModelState.IsValid)
{
SopFolder startFolder = new SopFolder { Id = sopfolder.Id };
//db.SopFolders.Attach(startFolder);
// db.SopFolders.Attach(sopfolder);
startFolder.Name = sopfolder.Name;
startFolder.LastUpdated = sopfolder.LastUpdated;
startFolder.SopFields = sopfolder.SopFields;
startFolder.SopFolderChildrens = sopfolder.SopFolderChildrens;
startFolder.Status = sopfolder.Status;
db.Entry(startFolder).State = EntityState.Modified;
db.SaveChanges();
return Content("true");
}
However this is not working. The model is not updated at all. If I shift the "entityState.Modified" before the modifications, it just creates a complete fresh duplicate of my data in the database (modified of course).
Is my approach correct or do I have to go a different path? What am I missing here? I guess there is another "hidden" id which lets the EF map the entities to the db entries but I am not sure about this. Thanks for help!
UPDATE:
Instead of creatinga new instance of SopFolder I also tried db.SopFolders.Find(sopfolder.Id) and this works for entries with no children. If I have entities with children, it creates a duplicate.
Regards,
Marcus
This is typical Disconnected Graph scenario. Please see this question for possible solutions:
Disconnected Behavior of Entity Framework when Updating Object Graph
You have already figure out the first solution - that is: update entities separately. Actually, what you should do is to fetch the original data from database and then do comparison of what have changed. There are some generic ways of doing that, some of them are described in "Programming EF DbContext" book by J.Lerman, which I strongly recommend to you before doing more coding using EF.
P.S. IMHO this is the worse downside of EF.
Replace SopFolder startFolder = new SopFolder { Id = sopfolder.Id }; with
SopFolder startFolder = db.SopFolders.FirstOrDefault(s=>s.Id.Equals(sopfolder.Id));
// then validate if startFolder != null
I recommend you to create your entity model with ParentId, not children object list. When you need treeview model collect it with recursive function from database.
public class SopFolder
{
public int Id { get; set; }
public string Name { get; set; }
public DateTime? LastUpdated { get; set; }
public int Status { get; set; }
public virtual ICollection<SopField> SopFields { get; set; }
//public virtual ICollection<SopFolder> SopFolderChildrens { get; set; }
public int? ParentFolderId { get; set; }
public virtual ICollection<SopBlock> Blocks { get; set; }
public virtual ICollection<SopReview> Reviews { get; set; }
}
When you create children folders, select it's parent, so collect your data. In childrens case try this :
List<SopFolder> sopfolderlist = ConvertTree(items.First());
SopFolder sopfolder = sopfolderlist[0];
if (ModelState.IsValid)
{
SopFolder startFolder = new SopFolder { Id = sopfolder.Id };
//db.SopFolders.Attach(startFolder);
// db.SopFolders.Attach(sopfolder);
startFolder.Name = sopfolder.Name;
startFolder.LastUpdated = sopfolder.LastUpdated;
startFolder.SopFields = sopfolder.SopFields;
startFolder.SopFolderChildrens = sopfolder.SopFolderChildrens;
foreach (var child in sopfolder.SopFolderChildrens)
{
db.SopFolders.CurrentValues.SetValues(child);
db.SaveChanges();
}
startFolder.Status = sopfolder.Status;
db.Entry(startFolder).State = EntityState.Modified;
db.SaveChanges();
return Content("true");
}

Many to many relation - have I done it right?

The idea is pretty simple. I have a list of tags. When I create a question I want to add some tags to it.
Models:
public class QuestionModel
{
public int Id { get; set; }
public String Content { get; set; }
public ICollection<TagModeltoQuestionModel> Tags { get; set; }
[NotMapped]
public ICollection<TagModel> AssignedTags { get { return Tags.Select(x => x.Tag).ToList(); } }
public int UserId { get; set; }
}
public class QuestionViewModel // helper - not in database
{
public QuestionModel Model { get; set; }
public ICollection<TagModel> Tags { get; set; }
}
public class TagModel
{
public int Id { get; set; }
public String Name { get; set; }
public ICollection<TagModeltoQuestionModel> Questions { get; set; }
[NotMapped]
public bool Assigned { get; set; }
[NotMapped]
public ICollection<QuestionModel> AssignedQuestions { get { return Questions.Select(x => x.Question).ToList(); } }
}
public class TagModeltoQuestionModel // many to many
{
[Key, Column(Order = 0)]
public int TagId { get; set; }
[Key, Column(Order = 1)]
public int QuestionId { get; set; }
public virtual QuestionModel Question { get; set; }
public virtual TagModel Tag { get; set; }
}
Controller:
[HttpPost]
public ActionResult Edit(QuestionViewModel questionViewModel)
{
if (ModelState.IsValid)
{
_repo.Update(questionViewModel.Model, questionViewModel.Tags); // see repo code below
return RedirectToAction("Index");
}
return View(questionViewModel.Model);
}
Repo:
public void Update(QuestionModel entity, ICollection<TagModel> tags)
{
AssignTags(entity, tags);
Db.Attach(entity);
Db.SaveChanges();
}
private void AssignTags(QuestionModel entity, ICollection<TagModel> tags)
{
tags = tags.Where(x => x.Assigned).ToArray(); // remove unassigned comming form View --> Controller
var linkedTags =
Db.TagsToQuestions.Where(x => x.QuestionId == entity.Id);
var linkedTagsIds = linkedTags.Select(x => x.TagId);
var selectedTagsIds = tags.Select(x => x.Id);
var oldTags = linkedTags.Where(x => !selectedTagsIds.Contains(x.TagId));
var newTags = tags.Where(x => !linkedTagsIds.Contains(x.Id)).Select(x=> new TagModeltoQuestionModel{QuestionId=entity.Id,TagId=x.Id});
foreach (var t in oldTags)
Db.Delete(t);
foreach (var t in newTags)
Db.Add(t);
Db.SaveChanges();
}
This works fine, though I'm not sure if this is the right way to go (in fact I implemented the whole many-to-many logic myself). Is there a smarter way to let EF do the job for me? I dug through a bunch of tutorials, but none of them worked for me.
Additionally I feel that AssignTags method could be written in a better way, so any comments concerning that also appreciated.
EDIT
According to haim770's answer I simplified the model the way he suggested.
My controller now looks like that:
public void Update(QuestionModel entity, ICollection<TagModel> tags)
{
Db.Attach(entity);
//these lines give the same result
//var ids = tags.Select(y => y.Id).ToArray();
//entity.Tags = Db.Tags.Where(x => ids.Contains(x.Id)).ToArray();
tags.ForEach(x => Db.Attach(x));
entity.Tags = tags;
Db.SaveChanges();
}
SaveChanges results in 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.
inner:
{"A duplicate value cannot be inserted into a unique index. [ Table name = TagModelQuestionModels,Constraint name = PK_TagModelQuestionModels ]
So how to implement it correctly?
You do not need the TagModeltoQuestionModel class. You could model many-to-many relations like this:
public class QuestionModel
{
//....
public ICollection<TagModel> Tags { get; set; }
}
public class TagModel
{
//....
public ICollection<QuestionModel> Questions { get; set; }
}
Question holds a reference to many Tags and each Tag holds a reference to many Questions.
The whole point of Entity Framework (like any other ORM) is to spare you from having to model your objects and their relations in a database-like way but rather let you model it in a pure Object Oriented way then letting the ORM do the 'dirty work' of intermediate-tables, foreign keys etc...

NHibernate: (Fluent) Mapping / Querying based upon a Getter that accesses an already mapped collection property

I don't know how to phrase this properly. I'm working with a pre-existing domain, where certain entities can contain children that are versioned. The children are responsible for their own version number, but ultimately, this version number only makes sense in context of the attached parent entity.
public class Blog
{
public virtual IList<VersionedItem> VersionedItems { get; set; }
public virtual CurrentVersionedItem {
get {
return VersionedItems.OrderByDescending(x => x.Version).FirstOrDefault();
}
}
}
public class VersionedItem
{
public virtual Blog { get;set; }
public virtual int Version { get; set; }
public virtual string Content { get; set; }
public virtual int SomeNumber { get; set; }
}
And what I'd like to achieve:
var blogs = Session.Query<Blog>(x=> x.CurrentVersionedItem.SomeNumber == 5)
While the IQueryable provider of NHibernate is forgiving, I will not eat everything. Is there a way to define a (fluent) mapping that resolves the "CurrentVersionedItem" property properly?
I'm also aware of the fact CurrentVersionedItem could potentially return null in this scenario (if it worked in the first place).
Why won't you do like this:
var item = session.Query<VersionedItem>().FirstOrDefault(q => q.SomeNumber == 5);
Blog blog;
if (item != null)
blog = item.Blog;

Categories

Resources