c# How to Recursively delete objects and its children - c#

This is my function that gives me a list of nodes based on its parentId:
public List<RepositoriesListViewModel> GetChildren(string ParentId)
{
List<RepositoriesListViewModel> data = new List<RepositoriesListViewModel>();
List<RepositoriesListViewModel> initialData = new List<RepositoriesListViewModel>();
var List = dbContext.Repositories.ToList();
foreach (var item in List)
{
initialData.Add(new RepositoriesListViewModel
{
id = item.Id,
parent = item.ParentId,
ApplicationsId = item.ApplicationsId,
text = item.Name,
Path = item.Path,
CreatedOn = item.CreatedOn
});
};
foreach (RepositoriesListViewModel child in initialData.Where(x => x.parent == ParentId))
{
child.Children = GetChildren(child.id);
data.Add(child);
}
return data;
}
I was wondering if it were possible to delete an item and its children using this function as well? Where would I add my delete call?
This is what my delete call looks like:
public void Delete(string Input)
{
try
{
var repo = Get(Input);
dbContext.Repositories.Remove(repo);
dbContext.SaveChanges();
logger.LogInformation(LoggingGlobals.Delete + " Repository: " + repo.Name);
}
catch (Exception e)
{
logger.LogError(e, "Failed to delete Repository");
}
}

It seems you want something like this:
public void Delete(string Input)
{
try
{
var children = GetChildren(Input);
foreach(var child in children)
{
Delete(child.Id);
}
var repo = Get(Input);
dbContext.Repositories.Remove(repo);
dbContext.SaveChanges();
logger.LogInformation(LoggingGlobals.Delete + " Repository: " + repo.Name);
}
catch (Exception e)
{
logger.LogError(e, "Failed to delete Repository");
}
}
So Before you delete the item itself, you first delete its children and their children.

Related

read csv file and return indented menu c#

