LINQ to SQL - Updating Data Context Objects in Partial Classes - c#

I have created an extensibility method for deleting one of my Linq To Sql objects called Reservation.
Well, in this partial method I created, I want to update some other objects. I can't seem to get the update to be persisted in the database. Here is my partial method for deleting the reservation.
public partial class LawEnforcementDataContext
{
partial void DeleteReservation(Reservation instance)
{
// Get ID's of those seated in course
var roster = new Roster(this, instance.CourseID);
var seated = from r in roster.All
where r.WaitingList == false
select r.ID;
// delete the reservation
this.ExecuteDynamicDelete(instance);
// get seated id's not in original seated ids
var newlySeated = from r in roster.All
where r.WaitingList == false && !seated.Contains(r.ID)
select r.ID;
var reservations = this.Reservations.Where(r => newlySeated.Contains(r.ID));
foreach (var r in reservations)
{
r.Confirmed = false;
// Have tried doing nothing, thinking calling code's db.SubmitChanges() would do the trick
//this.ExecuteDynamicUpdate(r); HAVE TRIED THIS
}
//this.SubmitChanges(); HAVE TRIED THIS
}
}
The delete is taking place but the update is not. Commented in the last few lines are some of the things I have tried.
Any ideas? Thanks!
EDIT
Here is what I have done to solve this:
public override void SubmitChanges(System.Data.Linq.ConflictMode failureMode)
{
ChangeSet delta = GetChangeSet();
foreach (var res in delta.Deletes.OfType<Reservation>())
{
// Get ID's of those seated in course
var roster = new Roster(this, res.CourseID);
var seated = from r in roster.All
where r.WaitingList == false
select r.ID;
base.SubmitChanges(failureMode);
// get seated id's not in original seated ids
var newlySeated = from r in roster.All
where r.WaitingList == false && !seated.Contains(r.ID)
select r.ID;
var reservations = this.Reservations.Where(r => newlySeated.Contains(r.ID));
foreach (var r in reservations)
{
r.Confirmed = false;
}
}
base.SubmitChanges(failureMode);
}

I expect the problem here is that it has already called GetChangeSet().
I suggest you override SubmitChanges() at the data-context, and apply this logic there instead...
partial class LawEnforcementDataContext
{
public override void SubmitChanges(
System.Data.Linq.ConflictMode failureMode)
{
ChangeSet delta = GetChangeSet();
foreach (var reservation in delta.Deletes.OfType<Reservation>())
{
// etc
}
base.SubmitChanges(failureMode);
}
}

Here is an explanation and nice way to update objects in partial classes:
Implementing linqtosql partial DataContext class - how to inspect before/after values
I hope it helps.

Related

Merge data from two arrays or something else

