How to create and add object to collection of a class - entity-framework-core

I have two entities - Movie and Review. I want to create a new review for a specific movie. Then add that review in the collection in the Movie entity. When I debug the code below, I can read the movie variable and see that it does indeed have this new review in its collections. But when I get the same movie again, the review collection is empty.
Movie
Review
Creating and adding the review
var movie = data.Movies.Find(5);
var newReview = new Review
{
Content = "Example",
MovieId = 5
};
movie.Reviews.Add(newReview);
data.Reviews.Add(newReview);
data.SaveChanges();

The problem is fetching the data again.
Based on your comment, you find the movie again using
data.Movies.Find(5)
However, this will not automatically bring the related data back with the movie. EF Core requires by default to eagerly load those related entities using .Include()
To remedy the data fetching for your query it would look like this:
data.Movies
.Include(m => m.Reviews)
.FirstOrDefault(5)
If a movie could have zero reviews and you want to perform a true LEFT JOIN, then modify the above to:
data.Movies
.Include(m => m.Reviews).DefaultIfEmpty()
.FirstOrDefault(5)

Related

Is there a better way to perform a query like this in Entity Framework

I have a model called User and each user has a list of another model called Hobbies. I want to retrieve a User who has a hobby with a certain ID, is there anything better using Entity Framework than just retrieving all users then searching inside each user's hobbies list and match it with the hobby id, a pseudo code would look like the following
UserList = select all users from db
targetUser = null;
for User in UsersList:
for Hobby in User.HobbiesList:
if(Hobby.ID == currentHobby.ID)
{
targetUser = User;
}
First of all, EF won't automatically get all the linked entities, you need to explicitly Include everything you want to see in the end result.
As for the question, yes of course, you can use all the standard LINQ filters when working with EF. In your case
db.Userlist.Where(user => user.HobbiesList.Any(hobby => hobby.ID == currentHobby.ID))
Don't forget to Include(user => user.HobbiesList) if you want to see it in the results.

how to audit log inserts (adds) of records using tracker enabled dbcontext

We are using https://github.com/bilal-fazlani/tracker-enabled-dbcontext
to create an audit trail of changes. We'd also like to record inserts into the trail of new records. we can loop though the entities just added but there seems to be no way of getting the ID for the entity just added?
A year ago there was an article written suggesting it's a limitation / not possible - https://www.exceptionnotfound.net/entity-change-tracking-using-dbcontext-in-entity-framework-6/
but there are also some comments suggesting there is a way. we studied those and the related code but are not any clearer, is it actually possible to audit inserts properly with this framework?
foreach (var entryAdded in addedEntities)
{
var entityKeyObject = objectContext.ObjectStateManager.GetObjectStateEntry(entryAdded.Entity).EntityKey;
var entityKey = entityKeyObject.EntityKeyValues.Length >= 1 ? entityKeyObject.EntityKeyValues[0].Value : 0;
// insert into audit log here..
}
Yes, it's possible to get inserted ID for entity just added.
In short, you simply need to handle two events:
PreSaveChanges: Get information not related to primary key
PostSaveChanges: Get information related to the primary key (which has been generated)
All the codes can be found using the link. So I cannot answer how to make it with this library but at least, I can ensure you at 100% it's possible.
One alternative is using another Entity Framework Audit Library
Disclaimer: I'm the owner of the project Entity Framework Plus
Wiki: EF+ Audit
This library allows you to audit & save information in a database. By default, it already supports ID added.
// using Z.EntityFramework.Plus; // Don't forget to include this.
var ctx = new EntityContext();
// ... ctx changes ...
var audit = new Audit();
audit.CreatedBy = "ZZZ Projects"; // Optional
ctx.SaveChanges(audit);
// Access to all auditing information
var entries = audit.Entries;
foreach(var entry in entries)
{
foreach(var property in entry.Properties)
{
}
}

Attaching an related entity to new entity, without retrieving from database

I've just started working with Web API this week, and I'm struggling with something which I think should be quite simple, but haven't been able to find the answer for yet. Perhaps I'm searching using the wrong terms.
One of the calls to the API passes through a GUID. I need to create a new entity (using Entity Framework) and set one of the relations to this newly passed in GUID. This GUID is the ID of a different entity in the database.
I'm struggling to attach the entity via the relation without fetching the whole entity too.
For example,
public void DoWork(IList<Guid> userGuids)
{
Order order = new Order() // This is an entity
{
CreateDate = DateTime.Now,
CreatedBy = "Me",
Items = (from i in this.Model.Items
where i.Id == userGuid
select i).ToList<Item>();
}
Model.Orders.Add(order);
Model.SaveAll();
}
In the above, I have to do a database call to attach the Item entities to the Order. Is there not a way around this? Seems very redundant to retrieve the whole entity objects when I only require their IDs (which I already have anyway!)!
One solution is stub entities as asked here: Create new EF object with foreign key reference without loading whole rereference object
Link to the source blog referenced: http://blogs.msdn.com/b/alexj/archive/2009/06/19/tip-26-how-to-avoid-database-queries-using-stub-entities.aspx
Snip from the blog - to be applied to your situation:
Category category = new Category { ID = 5};
ctx.AttachTo(“Categories”,category);
Product product = new Product {
Name = “Bovril”,
Category = category
};
ctx.AddToProducts(product);
ctx.SaveChanges();
This way (in the example) the Product is saved without ever loading the Category object.

