InvalidConstraintException when clearing parent table in C# - c#

Maybe I just don't see the forest for the trees, but currently I'm faced with the following situation that puzzles me:
I have a typed data set (not connected to a database), which contains two tables: Orders and Lines. Each entry in the Orders table represents an order and, surprisingly, each entry in the Lines table represents one line for an order. There can be multiple lines for each order.
There is a relation between the Lines and the Orders table connecting them via the order ID. The relation is configured to be a relation as well as a foreign-key constraint, with both the update rule and the delete rule being set to "Cascade".
I'd have expected that clearing the Orders table via set.Orders.Rows.Clear(); would also remove the respective entries from the Lines table - however I'm getting an InvalidConstraintException saying I can't do that because there are entries in the Lines table associated with entries in the Orders table.
I can of course work around this by first clearing the Lines table before clearing the Orders table, but I'm still puzzled as to why the Cascade-rule is not applied in this case.

Not sure why the Clear() shows the error it does with those constraints, but the intended functionality happens when you try to Remove a row. With that in mind, an extension method like this does the trick:
public static void RemoveAll(this DataTable table)
{
for (int index = table.Rows.Count - 1; index >= 0; index--)
{
table.Rows.RemoveAt(index);
}
}
Use case:
Transactions dataset = new Transactions();
dataset.Orders.AddOrderRow("1");
dataset.Orders.AddOrderRow("2");
dataset.Lines.AddLineRow(dataset.Orders[0], 1);
dataset.Lines.AddLineRow(dataset.Orders[0], 2);
dataset.Lines.AddLineRow(dataset.Orders[0], 3);
dataset.Lines.AddLineRow(dataset.Orders[1], 1);
dataset.Lines.AddLineRow(dataset.Orders[1], 2);
dataset.Lines.AddLineRow(dataset.Orders[1], 3);
Console.WriteLine($"Total Number of Lines before delete is {dataset.Lines.Count}"); // Prints 6
//dataset.Orders.Rows.Clear();
dataset.Orders.RemoveAll();
Console.WriteLine($"Total Number of Lines after delete is {dataset.Lines.Count}"); // Prints 0

Related

How to replace the whole entity list in many-to-many relation?

public async Task UpdateEntity(EntityModel update)
{
_context.AttachRange(update.Links);
_condext.Update(update);
}
In this example, I have many-to-many relation between EntityModel and LinkModel. The instance update is not attached to the context and has a list of LinkModel entities with added and removed rows. I want to update the whole list of linked elements in one request to database without comparing added and deleted rows. Is that possible?
Code in the example is working when adding linked elements the first time, but then throws an error about already existing elements in table "EntitiesLinksRelations".
Yes, it is possible, but you have to fetch the existing entity with its related links first -
public async Task UpdateEntity(EntityModel update)
{
_context.AttachRange(update.Links);
var existingEntity = _context.EntityModels
.Include(p => p.Links)
.FirstOrDefault(p => p.Id == update.Id);
existingEntity.Links.Clear();
foreach (var link in update.Links)
{
existingEntity.Links.Add(link);
}
await _context.SaveChangesAsync();
}
EDIT - A bit of clarification
The code above does not -
delete all the existing linking data (data from the joining table)
then insert new ones for all related data in the new list
as it might seem at the first glance, specifically because of the statement - existingEntity.Links.Clear();
How much data gets deleted or inserted depends on the data in the new list (update.Links). For example, if the existing EntityModel had 100 related LinkModel, and in the new list 50 of them had been removed and 20 new ones had been added, the code above -
loads all 100 related LinkModel
generates delete command for the removed 50 LinkModel
generates insert command for the added 20 LinkModel

LINQ - how to remove duplicate rows in table

After certain proccess, I wan to remove duplicates from the table and commit the changes, so only single values remain.
I have three criteria for removal:
Name
date
status (is always 1)
So if there are records with same Name, and same date and same status... remove one. Does not matter which one.
I have:
dbContext.tbl_mytable
Since you are talking about deleting records, you need to test this first.
So if there are records with same Name, and same date and same status... remove one. Does not matter which one.
I'm assuming you want to remove all but one, ie, if you have three records with the same details, you remove two and leave one.
If so, you should be able to identify the duplicates by grouping by { Name, date, status} and then selecting all except the first record in each group.
ie something like
var duplicates = (from r in dbContext.tbl_mytable
group r by new { r.Name, r.date, r.status} into results
select results.Skip(1)
).SelectMany(a=>a);
dbContext.tbl_mytable.DeleteAllOnSubmit(duplicates);
dbContext.SubmitChanges();