I have to create an indented navigation menu using below data from a .csv file:
ID;MenuName;ParentID;isHidden;LinkURL1;Company;NULL;False;/company2;About Us;1;False;/company/aboutus3;Mission;1;False;/company/mission4;Team;2;False;/company/aboutus/team5;Client 2;10;False;/references/client26;Client 1;10;False;/references/client17;Client 4;10;True;/references/client48;Client 5;10;True;/references/client510;References;NULL;False;/references
Using this data I have to develop an application that will parse the file and present the content in a console as the example below:
. Company.... About Us....... Team.... Mission. References.... Client 1.... Client 2
Menu items should be indented (depending on the parent), hidden items (isHidden==true) shouldn't be presented and items should be ordered alphabetically. So far I tried:
using (StreamReader sr = new StreamReader(#"file.csv"))
{
// Read the stream to a string, and write the string to the console.
string [] lines = sr.ReadToEnd().Split(/*';', */'\n');
for (int i = 1; i < lines.Length; i++)
{
Console.WriteLine($"String no {i} is : {lines[i-1]}");
}
}
With this i'm getting the lines but I'm stuck after that. I'm new in coding so any help will be appreciated :)
heres some code that should help you get off.
Working sample:
https://dotnetfiddle.net/L37Gjr
It first parses the data to a seperate object. This then gets used to build a m-ary tree, or a hierachical structure of connected nodes. (a node has a reference to 0 or more children).
https://en.wikipedia.org/wiki/M-ary_tree
Then tree traversal (use google if you need to know more) is used to insert and print the output, There is still something wrong however. it now uses level order traversal to print, this however comes up with an error:
Found root:1 - Company
Found root:10 - References
-------------------
1 - Company
2 - About Us
3 - Mission
4 - Team
10 - References
6 - Client 1
5 - Client 2
As you can see, it prints 4 - Team on the wrong level. I'll leave it to you to fix it (because i ran out of time), and if not i hope i gave you plenty ideas to go off and research on your own.
// sample for https://stackoverflow.com/questions/61395486/read-csv-file-and-return-indented-menu-c-sharp by sommmen
using System;
using System.Collections;
using System.Linq;
using System.Collections.Generic;
public class Program
{
public class Node<T>
{
public T Data {get;set;}
public List<Node<T>> Children { get; set;}
public Node()
{
Children = new List<Node<T>>();
}
// Tree traversal in level order
public List<Node<T>> LevelOrder()
{
List<Node<T>> list = new List<Node<T>>();
Queue<Node<T>> queue = new Queue<Node<T>>();
queue.Enqueue(this);
while(queue.Count != 0)
{
Node<T> temp = queue.Dequeue();
foreach (Node<T> child in temp.Children)
queue.Enqueue(child);
list.Add(temp);
}
return list;
}
public List<Node<T>> PreOrder()
{
List<Node<T>> list = new List<Node<T>>();
list.Add(this);
foreach (Node<T> child in Children)
list.AddRange(child.PreOrder());
return list;
}
public List<Node<T>> PostOrder()
{
List<Node<T>> list = new List<Node<T>>();
foreach (Node<T> child in Children)
list.AddRange(child.PreOrder());
list.Add(this);
return list;
}
}
public class Entity
{
public int id {get;set;}
public string menuName {get;set;}
public int? parentID {get;set;}
public bool isHidden {get;set;}
public string linkURL {get;set;}
}
public static void Main()
{
var data = #"ID;MenuName;ParentID;isHidden;LinkURL
1;Company;NULL;False;/company
2;About Us;1;False;/company/aboutus
3;Mission;1;False;/company/mission
4;Team;2;False;/company/aboutus/team
5;Client 2;10;False;/references/client2
6;Client 1;10;False;/references/client1
7;Client 4;10;True;/references/client4
8;Client 5;10;True;/references/client5
10;References;NULL;False;/references";
var lines = data.Split('\n');
var rootNodes = new List<Node<Entity>>();
var childItems = new List<Entity>();
// Parse the data to entities
// Items without a parent are used as rootnodes to build a tree
foreach(var row in lines.Skip(1))
{
var columns = row.Split(';');
var id = Convert.ToInt32(columns[0]);
var menuName = columns[1];
var parentID = ToNullableInt(columns[2]);
var isHidden = Convert.ToBoolean(columns[3]);
var linkURL = columns[4];
var entity = new Entity()
{
id = id,
menuName = menuName,
parentID = parentID,
isHidden = isHidden,
linkURL = linkURL
};
if(parentID == null)
{
Console.WriteLine("Found root:" + entity.id + " - " + entity.menuName);
rootNodes.Add(new Node<Entity>()
{
Data = entity
});
}
else
{
childItems.Add(entity);
}
}
// Add the childElements to their appropriate rootnode
foreach(var rootNode in rootNodes)
{
foreach(var childItem in childItems.OrderBy(a=>a.parentID).ThenBy(b=>b.menuName))
{
var newNode = new Node<Entity>()
{
Data = childItem
};
Insert(rootNode, newNode);
}
}
Console.WriteLine("-------------------");
foreach(var rootNode in rootNodes)
{
var indent = 0;
var previous = rootNode;
foreach(var node in rootNode.LevelOrder())
{
if(node.Data.isHidden) continue;
if(previous.Data.parentID != node.Data.parentID)
indent++;
for(var i = 0; i < indent; i++)
Console.Write("\t");
Console.WriteLine(node.Data.id + " - " + node.Data.menuName);
previous = node;
}
}
}
public static void Insert(Node<Entity> rootNode, Node<Entity> targetNode)
{
foreach(var current in rootNode.LevelOrder())
{
if(current.Data.id == targetNode.Data.parentID)
{
current.Children.Add(targetNode);
return;
}
}
}
public static int? ToNullableInt(string s)
{
int i;
if (int.TryParse(s, out i)) return i;
return null;
}
}

Avoid inserting duplicates in sql table through Entity Framework with multiple workers

I have a SQL table called Domain which contains the following columns:
I have multiple agents (workers) inserting rows into the database at the same time and I want to avoid inserting duplicate domains. The Id is the primary key and I not willing to change it.
Meanwhile Im checking first if the domain exist by this:
public async Task<List<DomainApiModel>> GetListOfExistingDomainsAsync(List<string> domains)
{
using (eTrafficBacklinks_V2Entities EMME_Context = new eTrafficBacklinks_V2Entities())
{
var rec = await EMME_Context.Domains.Where(p => domains.Contains(p.DomainName)).ToListAsync();
return rec.Select(p => new DomainApiModel(p)).ToList();
}
}
Then I filter the ones already existent and, finally, I insert the non existing by the following code:
public int Create(List<DomainApiModel> domainApiList, out List<DomainApiModel> domainApiListWithId, int chunkSizeLimit = 500)
{
using (eTrafficBacklinks_V2Entities EMME_Context = new eTrafficBacklinks_V2Entities())
{
EMME_Context.Configuration.AutoDetectChangesEnabled = false;
EMME_Context.Configuration.ValidateOnSaveEnabled = false;
int totalChanges = 0;
var listOfLists = domainApiList.ChunkSplit(chunkSizeLimit).ToList();
var listOfDomainData = new List<Domain>();
foreach (var list in listOfLists)
{
foreach (var apiModel in list)
{
var objectData = apiModel.GetDataObject();
EMME_Context.Domains.Add(objectData);
listOfDomainData.Add(objectData);
}
totalChanges += EMME_Context.SaveChanges();
}
domainApiListWithId = listOfDomainData.Select(d => new DomainApiModel(d)).ToList();
return totalChanges;
}
}
The problem is that in the meantime between checking if the domain exists and creating, another agent can insert the same domain and I am having duplicates in my table.
Anyone has any light of how to solve this problem?
Obs: I have the same problem with a table called page with the "url column" which is nvarchar950 type, so creating just a unique index is not a solution...
This problem can be easily solved adding a unique index to the table. Any attempt to add a duplicate value will throw an exception.
CREATE UNIQUE INDEX UX_DOMAIN_NAME
ON DOMAIN (DOMAIN_NAME)
Beware than it will require you to add each new value independently or the whole transaction will fail, even for those non duplicated values.
foreach (var list in listOfLists)
{
foreach (var apiModel in list)
{
var objectData = apiModel.GetDataObject();
EMME_Context.Domains.Add(objectData);
listOfDomainData.Add(objectData);
try{
totalChanges += EMME_Context.SaveChanges();
}
catch(SqlException se){
if(se.Number != 2601) // Unique key violation
{
// Handle other errors
}
}
}
}
Add an additional Index on all columns that need to be unique. Columns that are to big to be indexed can be indexed by a computed hash.
Here a concept for creating many entities and how to track the exceptions:
class MyService
{
public async Task<OperationResult<string, SomeEntity>> CreateManyAsync( IList<string> data, int chunkSize )
{
var succeded = new List<SomeEntity>( );
var failed = new List<FailedOperation<string>>( );
foreach ( var chunk in data.Select( ( dataItem, index ) => new { data = dataItem, chunk = index % chunkSize } ).GroupBy( c => c.chunk, c => c.data ) )
{
try
{
succeded.AddRange( await InternalCreateManyAsync( chunk ) );
continue;
}
catch ( Exception )
{
// we just eat this exception
}
foreach ( var singleItem in chunk )
{
try
{
succeded.Add( await InternalCreateSingleAsync( singleItem ) );
}
catch ( Exception ex )
{
failed.Add( new FailedOperation<string>( singleItem, ex ) );
}
}
}
return new OperationResult<string, SomeEntity> {
Succeded = succeded,
Failed = failed,
};
}
private async Task<IList<SomeEntity>> InternalCreateManyAsync( IEnumerable<string> data )
{
var result = new List<SomeEntity>( );
using ( var db = new MyCOntext( ) )
{
foreach ( var item in data )
{
result.Add( AddSingleToContext( item, db ) );
}
await db.SaveChangesAsync( );
}
return result;
}
private async Task<SomeEntity> InternalCreateSingleAsync( string data )
{
using ( var db = new MyContext( ) )
{
var e = AddSingleToContext( data, db );
await db.SaveChangesAsync( );
return e;
}
}
private SomeEntity AddSingleToContext( string data, MyContext context )
{
var entity = new SomeEntity { Data = data, };
context.SomeEntities.Add( entity );
return entity;
}
}
some utility classes
class SomeEntity
{
public int Id { get; set; }
public string Data { get; set; }
}
class FailedOperation<T>
{
public FailedOperation( T data, Exception error )
{
Data = data;
Error = error;
}
public T Data { get; }
public Exception Error { get; }
}
class OperationResult<TSource, TResult>
{
public IList<TResult> Succeded { get; set; }
public IList<FailedOperation<TSource>> Failed { get; set; }
}

Remove duplicate rows mvc

I have this method
Meeting is a class
Attendees is an ICollection in Meeting
Class
public partial class Meeting
{
public Meeting()
{
this.Attendees = new List<Attendees>();
}
public virtual ICollection<Attendees> Attendees{ get; set; }
[...]
Method Controller
private void RemoveRowsDuplicated(Meeting model)
{
if (model.Attendees != null)
{
foreach (var item in model.Attendees.GroupBy(x => x.UserName).Select(y => y.Last()))
{
context.Attendees.Remove(item);
}
}
}
The objective is remove duplicate Attendees with the same username in the table.
But the current method it deletes all records and keeps the duplicate
Where am I going wrong?
Correct version of your method will look like this:
private static void RemoveRowsDuplicated(Meeting model)
{
if (model.Attendees != null)
{
var duplicates = new List<Attendees>();
foreach (var item in model.Attendees.GroupBy(x => x.UserName).Where(x=>x.Count()>1))
{
duplicates.AddRange(item.Skip(1));
}
duplicates.ForEach(x=>context.Attendees.Remove(x));
}
}
You can try writing raw SQL and invoking via EF and return Attendees objects in a list.
var query = "Select * from Attendees group by username";
var attendeesList = dbContext.Database.SqlQuery<Attendees>(query).ToList<Attendees>();
As I can see you grouped elements by name and remove last item. So you remove unique elements.
Like this
private void RemoveRowsDuplicated(Meeting model)
{
if (model.Attendees != null)
{
var temporaryAtendees = new List<Attendees>();
foreach(var item in model.Attendees)
{
if (temporaryAtendees.Contains(item))
{
context.Attendees.Remove(item);
}
else
{
temporaryAtendees.Add(item);
}
}
}
}

EntityValidationErrors Reporting Incorrectly

I have a case where entities.SaveChanges() is throwing a DbEntityValidationException and stating
The AValue field is required", however the AValue property has been populated.
Is there something else that can be checked to determine why this is throwing this error.
The basic code throwing the error:
try
{
var entities = new MyEntities();
var item = (from i in entities.Item
where i.Id == 1
select i).First();
item.AValue = "NewValue";
entities.SaveChanges();
}
catch (DbEntityValidationException exception)
{
var report = exception.Message();
}
I've done a bit of digging and in the catch have put:
var x = item.AValue; // and it shows "NewValue"
and
var message = new StringBuilder();
foreach (var entityValidationError in exception.EntityValidationErrors)
{
foreach (var validationError in entityValidationError.ValidationErrors)
{
object value = null;
try { value = entityValidationError.Entry.CurrentValues.GetValue<object>(validationError.PropertyName); }
catch (System.Exception exception) { value = null; }
message.AppendLine(validationError.PropertyName + ": " + validationError.ErrorMessage + " (VALUE: " + (value ?? "-") + ")");
}
}
return message.ToString();
// and i get "AValue: The AValue field is required. (VALUE: -) so that value item is null
I'm pretty stumped at this point, just can't figure out why it looks fine during the try but still throws a validation error message. Is there anything else I can add to see if there is something else going on there? Or any other recommended troubleshooting steps?
Please verify that the Item.AValue is set up properly in the Entity Framework dataset definition so that it is pointing to the AValue column in the relevant database table. If for some reason this is missing, then setting it in code would not cause it to be updated in the Db, which could lead to this error.
Your code contains some small syntax mistakes:
var item = (from i in entities.Item
where i.Id = 1
select i).First();
should be (double == sign)
var item = (from i in entities.Item
where i.Id == 1
select i).First();
Also:
item.AValue = "NewValue"
should be followed by a ";" sign.
I've recreated a small example that works:
Object class:
public class Item
{
[Key]
public int Id { get; set; }
[Required]
public string Name { get; set; }
}
Entity Framework context class:
public class Context : DbContext
{
public DbSet<Item> items { get; set; }
public Context()
: base("Demo")
{
}
}
On the first run, adds an item in the database:
using (var ctx = new Context())
{
Item stud = new Item() { Name = "Name" };
ctx.items.Add(stud);
ctx.SaveChanges();
}
Now, replace the code above with the following code:
try
{
var entities = new Context();
var item = (from i in entities.items
where i.Id == 1
select i).First();
item.Name = "NewValue";
entities.SaveChanges();
}
catch (DbEntityValidationException exception)
{
}
It seems that the issue was that there was a sub-object on my item that also had a property of AValue and this was the one that was throwing the exception.
Is there some way (via the DbEntityValidationException) to report on what object it is as well?

how to write a recursive function for delete nested folder

I have got a task to delete the hierarchy of folders. When i am deleting a folder, the respective sub folders should be deleted.
public ActionResult DeleteLabel(int id)
{
var query = dbPanAgroDMSContext.LabelMaster.Where(x => x.ParentLabelId == id).ToList();
foreach(var item in query)
{
var query1 = dbPanAgroDMSContext.LabelMaster.Where(x => x.ParentLabelId == item.LabelId).ToList();
dbPanAgroDMSContext.LabelMaster.Remove(item);
foreach (var i in query1)
{
dbPanAgroDMSContext.LabelMaster.Remove(i);
}
}
LabelMaster label = dbPanAgroDMSContext.LabelMaster.Find(id);
dbPanAgroDMSContext.LabelMaster.Remove(label);
dbPanAgroDMSContext.SaveChanges();
return Json(new { Result = "OK" });
}
Instead of using repeated for loops I want to do it in a single block of code.Please help me to create a linq query?
Try this
public ActionResult DeleteLabel(int id)
{
Delete(id);
dbPanAgroDMSContext.SaveChanges();
return Json(new { Result = "OK" });
}
private void Delete(int id)
{
//For given id get all child ones first
var query = dbPanAgroDMSContext.LabelMaster.Where(x => x.ParentLabelId == id).ToList();
foreach(var item in query)
{
//for each child ,delet its' childs by calling recursively
Delete(item.Id);
}
LabelMaster label = dbPanAgroDMSContext.LabelMaster.Find(id);
dbPanAgroDMSContext.LabelMaster.Remove(label);
}

Categories