One view, multiple nested forms, multiple tables

Background
I was given the task of writing a Small Business online database. This database is to include a lot of info as well as info on their directors and branches. Since any business can have an unlimited amount of directors and branches, I need to create a database that is not limited to just one director and/or branch.
What do I have
Currently I have 3 tables.
smmes [id, company_name, trading_name, business_address, registration_number, tax_reference, vat_number, bbbee_status, employees, awards, created, modified]
ownerships [id, smme_id, name, surname, gender, age, race, disability, qualification, created, modified]
branches [id, smme_id, location, contact_number, contact_person, created, modified]
Note: smme_id is the id of the company in smmes that the branch or director belongs to.
And I have a view for the SMME's.
What is my question
I'm VERY new to cakePHP (in fact, this is my first app I'm creating with cakePHP). I want to know how I can make one form where a user can enter all this detail and then add the details for all directors and branches from one view. I would prefer that they do not have various views to go through to create all the details. Add to that, this one view should then save all the data to the correct tables with the correct smme_id.
Is this possible or should I rather leave cakePHP and write it manually.
You can load model on demand in your controller and then pass model specific data(received from posted form) to loaded model's save method.
public function detail(){
if($this->request->is('post')): // update only when form is posted
$this->loadModel('ownerships');
$owner_name= $this->request->data['Ownername'];
$ownerships_data = array('Ownership' = > array(
'name' = > $owner_name
//add other keys from posted form
)
);
$this->Ownership->saveAll($ownerships_data);
// load other models for saving posted data in related tables
endif;
}
Similarly load other models and pass fields from posted form as array to it's save method.
Suppose URL format is http://example.com/director/detail.So you would like to put above method(termed as action in MVC terminology) in app/controllers/directors_controller.php
Generally if URL format is http://somesite.com/abc/xyz it will look for xyz action in
app/controllers/abcs_controller.php
You can read more about cake conventions here

Adding & Removing Associations - Entity Framework

I'm trying to get to grips with EF this week and I'm going ok so far but I've just hit my first major snag. I have a table of items and a table of categories. Each item can be 'tagged' with many categories so I created a link table. Two columns, one the primary ID of the item, the other the primary ID of the category. I added some data manually to the DB and I can query it all fine through EF in my code.
Now I want to 'tag' a new item with one of the existing categories. I have the category ID to add and the ID of the Item. I load both as entities using linq and then try the following.
int categoryToAddId = Convert.ToInt32(ddlCategoriesRemaining.SelectedValue);
var categoryToAdd = db.CollectionCategorySet.First(x => x.ID == categoryToAddId);
currentCollectionItem.Categories.Add(categoryToAdd);
db.SaveChanges();
But I get "Unable to update the EntitySet 'collectionItemCategories' because it has a DefiningQuery and no element exists in the element to support the current operation."
Have I missed something? Is this not the right way to do it? I try the same thing for removing and no luck there either.
I think I have managed to answer this one myself. After alot of digging around it turns out that the Entity Framework (as it comes in VS2008 SP1) doesn't actually support many to many relationships very well. The framework does create a list of objects from another object through the relationship which is very nice but when it comes to adding and removing the relationships this can't be done very easily. You need to write your own stored procedures to do this and then register them with Entity Framework using the Function Import route.
There is also a further problem with this route in that function imports that don't return anything such as adding a many to many relationship don't get added to the object context. So when your writing code you can't just use them as you would expect.
For now I'm going to simply stick to executing these procedures in the old fashioned way using executenonquery(). Apparently better support for this is supposed to arrive in VS2010.
If anyone feels I have got my facts wrong please feel free to put me right.
After you have created your Item object, you need to set the Item object to the Category object on the Item's Categories property. If you are adding a new Item object, do something like this:
Using (YourContext ctx = new YourContext())
{
//Create new Item object
Item oItem = new Item();
//Generate new Guid for Item object (sample)
oItem.ID = new Guid();
//Assign a new Title for Item object (sample)
oItem.Title = "Some Title";
//Get the CategoryID to apply to the new Item from a DropDownList
int categoryToAddId = Convert.ToInt32(ddlCategoriesRemaining.SelectedValue);
//Instantiate a Category object where Category equals categoryToAddId
var oCategory = db.CategorySet.First(x => x.ID == categoryToAddId);
//Set Item object's Categories property to the Category object
oItem.Categories = oCategory;
//Add new Item object to db context for saving
ctx.AddtoItemSet(oItem);
//Save to Database
ctx.SaveChanges();
}
Have you put foreign keys on both columns in your link table to the item and the category or defined the relationship as many to many in the Mapping Details?