I'm new to developing UWA. I'm trying to develop an app that stores information on a local database.
I can store and get information without problem, but I can't find how to work with relationships.
I have:
public class ToDoItem
{
public int ToDoItemId
{
get; set;
}
public int ToDoItemCategoryId
{
get; set;
}
public virtual ToDoItemCategory ToDoItemCategory
{
get; set;
}
[Required]
public string Description
{
get; set;
}
}
And:
public class ToDoItemCategory
{
public int ToDoItemCategoryId
{
get; set;
}
public virtual ICollection<ToDoItem> ToDoItems
{
get; set;
}
[Required]
public string Name
{
get; set;
}
}
And, I'm saving the information this way:
using (var db = new DatabaseContext())
{
var cat = db.ToDoItemCategories.Single(m => m.ToDoItemCategoryId == SelectedCategory.ToDoItemCategoryId);
ToDoItem model = new ToDoItem() { Description = description, ToDoItemCategory = cat };
db.ToDoItems.Add(model);
db.SaveChanges();
}
But ToDoItems don't get saved to the ToDoItemCategory ToDoItems list. Also, ToDoItemId is being auto-generated with negative ID's, I don't understand why.
I've also tried something similar to this:
using (var db = new DatabaseContext())
{
var cat = db.ToDoItemCategories.Single(m => m.ToDoItemCategoryId == SelectedCategory.ToDoItemCategoryId);
ToDoItem model = new ToDoItem() { Description = description, ToDoItemCategory = cat };
db.ToDoItems.Add(model);
cat.ToDoItems.Add(model);
db.SaveChanges();
}
But no luck. What am I doing wrong here?
After testing and searching a lot I've found a solution to this problem.
Categories = Context.ToDoItemCategories.Where(predicate).Include(m => m.ToDoItems).ToList();
Apparently the navigation properties only get loaded after the Include. Doing that everything works fine.
Related
I created my database and started developing a web application in c# with EF5 and the DB First approach. I can modify my entities on their own data fields but donĀ“t get it to work when it comes to updating relationships. A simple relationship example is Project <- ProjectCategoryIntersection -> Category
Model:
public class Project
{
public TProject project { get; private set; }
public List<string> Categories { get; set; }
}
public partial class TProject //generated table object
{
public virtual ICollection<TProjectCategoryIntersection> TProjectCategoryIntersection { get; set; }
}
public partial class TProjectCategoryIntersection
{
public int Id { get; set; }
public int ProjectId { get; set; }
public int ProjectCategoryId { get; set; }
public virtual TProject T_Project { get; set; }
public virtual TCategory T_ProjectCategory { get; set; }
}
Save:
public void SaveProject(Project project)
{
var context = new ProjectManagementEntities();
TProject projectToUpdate = new TProject();
projectToUpdate.Id = project.Id;
foreach (var category in project.Categories)
{
var cat = (from c in context.TProjectCategory
where c.Name == category
select c).FirstOrDefault();
var inters = new TProjectCategoryIntersection() { ProjectCategoryId = cat.Id, ProjectId = project.project.Id, TProject = project.project, TProjectCategory = cat };
projectToUpdate.TProjectCategoryIntersection.Add(inters);
}
var entry = context.Entry(projectToUpdate).State = EntityState.Modified; //throws exceptions
context.SaveChanges();
}
exception:
Conflicting changes to the role 'TProject' of the relationship 'ProjectManagementModel.FK_TProjectCategoryIntersection_TProject' have been detected.
I also receive a multiple instances ChangeTracker exception when i try to add the categories directly to the project object:
project.project.TProjectCategoryIntersection.Add(inters);
Should i remove the generated table object from my model?
public class Project
{
public TProject project { get; private set; } //remove this?
public List<string> Categories { get; set; }
}
Solution
I ended up removing the generated table object public TProject project { get; private set; } and changed my code to:
public void SaveProject(Project project)
{
var context = new ProjectManagementEntities();
var projectToUpdate = context.T_Project.Find(project.Id);
foreach (var item in projectToUpdate.T_ProjectCategoryIntersection.ToList())
{
var oldCat = context.T_ProjectCategoryIntersection.Find(item.Id);
context.T_ProjectCategoryIntersection.Remove(oldCat);
}
foreach (var category in project.Categories)
{
var cat = (from c in context.T_ProjectCategory
where c.Name == category
select c).FirstOrDefault();
var inters = new T_ProjectCategoryIntersection() { ProjectCategoryId = cat.Id, ProjectId = project.Id };
context.T_ProjectCategoryIntersection.Add(inters);
}
//more code...
context.Entry(projectToUpdate).State = EntityState.Modified;
context.SaveChanges();
}
Apperantly this happens when you use reference to an object and also an Integer for the ID within the same object and change both of them. When this happens EF can not know which one is the correct reference
Try setting only Ids and set null for references like
var inters = new TProjectCategoryIntersection() { ProjectCategoryId = cat.Id,
ProjectId = project.project.Id};
I am pretty new to Entity Framework and I am using this method in order to query through my database:
var _context = new StudioEntities();
var results = _context.tblStudios.Select(u => new
{
u.Standort,
u.Name,
u.Id
}).ToList();
Now my goal is to add local data as well which isn't present in the database. I tried it with this code but it didn't work:
results.Add(new tblStudio { Id = 0, Name = "Gesamt" });
Can someone help me with this? Thanks
Edit:
My table class looks like this:
public partial class tblStudio
{
public int Id { get; set; }
public string Name { get; set; }
public string Standort { get; set; }
public Nullable<int> Plz { get; set; }
}
The result is not a List of tblStudios, it is a List of Anonymous Type. So if you want to add an item to the result you should do like this:
var results = _context.tblStudios.Select(u => new tblStudiosDTO()
{
Standort = u.Standort,
Name = u.Name,
Id = u.Id
}).ToList();
results.Add(new tblStudiosDTO() { Id = "0", Name = "Gesamt" });
But because you cannot project onto a mapped entity then you need to create a DTO class like tblStudiosDTO with needed properties from the tblStudios entity.
public class tblStudiosDTO
{
public string Standort { get; set; }
public string Name { get; set; }
public string Id { get; set; }
}
I have a problem when I am updating data to database. When I want to update data, Entitiy Framework adds new rows to tables that can have multiple rows (tables that have foreign key).
Database model:
When I update Phone/Contact or Tags entity, Entity Framework automatically adds new row instead of updating it
Here is code that I used:
public string UpdateContact(Contact contact)
{
if (contact != null)
{
int id = Convert.ToInt32(contact.id);
Contact Updatecontact = db.Contacts.Where(a => a.id == id).FirstOrDefault();
Updatecontact.firstname = contact.firstname;
Updatecontact.lastname = contact.lastname;
Updatecontact.address = contact.address;
Updatecontact.bookmarked = contact.bookmarked;
Updatecontact.city = contact.city;
Updatecontact.notes = contact.notes;
Updatecontact.Emails1 = contact.Emails1;
Updatecontact.Phones1 = contact.Phones1;
Updatecontact.Tags1 = contact.Tags1;
db.SaveChanges();
return "Contact Updated";
}
else
{
return "Invalid Record";
}
}
EDIT:
Here is EF Model code:
Contact:
public partial class Contact
{
public Contact()
{
this.Emails1 = new HashSet<Email>();
this.Phones1 = new HashSet<Phone>();
this.Tags1 = new HashSet<Tag>();
}
public int id { get; set; }
public string firstname { get; set; }
public string lastname { get; set; }
public string address { get; set; }
public string city { get; set; }
public Nullable<byte> bookmarked { get; set; }
public string notes { get; set; }
public virtual ICollection<Email> Emails1 { get; set; }
public virtual ICollection<Phone> Phones1 { get; set; }
public virtual ICollection<Tag> Tags1 { get; set; }
}
Emails/Tags and Phone have same model (with different name for value)
public partial class Email
{
public int id { get; set; }
public int id_contact { get; set; }
public string email1 { get; set; }
public virtual Contact Contact1 { get; set; }
}
Update properties rather than set new objects.
Updatecontact.Emails1.email1 = contact.Emails1.email1;
Updatecontact.Phones1.number = contact.Phones1.number;
Updatecontact.Tags1.tag1 = contact.Tags1.tag1;
Edit: seems that your contact model has lists of emails, phones and tags. If this is so, then simple assignment won't work. Instead, when sent from the client, you have to find one-by-one and update:
foreach ( var email in contact.Emails1 )
{
// first make sure the object is retrieved from the database
var updateemail = Updatecontact.Emails1.FirstOrDefault( e => e.id == email.id );
// then update its properties
updateemail.email1 = email.email1;
}
// do the same for phones and tags
It's doing that because you're setting the different HashSet values to the values of a completely different collection, namely from what you call contact in that method. In order for you to properly do an update, you're going to have to loop through the emails, phones, and tags to check if those need to be added/updated/deleted on the actual object that you're trying to update.
First, why do you have to search for the contact if you are already receiving it by parameter? That makes me think that you are creating a new one because you are in a different context, if so, then it creates a new record because you have 2 different object in 2 different context.
Try using just one object in the same context to update, EF should mark the object to modification by itself, if not then try making sure before saving that your object has EntityState.Modified.
I have 3 entities in my case. Invoice, InvoiceDetail and Item.
Invoice has a collection of InvoiceDetail.And each InvoiceDetail has an Item.
Please see the code below:
var ctx = new TestEntities();
var newInvoice = new Invoice
{
CreationDate = DateTime.Now,
UserId = 14
};
newInvoice.InvoiceDetails.Add(new InvoiceDetail
{
ItemId = 345,
ItemCount = 10
});
newInvoice.InvoiceDetails.Add(new InvoiceDetail
{
ItemId = 534,
ItemCount = 10
});
ctx.Invoices.Add(newInvoice);
ctx.SaveChanges();
// workaround
// ctx.Items.ToList();
foreach (var i in newInvoice.InvoiceDetails)
{
// In this line I get NullReferenceException
Console.WriteLine(i.Item.Title);
}
I get NullReferenceException when I want to retrieve each InvoiceDetail's Item data.
The problem is solved when I uncomment, commented part of the code. (ctx.Items.ToList())
UPDATE 1 :
And also this is the Item class:
public partial class Item
{
public Item()
{
this.InvoiceDetails = new HashSet<InvoiceDetail>();
}
public long Id { get; set; }
public string Title { get; set; }
public virtual ICollection<InvoiceDetail> InvoiceDetails { get; set; }
}
UPDATE 2:
public partial class InvoiceDetail
{
public long Id { get; set; }
public long InvoiceId { get; set; }
public long ItemId { get; set; }
public int ItemCount { get; set; }
public virtual Invoice Invoice { get; set; }
public virtual Item Item { get; set; }
}
[NOTE: I am assuming EF5]
The problem could be related to the way you create instances of Invoice and InvoiceDetail. You are newing up instances so they are not EF proxies with all of the necessary components for lazy loading.
I suggest you try using the DbSet.Create() method instead of new
var newInvoice = ctx.Set<Invoice>().Create();
newInvoice.CreationDate = DateTime.Now;
newInvoice.UserId = 14;
var detail1 = ctx.Set<InvoiceDetail>().Create();
detail1.ItemId = 345;
detail1.ItemCount = 10;
newInvoice.InvoiceDetails.Add(detail1);
//...
I can't promise this will fix your problem as EF is such an intricate and varied beast but it is worth giving this a try ...
I am trying to delete the project from the database but I get the following exception:
"DbUpdateException was unhandled"
------------------------------------------------------------
public class Project
{
public Project()
{
Customers = new List<Customer>();
Materials = new List<Material>();
Workers = new List<Worker>();
}
[Key]
public long ProjectID { get; set; }
public DateTime DateCreated { get; set; }
public DateTime DateFinished { get; set; }
public string Name { get; set; }
public string Description { get; set; }
public decimal Price { get; set; }
//Customer TheCustomer = new Customer();
public ICollection<Customer> Customers { get; set; }
public ICollection<Material> Materials { get; set; }
public ICollection<Worker> Workers { get; set; }
}
------------------------------------------------------------------------
if (cb_Projects.SelectedValue != null)
{
using (var db = new ProjectContext())
{
Project p = db.Projects.Find(cb_Projects.SelectedValue);
if (db.Entry(p).State == EntityState.Detached)
{
db.Projects.Attach(p);
}
p.Customers.Clear();
p.Workers.Clear();
p.Materials.Clear();
db.Projects.Remove(p);
db.SaveChanges();
When you called this:
p.Customers.Clear();
p.Workers.Clear();
p.Materials.Clear();
You did noting because it only works if collections are populated moreover if those relations are one-to-many you will also need to delate (call Remove) on every single dependent entity. To populate those collections you must either use eager loading
long selectedValue = cb_Projects.SelectedValue;
Project p = db.Projects.Include(p => p.Customers)
.Include(p => p.Workers)
.Include(p => p.Materials)
.Single(p => p.ProjectID == selectedValue);
or mark all three properties as virtual to enable lazy loading.
Your current code should be handled by cascade delete.
This also doesn't make much sense:
if (db.Entry(p).State == EntityState.Detached)
{
db.Projects.Attach(p);
}
You are searching for the project in the new instance of the context so it will always be loaded from the database and its state will be Unchanged.