Update Entity without affecting its Navigation Property - c#

in .net core 3.1
if i has a model class that has a navigation property inside of it,
public class Department
{
public int ID { get;set; }
public string Name { get; set; }
public List<Student> Students { get; set; }
}
public class Student
{
public int ID { get; set;}
public string Name { get; set;}
}
According to the models before what is the difference between .AsNoTracking() and with tracking related to the Navigation Property
For Example
if i retrieve object of Department with no tracking will it populate the Student list or not and the same for withTracking
and another thing is
if i want to update the Department without affecting the Student list inside of it, how should i achieve that.
and last this because i have been through the docs without understanding
what EntityState.Detached means
and the difference between DBSet.Attach() and DBSet.Update()

There is a fairly good article that describes the behavior of tracking here: https://learn.microsoft.com/en-us/ef/core/querying/tracking.
As for Detach/Attach you can use the following as a guide. The example will provide some commentary around its usage.
https://learn.microsoft.com/en-us/dotnet/api/system.data.objects.objectcontext.detach?view=netframework-4.8
https://learn.microsoft.com/en-us/dotnet/api/system.data.objects.objectcontext.attach?view=netframework-4.8&viewFallbackFrom=netcore-3.1
As for updating without affecting the students what I usually do is make sure that I use the .Include(x=> x.Students) then perform the update on the entity object I am interested in and save those changes to the context.
Lastly there is a fantastic book that you can also read called Entity Framework Core in action. I found it very helpful when starting out with EF.
Hope this helps. Good Luck.

Related

MVC ViewModel EntityFrameWork ICollection,IEnumerable virtual Class

I have gone through a lot of documents and found several ways of trying to make a CreateViewModel.
I want to know what does ICollection Does.
public Payment{
public int ID {get; set;}
public string Details {get; set;}
public virtual ICollection<Expense> Expenses {get; set;}
}
public Expense{
public int ID {get; set;}
public string MyDetails {get; set;}
}
So I guess in my View, I can Use just the plain class as virtual to make a Create Method. But if I want to use ViewModel to make a view with 2 or more DataModels for Creation. How will I go with that. Cause I guess I can always just make a
public virtual Payment payments {get; set;}
public virtual Expense expenses {get; set;}
But I am trying to ready this for Dynamically Having a Add Button Generating an Expense Details Input.
Not to mention, the IEnumerable as well, but I guess this needs an ID more suitable for Editing and Details for what I understand.
All you need is a PaymentViewModel class, where you'll use List<Expense> instead of ICollection<Expense>:
public class PaymentViewModel
{
// ID property unnecessary here because it doesn't need
// to be posted from the form
public string Details { get; set; }
// You may want to use something like `List<ExpenseViewModel>`
// based on your needs
public List<Expense> Expenses { get; set; }
}
With that, you add additional expense records to your form by making sure the input names are in the format of Expenses[N].MyDetails, where N is the index. Whatever JavaScript solution you use to add additional expense records to the form should create these inputs with properly indexed names. This would be a good place to use a JavaScript templating solution, or something that handles data-binding like Knockout.
For editing existing expenses, should you have the need, you just generate the fields like with any collection, but you need to use a for loop rather than the more traditional foreach:
#for (var i = 0; i < Model.Expenses.Count(); i++)
{
#Html.EditorFor(m => m.Expenses[i].MyDetails)
}
As a side note, since you asked, ICollection<T> is the required type for entity navigation properties with Entity Framework. These reasons are very low level, but has to do with the way Entity Framework handles one-to-many and many-to-many relationships at an object level and issues such as lazy loading. However, ICollection<T> doesn't work for actually submitting items through an HTML form, due mostly to the fact that it's not indexable. That's why a view model is so important in this scenario.

retrieving database-computed properties of classes within entity framework

