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);
Related
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);
I am writing a parser for one of website, which have products connected to categories. I am trying to build my own database with these items.
I have decided to use Entity Framework, but I am new to this framework, so here's my problem:
During parsing i have multiple items with same category. But categories are kind of trees. I mean, category have a reference to parentCategory.
During of parsing i have a list of category inheritance f.e : category1 -> category1.1 -> category1.1.1
Each product I parse and add to database need to verify if that category exist and go through category inheritance to create non existing categories.
Code looks like this:
Category parentCategory = null;
foreach (var sCategory in categories)
{
var currentCategory = d.CategorySet.SingleOrDefault(category => category.Name == sCategory && category.Parent == parentCategory);
if (currentCategory == null)
{
currentCategory = new Category(){Name = sCategory,Parent = parentCategory};
if(parentCategory != null)
d.Entry(parentCategory).State = EntityState.Unchanged;
}
parentCategory = currentCategory;
}
But in this case, SingleOrDefault LinQ does not work because of exception:
Unable to create a constant value of type 'DataBaseModel.Category'. Only primitive types or enumeration types are supported in this context.
I know that I should compare IDs of category, but in this case it needs to saveChanges into db every time I add sth to DB.
Is there any other possibility to handle that?
I have solved this issue by creating local Dictionaries of Categories and before usage, fill this dictionaries by data from database.
_categoriesDictionary.Clear();
foreach (var category in this.Container.CategorySet)
{
Category temp = category;
string fullCategoryString = "";
while (temp != null)
{
fullCategoryString = fullCategoryString.Insert(0, temp.Name + ";");
temp = temp.Parent;
}
_categoriesDictionary.Add(fullCategoryString, category);
}
And then when analyzing the record:
Category parentCategory = null;
string fullCatString = "";
foreach (var sCategory in categories)
{
fullCatString += sCategory + ";";
Category currentCategory;
if (!_categoriesDictionary.TryGetValue(fullCatString, out currentCategory))
{
currentCategory = new Category()
{
Name = sCategory,
Parent = parentCategory
};
this.Container.CategorySet.Add(currentCategory);
_categoriesDictionary.Add(fullCatString, currentCategory);
}
parentCategory = currentCategory;
}
result.Category = parentCategory;
This has another adventage from my point of view:
Its collecting data on start, and then do not query DB every time
I'm working on c# winforms Application. I want to find and Update any specific record on parse.com.
My problem is that I find the record but I don't know how to update it.
The find code is this:
int ID = Convert.ToInt32(txtId.Text);
var FindID = (from find in ParseObject.GetQuery("DriverID")
where find.Get<Int32>("DriverID") == ID
select find);
var ID = FindID.FindAsync();
Finally its working
public async void UpdateDriverOnParse(Int32 ID)
{
var query = (from find in ParseObject.GetQuery("DriverLogin")
where find.Get<Int32>("SystemID") == ID
select find);
// Retrieve the results
IEnumerable<ParseObject> Data = await query.FindAsync();
//for updating the selected row
foreach (var row in Data)
{
row["Pin"] = Convert.ToInt32(txtPinNo.Text);
row["DriverID"] = Convert.ToInt32(txtCallSign.Text);
row["Name"] = txtFirstName.Text+" "+txtMname.Text+" "+txtLastName.Text;
await row.SaveAsync();
}
}
FindAsync returns an IEnumerable. If you just want one object you can use FirstAsync. When you have your object, update it like you would any IDictionary and then call 'await ID.SaveAsync()'
Say, I have the following conceptual model, there are strories that have tags (more than one, so it's a many-to-many relationship), plus each tag belongs to a particular category.
My data comes from an external source and before inserting it I want to make sure that no duplicated tags are added.
Updated code snippet:
static void Main(string[] args)
{
Story story1 = new Story();
story1.Title = "Introducing the Entity Framework";
story1.Tags.Add(new Tag { Name = ".net", });
story1.Tags.Add(new Tag { Name = "database" });
Story story2 = new Story();
story2.Title = "Working with Managed DirectX";
story2.Tags.Add(new Tag { Name = ".net" });
story2.Tags.Add(new Tag { Name = "graphics" });
List<Story> stories = new List<Story>();
stories.Add(story1);
stories.Add(story2);
EfQuestionEntities db = new EfQuestionEntities();
Category category = (from c in db.Categories
where c.Name == "Programming"
select c).First();
foreach (Story story in stories)
{
foreach (Tag tag in story.Tags)
{
Tag currentTag = tag;
currentTag = GetTag(tag.Name, category, db);
}
db.Stories.AddObject(story);
}
db.SaveChanges();
}
public static Tag GetTag(string name, Category category, EfQuestionEntities db)
{
var dbTag = from t in db.Tags.Include("Category")
where t.Name == name
select t;
if (dbTag.Count() > 0)
{
return dbTag.First();
}
var cachedTag = db.ObjectStateManager.GetObjectStateEntries(EntityState.Added).
Where(ose => ose.EntitySet == db.Tags.EntitySet).
Select(ose => ose.Entity).
Cast<Tag>().Where(x => x.Name == name);
if (cachedTag.Count() != 0)
{
return cachedTag.First();
}
Tag tag = new Tag();
tag.Name = name;
tag.Category = category;
db.Tags.AddObject(tag);
return tag;
}
However, I get an exception about an object with the same EntityKey that is already present in the ObjectContext.
Also, if I remove the else statement I will get an exception about violating an FK constraint, so it seems like its Category attribute is set to null.
I 've had the same problem with EF. Here's what I ended up doing:
Instead of doing story1.Tags.Add(new Tag { Name = ".net", }) yourself, routed all Tag creation through a helper method like this: story1.Tags.Add(GetTag(".net")).
The GetTag method checks the tags in the context to see if it should return an existing entity, like you do. If it does, it returns that.
If there is no existing entity, it checks the ObjectStateManager to see if there are Tag entities added to the context but not already written to the db. If it finds a matching Tag, it returns that.
If it still has not found the Tag, it creates a new Tag, adds it to the context, and then returns it.
In essence this will make sure that no more than one instance of any Tag (be it already existing or just created) will be used throughout your program.
Some example code lifted from my project (uses InventoryItem instead of Tag, but you get the idea).
The check in step 3 is done like this:
// Second choice: maybe it's not in the database yet, but it's awaiting insertion?
inventoryItem = context.ObjectStateManager.GetObjectStateEntries(EntityState.Added)
.Where(ose => ose.EntitySet == context.InventoryItems.EntitySet)
.Select(ose => ose.Entity)
.Cast<InventoryItem>()
.Where(equalityPredicate.Compile())
.SingleOrDefault();
if (inventoryItem != null) {
return inventoryItem;
}
If the Tag is not found in step 3, here's the code for step 4:
inventoryItem = new InventoryItem();
context.InventoryItems.AddObject(inventoryItem);
return inventoryItem;
Update:
It should be used like this:
Story story1 = new Story();
story1.Title = "Introducing the Entity Framework";
story1.Tags.Add(GetTag(".net", category, db));
story1.Tags.Add(GetTag("database", category, db));
I have a structure like the following using EF:
An "Event" has many "Guests"
Let's say Event.Guests has 4 Guest elements, with the id's 1,2,3,4
I want to update the Event.Guests to have the guests with id's 3,4,5 (if Guest 5 doesn't exist I want to create it).
What's the most efficient way to remove the existing guests from Event and add the new one?
This is what I'm doing right now:
var newGuests = new List<Guest>();
var existingGuests = #event.Guests.ToList();
// GetNewGuestsIds will return the new guests list (3,4,5)
foreach (var guestId in GetNewGuestsIds())
{
Guest guest = existingGuests.FirstOrDefault(eg => eg.Id == guestId);
if (guest == null)
{
guest = db.Guests.CreateObject();
// fill guest data here
}
newGuests.Add(guest);
}
foreach (var existingGuest in existingGuests)
{
// Remove the existing element from the list to add
var removed = newGuests.RemoveAll(g => g.Id == existingGuest.Id);
if (removed == 0) // The existing host is not on the list to add, delete it
{
#event.EventHosts.Remove(existingGuest);
}
}
foreach (var guest in newGuests)
{
#event.Guests.Add(guest);
}
But I think this might be improved ... I just don't know how.
Thanks!
After some thought I came with this result which seems a lot better:
var existingGuests = #event.Guests.ToList();
// GetNewGuestsIds will return the new guests list (3,4,5)
foreach (var guestId in GetNewGuestsIds())
{
if (existingGuests.RemoveAll(g => g.Id == guestId) == 0)
{
guest = db.Guests.CreateObject();
// fill guest data here
#event.Guests.AddObject(guest);
}
}
existingGuests.ForEach(g => #event.Guests.Remove(g));
I hope this helps someone else.