How to combine Id from the list I get from file /test.json and id from list ourOrders[i].id?
Or if there is another way?
private RegionModel FilterByOurOrders(RegionModel region, List<OurOrderModel> ourOrders, MarketSettings market, bool byOurOrders)
{
var result = new RegionModel
{
updatedTs = region.updatedTs,
orders = new List<OrderModel>(region.orders.Count)
};
var json = File.ReadAllText("/test.json");
var otherBotOrders = JsonSerializer.Deserialize<OrdersTimesModel>(json);
OtherBotOrders = new Dictionary<string, OrderTimesInfoModel>();
foreach (var otherBotOrder in otherBotOrders.OrdersTimesInfo)
{
//OtherBotOrders.Add(otherBotOrder.Id, otherBotOrder);
BotController.WriteLine($"{otherBotOrder.Id}"); //Output ID orders to the console works
}
foreach (var order in region.orders)
{
if (ConvertToDecimal(order.price) < 1 || !byOurOrders)
{
int i = 0;
var isOurOrder = false;
while (i < ourOrders.Count && !isOurOrder)
{
if (ourOrders[i].id.Equals(order.id, StringComparison.InvariantCultureIgnoreCase))
{
isOurOrder = true;
}
++i;
}
if (!isOurOrder)
{
result.orders.Add(order);
}
}
}
return result;
}
OrdersTimesModel Looks like that:
public class OrdersTimesModel
{
public List<OrderTimesInfoModel> OrdersTimesInfo { get; set; }
}
test.json:
{"OrdersTimesInfo":[{"Id":"1"},{"Id":"2"}]}
Added:
I'll try to clarify the question:
There are three lists with ID:
First (all orders): region.orders, as order.id
Second (our orders): ourOrders, as ourOrders[i].id in a while loop
Third (our orders 2): from the /test.json file, as an array {"Orders":[{"Id":"12345..."...},{"Id":"12345..." ...}...]}
There is a foreach in which there is a while, where the First (all orders) list and the Second (our orders) list are compared. If the id's match, then these are our orders: isOurOrder = true;
Accordingly, those orders that isOurOrder = false; will be added to the result: result.orders.Add(order)
I need:
So that if (ourOrders[i].id.Equals(order.id, StringComparison.InvariantCultureIgnoreCase)) would include more Id's from the Third (our orders 2) list.
Or any other way to do it?
You should be able to completely avoid writing loops if you use LINQ (there will be loops running in the background, but it's way easier to read)
You can access some documentation here: https://learn.microsoft.com/en-us/dotnet/csharp/programming-guide/concepts/linq/introduction-to-linq-queries
and you have some pretty cool extension methods for arrays: https://learn.microsoft.com/en-us/dotnet/api/system.linq.enumerable?view=net-6.0 (these are great to get your code easy to read)
Solution
unsing System.Linq;
private RegionModel FilterByOurOrders(RegionModel region, List<OurOrderModel> ourOrders, MarketSettings market, bool byOurOrders)
{
var result = new RegionModel
{
updatedTs = region.updatedTs,
orders = new List<OrderModel>(region.orders.Count)
};
var json = File.ReadAllText("/test.json");
var otherBotOrders = JsonSerializer.Deserialize<OrdersTimesModel>(json);
// This line should get you an array containing
// JUST the ids in the JSON file
var idsFromJsonFile = otherBotOrders.Select(x => x.Id);
// Here you'll get an array with the ids for your orders
var idsFromOurOrders = ourOrders.Select(x => x.id);
// Union will only take unique values,
// so you avoid repetition.
var mergedArrays = idsFromJsonFile.Union(idsFromOurOrders);
// Now we just need to query the region orders
// We'll get every element that has an id contained in the arrays we created earlier
var filteredRegionOrders = region.orders.Where(x => !mergedArrays.Contains(x.id));
result.orders.AddRange(filteredRegionOrders );
return result;
}
You can add conditions to any of those actions (like checking for order price or the boolean flag you get as a parameter), and of course you can do it without assigning so many variables, I did it that way just to make it easier to explain.

Adding to or updating an entity in a foreach loop takes too long time before calling SaveChanges()?

I have this method that saves an entity with its related items (many-to-many relationship),
private static void Save<T>(TbCommonHistoryLog log, List<T> lstDetails) where T : IHasSerial
{
foreach (var item in lstDetails.OrderBy(x => x.Serial))
{
var ser = SerializeObject(item);
var record = oContext.TbHistoryLog_Lists.FirstOrDefault(x => x.ListObjectJson == ser);
if (record == null) //add new list item
{
TbCommonHistoryLog_Lists listObject = new TbCommonHistoryLog_Lists()
{
ListObjectJson = SerializeObject(item)
};
var details = new TbCommonHistoryLogDetails { TbHistoryLog = log, TbHistoryLog_Lists = listObject };
oContext.TbHistoryLogDetails.Add(details);
}
else //attach an existing list item
{
var o = oContext.TbHistoryLog_Lists.Find(record.Id);
oContext.TbHistoryLog_Lists.Attach(o);
var details = new TbCommonHistoryLogDetails { TbHistoryLog = log, TbHistoryLog_Lists = o };
oContext.TbHistoryLogDetails.Add(details);
}
}
oContext.BulkSaveChanges();
}
I have two tables: TbCommonHistoryLog, TbCommonHistoryLog_Lists, that are in many to many relationship, the joining table is TbCommonHistoryLogDetails,
What I'm doing here is an auditing for master-detail models, all audits are serialized to JSON in DB, I save the head object in the TbCommonHistoryLog table, and every list item in the TbHistoryLog_Lists table, in the mthod above I check if the list item is already exists in the database or not to avoid duplicating.
but this process takes more than 15 seconds which is a very long time, I can't figure out what am I doing wrong here.. please help?
For every single item in collection you're querying database. My suggestion is to save records in var, then ask the variable if the item is in database.
var databaseRecords = oContext.TbHistoryLog_Lists.ToList();
Then in the loop:
var record = databaseRecords.FirstOrDefault(x => x.ListObjectJson == ser);

Iterating over two lists in c#

So i have a function that gets a list of students from a web service and also query the localdb for all the students in there. the data is placed in two different list. So i want to check to see if a new student already exists in the localdb List. if it does, update it and it if doesn't then add it. i unable to get it working . I am trying to perform this using LINQ, but i can't seem to get it working right. My LINQ skills are amateurish at best.
public async Task GetStudents()
{
String controllerName = "Students";
List<Students> newStudentData = await RunGetAsync<Students>(controllerName);
// get all the service types that already exists in the localStudent Db
List<Students> currentStudentData = db.Studentss.ToList();
foreach (Students existingStudents in currentStudentData)
{
foreach (Students newStudents in newStudentData)
{
IEnumerable<Students> selectStudents = from student in newStudentData // check if Students exist in the database
where student.Id == existingStudents.Id
select student;
if (selectStudents == null) // didn't find it, then add it
{
db.Students.Add(newStudents);
}
if (selectStudents != null) // found it , then update the informations
{
Students updatedStudents = new Students();
foreach (var field in selectStudents)
{
updatedStudents.FName = field.FName;
updatedStudents.LName = field.LName;
updatedStudents.ZipCode = field.ZipCode;
updatedStudents.AccessCode = field.AccessCode;
}
db.Entry(updatedStudents).State = System.Data.Entity.EntityState.Modified;
}
}
}
db.SaveChanges();
}
Thank you very much for your help.
you're looping more than you need :
foreach (Students newStudents in newStudentData)
{
var student = currentStudentData.FirstOrDefault(s => s.Id == newStudents.Id);
if(student == null)
{
//add
}
else
{
//update
}
}
with FirstOrDefault you can find out if it exists and get a reference to it at the same time, if it does.
You could use Intersect and Except like below:
//Find students that already exist to update
var updateStudents = currentStudentData.Intersect(newStudentData);
//Find new students to add
var addStudents = newStudentData.Except(currentStudentData);

How can I edit or add to a particular field without pull the all object

How I can do just this ( a.myFavorits.Add()) without pulling the all object to var a , because a has a lot of data, and I don't want to pull all a object, but I can't find a way do do it.
I want to do the lambada and the linq without return something but linq is always return something
public static void addFavorits(long f,long idUser)
{
using (var db = dataBase())
{
// here i pull object user from users table
var a = db.users.Where(c => c.id == idUser).SingleOrDefault();
// here i adding to the object field myFavorits new value
//myFavorits is also a table of entitys that connected to user object
a.myFavorits.Add(new BE.FavoritsUsersLong { myLong = f });
db.SaveChanges();
}
}
I thought to do something like this but i dont know how to set the field users_TableId that is the key that connect the 2 tables
public static void addFavorits(long favoritId,long idUser)
{
using (var db = dataBase())
{
db.favoritsUsersLong.Add(new BE.FavoritsUsersLong {myLong = favoritId}
/*,users_TableId =idUser*/);
db.SaveChanges();
}
}
Here's a concrete example that does what you want. In this example, only the Name of a Company is modified and saved. Or an item is added to one of its collections.
var cmp = new Company{ CmpId = 1, Name = "Cmp1" }; // CmpId is the primary key
db.Companies.Attach(cmp);
db.Entry(cmp).Property(c => c.Name).IsModified = true;
// Or add an entity to a collection:
cmp.Users = new[] {new User { Name = "a1", PassWord = "a1" } };
try
{
db.Configuration.ValidateOnSaveEnabled = false;
db.SaveChanges();
}
finally
{
db.Configuration.ValidateOnSaveEnabled = true;
}
Result in SQL:
DECLARE #0 VarChar(30) = 'Cmp1'
DECLARE #1 Int = 1
UPDATE [dbo].[Company]
SET [Name] = #0
WHERE ([CmpId] = #1)
There are a few things to note here:
Obviously you need to know the Id of the entity you want to modify.
The object you create is called a stub entity, which is an incomplete entity. When you try to save such an entity, EF is very likely to complain about null values in required properties. That's why almost certain you'd have to disable validation (temporarily, or, better, dispose the context immediately).
If you want to add an item to a collection, you should leave validation enabled, because you'd want to know for sure that the new entity is valid. So you shouldn't mix these two ways to use a stub entity.
If you often need roughly the same small part of your entity you may consider table splitting.
I'm guessing this is what you want? I don't see you 'editting' I only see you adding.
using (var db = dataBase())
{
var a = new user();
....
//set properties etc..
...
a.myFavorits.Add(new BE.FavoritsUsersLong { myLong = f });
db.users.Add(a);
db.SaveChanges();
}

Duplicate record in database when call SaveChanges()

Hi i have a problem with EF. In my application i have to load from database some content to populate a DataGrid.
UserControl :
contenus = new List<Contenu>();
contenus = sacoche.Contenus.ToList(); // i get sacoche in the parameter of the contructor
ContenuViewSource.Source = contenus;
ContenuView = (ListCollectionView)ContenuViewSource.View;
ContenuView.Refresh();
everything work just fine, but when i try to add some others Contenus i get a duplicate record in the database. The only difference between the duplicated record is that the first record loose his foreign key.
Here i add my Contenuto my Sacoche:
editableSacoche = SacocheDal.dbContext.Sacoches.Include("Contenus").First(i => i.SacocheID == editableSacoche.SacocheID);
editableSacoche.Contenus = contenus;
SacocheDal.dbContext.SaveChanges();
all i do is get the Sacoche and add to it his Contenu and finally call SaveChanges().
Here is the result :
EDIT: I tried to get only the new items but failed.
List<Contenu> contenuAjoute = contenus.Except(editableSacoche.Contenus.ToList()).ToList();
in contenuAjoutei get all the records even if they are equal ...
Try this:
editableSacoche = SacocheDal.dbContext.Sacoches.Include("Contenus").First(i => i.SacocheID == editableSacoche.SacocheID);
editableSacoche.Contenus = null;
editableSacoche.ContenusID = contenus.ID;
SacocheDal.dbContext.SaveChanges();
I found a way to achieve what i wanted. I create an ItemComparer and use Exceptto only add the new items.
Here is the comparer :
class ContenuComparer : IEqualityComparer<Contenu>
{
public bool Equals(Contenu x, Contenu y)
{
if (x.ContenuID == y.ContenuID)
return true;
return false;
}
public int GetHashCode(Contenu obj)
{
return obj.ContenuID.GetHashCode();
}
}
And Here the code :
editableSacoche = SacocheDal.dbContext.Sacoches.Include("Contenus").First(i => i.SacocheID == editableSacoche.SacocheID);
List<Contenu> contenuAjoute = contenus.Except(editableSacoche.Contenus.ToList(), new ContenuComparer()).ToList();
foreach (Contenu c in contenuAjoute)
{
editableSacoche.Contenus.Add(c);
}
SacocheDal.dbContext.SaveChanges();
I don't now if it's the right way but it works fine.

Categories

Resources