I have a good understanding of EF, and generated my database successfully. Now, I am struggling with adding dynamic properties to one of the entity classes. For example, I have a Post class, and other users can make comments to the posts. When I list the existing posts, I want to display the number of comments made to corresponding post.
One solution might be having a property called CommentCount, and updating the Post by increasing the (int) value of the CommentCount property by 1 when a new comment is made.
The other solution, and I think it is a better solution, is that when retrieving the post from the DB, the number of comments associated with the post can be computed and retrieved at the same time and assigned to CommentCount property of the post instance. However, I do not know how to achieve this with EF.
Which approach is highly recommended? Or, is there any other ways of doing this? If it is the second one, how can I achieve this with EF?
1) You should simply consider not putting the property called CommentCount into your model. When you develop for example a WPF Windows application, you should consider using MVVM pattern and the CommentCount would belong to your ViewModel class and not to your Model class. There you implement INotifyPropertyChanged and you can use it from your frontend Views. Analogically there is MVC pattern for ASP.NET etc.
There are other design patterns like Repository pattern. Using this pattern you can create the CommentCount in your repository class and not in your
model class. This would be similar to your second solution.
2) I assume from your question that you are using code-first approach:
generated my database successfully
If you do so and you wish to include CommentCount directly in your Model class, you can do it this by adding partial class file to your project like this:
namespace DBModel.Models
{
public partial class Post
{
public int CommentsCount
{
get { return this.Comments.Count; }
}
...
But I cannot see why to create extra property in your model just for that.
On the other hand adding this field as a computed field into your SQL database could make sense and then it would be part of your EF model.
If you calculation is very complex you should try creating a View in your DB and then add it to your Model?
But if your Model have something simple like
class Post {
public int postid { get; set; }
public virtual ICollection<comment> comment { get; set; }
}
In your controller you can do
db.post(x => x.postid == yourid).comments.count()
to get total of comment
or in your view
#foreach (var item in Model)
{
<li>item.postid;</li>
<li>item.comment.Count();</li>
}
Or update your class
class Post {
public int postid { get; set; }
public virtual ICollection<comment> comment { get; set; }
public int CommentCount
{
get
{
return comment.Count();
}
}
}
Just remember bring related data in your query.
In my case POI have properties parish_id, sector_id, city_id and parish have municipality, and municipality have state.
Using this query I can get Poi with all the related data.
filter = db.poi
.Include("parish")
.Include("sector")
.Include("city")
.Include("parish.municipality")
.Include("parish.municipality.state")
.Where(x => x.sector_id == SectorID);

Entity Framework Code First Relationships; what am I missing?

I'm experimenting with EF5 Code First and I am using the models (show below).
When I look at the database that is created, I am confused because I do not see anything in the Track table that points to the Category table. Category has a FK pointing back to Track but that means that there are going to be duplicates of the categories?
A little background: I am trying to build a model that has tracks and every track can have 1 to N Categories. All of the categories are already defined, that is they are basically a lookup and I plan to create them in the seed method when database is created.
I think I am not understanding something obvious... When I query a track, how will I know what category it contains?
Thx
public class Track : IAuditInfo
{
public Int32 Id { get; set; }
public String Name { get; set; }
public String Description { get; set; }
public String Data { get; set; }
public DateTime CreatedOn { get; set; }
public DateTime ModifiedOn { get; set; }
public ICollection<Category> Categories { get; set; }
public Track()
{
Categories = new List<Category>();
}
}
public class Category
{
public Int32 Id { get; set; }
public Boolean IsVisible { get; set; }
public String DisplayName { get; set; }
}
Your current model is a one-to-many relationship between tracks and categories.
This usually implemented, as you have noted that entity framework does, using a foreign key on the many side (category) to the one side (track).
If I understand you correctly, what you want is a many-to-many relationship. Many tracks can be related to the same category, and a single track can belong to many categories.
To let entity framework understand that you want a many-to-many relationship you can simply add a ICollection property to your category class.
So both your classes should have a collection of the other class.
I.e. tracks have many categories and categories have many tracks.
For more information you can also see: http://msdn.microsoft.com/en-us/data/hh134698.a.nospx
Olav is right, your data model at the moment is not telling Entity Framework that there is a many-to-many relationship in there.
The simplest way to resolve this is to add
public virtual ICollection<Track> Tracks { get; set; }
to your Category class.
However... You may not want to pollute your domain model with artefacts that are not relevant to your domain. More importantly, when you do it this way, it is up to Entity Framework to figure out what to call the binding table. Prior to EF6 this naming is non deterministic (see http://entityframework.codeplex.com/workitem/1677), which may mean that two different machines compiling the same code will decide on different names for that table and cause some interesting migration problems in your production system.
The answer to both problems is to always explicitly manage many-to-many relationships with Fluent Configuration.
In your Data Context class, override the OnModelCreating, something like this:
public class MyDb : DbContext
{
public IDbSet<Track> Tracks { get; set; }
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
modelBuilder.Entity<Track>()
.HasMany(t => t.Categories)
.WithMany()
.Map(c => c.ToTable("CategoriesForTracks"));
}
}
If you do this, you don't need to add a navigation property to your Category class, though you still can (if you do, you should use the overload for WithMany that allows you to specify a property).
Relationships between entities and how to map that to a relational database is inherently hard. For anything other than the simplest parent-child relationships you will want to use the fluent API to make sure you actually get what you want.
Morteza Manavi has a really good blog series describing relationships in EF Code First in exhaustive detail.
NOTE
You should usually make navigation properties virtual. So, you should change your Category class like this:
public virtual ICollection<Category> Categories { get; set; }
In theory, not making it virtual should just cause eager loading rather than lazy loading to happen. In practice I have always found lots of subtle bugs appearing when my navigation properties are not virtual.

How to map a Value Type which has a reference to an entity?

I'm having a problem with a mapping in Entity Framework.
I have the following classes (simplified):
public class Building
{
public int ID { get; set; }
// *.. snip..* other properties
public Location Location { get; private set; }
}
public class Location
{
public string Street {get; set;}
public Country country {get; set}
}
public class Country
{
public int ID { get; set; }
public string Name { get; set; }
}
Building and Country are entities, they are saved in the database. Location is a value type and should map to the same table as Building.
However, when I map it this way, entity framework wants to map Location to a table as well and is complaining it has no Key. I don't want to give it a key since it belongs to the Building and should not be an entity at all.
I have seen workarounds which say you need to put Country on the Building-class, but that just doesn't feel good (and is semantically just plain wrong).
I'm using Entity Framework 5
Since the release of Entity Framework Core 2, it is now possible to achieve this using owned entities.
Your configuration would look like:
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
// ...
modelBuilder.Entity<Building>().OwnsOne(x => x.Location);
modelBuilder.Entity<Location>().HasOne(x => x.Country);
// ...
}
This way, properties from Location class will be a part of the table Building class is mapped to. This means you will only have tables for Building and Country classes and the Building table will have a foreign key to the Country table.
I know it's been a long since you posted the question, but I thought this answer might be helpful to someone who comes across this question.
In my opinion the Entity Framework shouldn't allow such a case.
I understand that you don't consider the Location as an Entity but adding entity references to complex types doesn't seem like a solid approach either. The relationship of a building to a country is quite straight forward. A building belongs to one country. Thus a building model should include a country id. What would you expect to be mapped?
If you would expect the table Building to have just three columns ID, Street, CountryId and you still want to hold the Location model then you should use the following complex type.
public class Location
{
public string Street {get; set;}
public int countryId {get; set}
}
If however you would expect your Building table to have all the fields from the model Country then that could lead to some tricky situations like what would happen If you wanted to add new fields to the Country model or If you wanted to add other complex types or entities to your Country model according to a new Business Case.
Those cases would mess with the relational concept and would over-complicate your structure without any meaningful reason. (in my opinion of course)
You may mark Location property in Building class with [NotMapped] Attribute.
using System.ComponentModel.DataAnnotations.Schema;
public class Building
{
[NotMapped]
public Location Location { get; private set; }
}
Hope that solves your problem!

Using Entity Framework classes in WCF service without recreating the model

I have a dll project that holds an edmx. In the same solution, I have a WCF Library project and within this project is an interface and a class to hold all OperationContracts. I've noticed that this works great with just one table in the model. Once I add another table to the edmx with a relationship to the first table, the service breaks. So, I've narrowed down that the errors I've been receiving are (in a general sense) due to the relationships between my EF types and more specifically the way the relationships are declared within each class... So basically I just went in and found the following in my Person class:
public int PersonId { get; set; }
public string FirstName { get; set; }
public string LastName { get; set; }
public string DisplayName { get; set; }
public bool IsEmployee { get; set; }
public Nullable<int> OrganizationId { get; set; }
public virtual ICollection<Contact> Contacts { get; set; }
So this led me to (and I have no idea why) try to simple remove the virtual keyword on the relationship with Contact and POOF!!!! Like magic the service was up and running just as it was with the single Person table being the only class from the model.
So my question is this... why?
Why when I remove the virtual keyword does the WCF service suddenly work? And what does this mean in regards to the relationship between Person and Contact?
I have a theory that the reason this happens is because now by removing the virtual keyword I have somehow broke the connection between Person and Contact in such a way that the service is no longer trying to pull in any more information than just the Person... and thus doesn't break?
I apologize for my naivety on this matter but would greatly appreciate a little clarity.
Thanks in advance.
The virtual keyword tells Entity Framework that it can (if you tell it too) use Lazy loading to load the elements in the collection when you query the collection.
So removing the virtual keyword means that Lazy loading will not work any more. But if you're fine with that then that's great.

Categories

Resources