Appending and deleting linked tables in access using c# issue

I have a piece of code that goes through all the linked tables and tables in an access database and for every table(all linked in this case) that matches a certain criteria it should add a new table and delete the old. The new is on a sql server database and the old the oracle, however this is irrelevant. The code is:
var dbe = new DBEngine();
Database db = dbe.OpenDatabase(#"C:\Users\x339\Documents\Test.accdb");
foreach (TableDef tbd in db.TableDefs)
{
if (tbd.Name.Contains("CLOASEUCDBA_T_"))
{
useddatabases[i] = tbd.Name;
string tablename = CLOASTableDictionary[tbd.Name];
string tablesourcename = CLOASTableDictionary[tbd.Name].Substring(6);
var newtable = db.CreateTableDef(tablename.Trim());
newtable.Connect = "ODBC;DSN=sql server copycloas;Trusted_Connection=Yes;APP=Microsoft Office 2010;DATABASE=ILFSView;";
newtable.SourceTableName = tablesourcename;
db.TableDefs.Append(newtable);
db.TableDefs.Delete(tbd.Name);
i++;
}
}
foreach (TableDef tbd in db.TableDefs)
{
Console.WriteLine("After loop "+tbd.Name);
}
There are 3 linked tables in this database 'CLOASEUCDBA_T_AGENT', 'CLOASEUCDBA_T_CLIENT' and 'CLOASEUCDBA_T_BASIC_POLICY'. The issue with the code is that it updates the first two tables perfectly but for some unknown reason, it never finds the third. Then in the second loop, it prints it out... it seems to just skip over 'CLOASEUCDBA_T_BASIC_POLICY'. I really dont know why. The weird thing is then that if run the code again, it will change 'CLOASEUCDBA_T_BASIC_POLICY'. Any help would be greatly appreciated.
Modifying a collection while you are iterating over it can sometimes mess things up. Try using a slightly different approach:
Iterate over the TableDefs collection and build a List (or perhaps a Dictionary) of the items you need to change. Then,
Iterate over the List and update the items in the TableDefs collection.

Possibilities and nested foreach loops C#

I am writing a program that can calculate all possible places for people sitting at a random amount of tables (can go up to 1000+):
As you see on the image, the black dots represent people (but in the computer, there are different types of people.)
There are two types of tables : blue and pink ones. The blue ones can contain 3 people and the pink 2 people.
To get all possible places for any person to sit I could use foreach loops (8of them) and then I can run some extra code...
But what happens if I add 200 tables? Then do I need to use 200 foreach loops?
Is there any way that this can be coded faster and less-space-consuming-coded?
What I tried? =>
switch(listoftables.Count)
{
case 1:foreach(Table table in listoftables){ //code to add people to this table}break;
case 2: foreach(Table table1 in listoftables)
{foreach(Table table1 in listoftables){//code to add people to this table
}}break;
}
INPUT : array with editable Table class objects (its a class created by myself)
PROCESS : the above List is edited and is added to another List object, where after the whole foreach process has ended, the OUTPUT will write all possible configurations (who are in the other List object) to the screen.
Example part of output :
// List<Table> listofalltables was processed
List<listofalltables> output
=> contains as [0] in array : List first
=> contains as [0] in array : Table.attachedpeople (is list)
Try a recursive method. A small example :
public List<Table> assignToTable(List<Person> invited, List<Table> tables)
{
if(!tables.HasRoom)
return tables;
else
{
assign(tables,invited) //code to add a person to a table
assignToTable(invited, tables);
}
}
If I were you I'll create a object taht represent you tables with a propertie to know if there is still some room avaiblable. This will assign to every people a table without any foreach.
Then in you main you could have a method that will rearrange the tables in all the way possible :
Table 1
Table 2
Table 3
Then
Table 1
Table 3
Table 2
...
Table 3
Table 2
Table 1
and call the recursive method on those lists and you will have all the possibility where poeple can sit...
Taken #Guigui's answer and changed it to how I interpret the question. This will try to seat everyone everywhere (except when there are more people than chairs, is that a case? I assumed more chairs than people) by recursion and loops, as you see the complexity will be of the form O(Math.Power(nrPeople, nrSeats)) which is a lot (if I'm not mistaken).
Person[] peopleInvited = ....; // Avoid copying this, we are not modifying it
public void AssignToTable(int invited, SeatingArrangements tables)
{
if(!tables.HasRoom || invited == peopleInvited.Length)
// Do what? Print the seating?
else
{
var personToSeat = peopleInvited[invited];
foreach (var possibleSeating in tables.GetEmptyChairs())
{
// Add one person to a table somewhere, but don't modify tables
var newArrangments = Assign(possibleSeating, personToSeat, tables)
AssignToTable(invited + 1, newArrangements);
}
}
}
Well, it more a question about math than programming. What you are trying to do is creating permutations of people. Basically you have N tables and 2N+2 seats. You can assign a number to each seat. Then the result will be the set of K-permutations of 2N+2, where K is the number of people invited, and N is the number of tables.
You can do this using loops, but you can also do it recursively. There are algorithms ready to use out there. For example:
Algorithm to generate all possible permutations of a list?

Iterating through two identical data sources

I have data with the same schema in a pipe delimited text file and in a database table, including the primary key column.
I have to check if each row in the file is present in the table, if not generate an INSERT statement for that row.
The table has 30 columns, but here I've simplified for this example:
ID Name Address1 Address2 City State Zip
ID is the running identity column; so if a particular ID value from the file is found in the table, there should be no insert statement generated for that.
Here's my attempt, which doesn't feel correct:
foreach (var item in RecipientsInFile)
{
if (!RecipientsInDB.Any(u => u.ID == item.ID ))
{
Console.WriteLine(GetInsertSql(item));
}
}
Console.ReadLine();
EDIT: Sorry, I missed the asking the actual question; how to do this?
Thank you very much for all the help.
EDIT: The table has a million plus rows, while the file has 50K rows. This a one time thing, not a permanent project.
I would add all the RecipientsInDB Ids in a HashSet and then test if the set contains the item Id.
var recipientsInDBIds = new Hashset(RecipientsInDB.Select(u => u.ID));
foreach (var item in RecipientsInFile)
{
if (!recipientsInDBIds.Contains(item.ID ))
{
Console.WriteLine(GetInsertSql(item));
}
}
Console.ReadLine();
Try comparing the ID lists using .Except()
List<int> dbIDs = Recipients.Select(x=>x.ID).ToList();
List<int> fileIDs = RecipientsFile.Select(x=>x.ID).ToList();
List<int> toBeInserted = fileIDs.Except(dbIDs).ToList();
toBeInserted.ForEach(x=>GetInsertSqlStatementForID(x));
For the pedantic and trollish among us in the comments, please remember the above code (like any source code you find on the interwebs) shouldn't be copy/pasted into your production code. Try this refactoring:
foreach (var item in RecipientsFile.Select(x=>x.ID)
.Except(DatabaseRecipients.Select(x=>x.ID)))
{
GetInsertSqlStatementForID(item);
}
Lots of ways of accomplishing this. Yours is one way.
Another would be to always generate SQL, but generate it in the following manner:
if not exists (select 1 from Recipients where ID == 1234)
insert Recipients (...) values (...)
if not exists (select 1 from Recipients where ID == 1235)
insert Recipients (...) values (...)
Another would be to retrieve the entire contents of the database into memory beforehand, loading the database IDs into a HashSet, then only checking that HashSet to see if it exists - would take a little longer to get started, but would be faster for each record.
Any of these three techniques would work - it all depends on how big your database table is, and how big your file is. If they're both relatively small (maybe 10,000 records or so), then any of these should work fine.
EDIT
And there's always option D: Insert all records from the file into a temporary table (could be a real table or a SQL temp table, doesn't really matter) in the database, then use SQL to join the two tables together and retrieve the differences (using not exists or in or whatever technique you want), and insert the missing records that way.

Categories