Include not working in EntityFramework 6.0? - c#

I have this three model in my databse:
public class Parent{
public Child B {get; set;}
// ....more properties....
}
public class Child{
public City City {get; set;}
// ....more properties....
}
public class City {
public string Name{get; set;}
}
now I want to get all parent including its children and also city of children using this code:
using (var ctx = new DataBaseCtx())
{
var result = ctx.Parents.Include(x => x.B.City)
.Select(x => new ParentDTO
{
B= x.B,
// .... other properties ....
}).ToList();
}
but when i try to access to result.B.City this is null...
where is wrong in my code or approach?

You have to include your child class before calling city
var result = ctx.Parents.Include(x => x.Child).Include(y=>y.City).ToList();

Related

EF Core query and projection to dto doesn't fill the property

How to write EF Core query to fill the properties in certain order?
public record PersonDto : BaseDto //Id is Guid
{
public string Firstname { get; init; }
public string Lastname { get; init; }
public DateOnly Birthday { get; init; }
public IReadOnlyCollection<Guid> AddressesIds { get; init; }
public Guid? MainAddressId { get; init; }
}
internal class Person : SoftDeletableEntity //Id is Guid
{
public Person()
{
Addresses = new HashSet<Address>();
Emails = new HashSet<Email>();
PhoneNumbers = new HashSet<PhoneNumber>();
}
public string Firstname { get; set; }
public string Lastname { get; set; }
public DateOnly Birthday { get; set; }
public ICollection<Address> Addresses { get; set; }
public Guid? MainAddressId => MainAddress?.Id;
public Address? MainAddress => Addresses.Where(adr => adr.IsPrimary).FirstOrDefault();
}
internal sealed partial class Context : DbContext
{
public DbSet<Person> People => Set<Person>();
}
var context = new Context();
var peopleQuery = context.People
.Skip(10)
.Take(10)
.Select(p=> new PersonDto(){
AddressesIds = new HashSet<Guid>(p.Addresses.Select(a => a.Id).Where(a => a.IsPrimary),
MainAddressId = p.MainAddressId,
//bla bla
};
var peopleResult = people.ToList();
At the end of this fragment, peopleResult has all the addresses ids have been loaded, but the MainAddressId of the dto is null.
When I debug the code, MainAddressId is called before populate the list of Addresses, how I change this, or how is this supposed to be done if I'm doing it wrong.
Thanks in advance.
Do you really need MainAddressId and MainAddress properties of the Person. As I understand you have these in the Address model? You can just do the select like this:
var peopleQuery = context.People
.Skip(10)
.Take(10)
.Select(p=> new PersonDto(){
AddressesIds = new HashSet<Guid>(p.Addresses.Select(a=> a.Id),
MainAddressId = p.Addresses.FirstOrDefault(a=> a.IsPrimary),
};
var peopleResult = people.ToList();
You need only the Addresses, then you can easily filter which is the main one, and assign it to the DTO's property.
I Think the problem is that calculated properties in your entity. I had a similar problem a few months ago. I just removed that properties from my Entity an put it in my DTOs.

How to remove list object from an item in List?

I have a class:
public class FlightDetails
{
public string FlightId {get; set;}
public string PilotName {get; set;}
public string Area {get; set;}
public string Country {get; set;}
}
Here sending response:
public async Task<List<FlightDetails>> GetFlightAsync(string FlightId)
{
//
var flights = new List<FlightDetails>();
flights = response.AllFlights;
flights = flights.Where(x => x.FlightId.Contains(FlightId)).ToList();
//
return flights;
}
Getting List here and data is filled but issue is don't want FlightId and Country in the response which I am sending. How to remove this objects in the List? Finally in the List item there should be only PilotName and Area.
Update:
I forgot the following line before the flights = response.AllFlights;
var request = await _rest.Get<WorldFlights>(url + $"?FlightId={FlightId}");
You will need to create another object, and map there only the properties you want. For example:
public class FlightDetailsResponse
{
public string PilotName {get; set;}
public string Area {get; set;}
}
And then in the function:
public async Task<List<FlightDetailsResponse>> GetFlightAsync(string FlightId){
var flights = response.AllFlights;
var flightResponses = flights
.Where(x => x.FlightId.Contains(FlightId).ToList())
.Select(x => new FlightDetailsResponse{
PilotName = x.PilotName,
Area = x.Area
});
return flightResponses;
}
This way the response will only contain the PilotName and Area fields.
PD: What I wrote might not compile because of missing commas or something like that. Sorry, it has been some time since I last wrote C#, but the idea is that.

Auto populate properties in class

Suppose that I have a class name User, is there any ways to populate the class properties and default value in Visual Studio as follow. I feel it is tedious to repeat typing the properties again and again.
Probably there is default shortcut to do this in Visual Studio or any extension that can do this?
Case 1
var user = db.User.Where(x => x.UserID).Select(o => new User
{
UserId = o.UserId, // auto populate
Username = o.Username, // auto populate
........ // auto populate all properties inside User class
}
);
case 2
var user = new User();
user.UserID = "", // auto populate with type default value
user.Username = "", // auto populate with type default value
........ // auto populate all properties inside User class
........ // auto populate all properties inside User class
I'm not quite sure what are you trying to achieve here
var user = db.User.Where(x => x.UserID).Select(o => new User
{
UserId = o.UserId, // auto populate
Username = o.Username, // auto populate
........ // auto populate all properties inside User class
}
since both new User and db.User are the same type of class/entity. But let's say you have an entity model User and a DTO/View model called UserViewModel and you want automatically to map the values of User to UserViewModel you can use automapper https://docs.automapper.org/en/stable/Getting-started.html Example this is the Entity definition
public class User
{
public int Id {get; set;}
public string FirstName {get; set;}
public string LastName {get; set;}
public int Age {get; set;}
}
And we have a UserViewModel to which you want to map the data from User
public class UserViewModel
{
public int Id {get; set;}
public string FirstName {get; set;}
public string LastName {get; set;}
public int Age {get; set;}
}
With AutoMapper you can do this
var configuration = new MapperConfiguration(config =>
{
config.CreateMap<User, UserViewModel>();
}
and then you can use the mapper configuration like this
var user = db.User.Where(x => x.UserID).First();
var userViewModel = configuration.CreateMapper().Map<UserViewModel>(user);
This will automatically populate the property values of User to UserViewModel. Alternatively you can have a view model like this
public class UserViewModel
{
public int Id {get; set;}
public string FullName {get; set;}
public int Age {get; set;}
}
This time not all properties of User and UserViewModel match one to one and you will have to set a custom mapper configuration i.e.
var configuration = new MapperConfiguration(config =>
{
config.CreateMap<User, UserViewModel>()
.ForMember(uvm => uvm.FullName, o => o.MapFrom($"{u.FirstName} {u.LastName}") );
}
This way you are configuring the automatic User to UserViewModel mapping to map the FullName property value by concatenating FirstName and LastName from User
Well I have build a library that could solve this problem for you, called FastDeepCloner
public class User
{
public virtual string Name { get; set; } = "sdjh";
public virtual int PasswordLength { get; set; } = 6;
public Circular Test { get; set; } = new Circular();
}
public class CloneToTest
{
[FastDeepCloner.FastDeepClonerColumn("Name")]
[FastDeepCloner.FastDeepClonerColumn("LastName")] // Or
public string FullName { get; set; }
// You see here the type could be difrrent then the orginal type.
// FastDeepCloner will try to convert it, if it fail then a default value will be inserted insted
public string PasswordLength { get; set; }
// You could add a path insted, remember this only work on none list items.
[FastDeepClonerColumn("Test.myBar.Id")]
public int Id { get; set; }
public Circular Test { get; set; }
}
Now simple use
var user = new User() { Name = "alen toma" };
var cloneTo =new CloneToTest();
FastDeepCloner.DeepCloner.CloneTo(user, cloneTo);
Assert.AreEqual(user.Name, cloneTo.FullName);

C# Entity Framework - returning list of single entity after a join lambda query

I have two classes:
public class classA
{
public string keyA {get; set;}
public string propAA {get; set;}
public string propAB {get; set;}
public string propAC {get; set;}
}
public class classB
{
public string keyB {get; set;}
public string foreignkeyA {get; set;}
public string propBA {get; set;}
public string propBB {get; set;}
public string propBC {get; set;}
}
If I do a join between them..
var query = db.ClasseA.
Join(db.ClasseB, a => a.keyA, b => b.foreignkeyA,
(ca, cb) => new { ca, cb })
.Where(q => q.cb.propBC == 'some value')
.Select();
The result is a combination (list) of classA and classB objects.
But, I need that the result is only a list of class A.
Something like:
List<classA> query = db.ClasseA.
Join(db.ClasseB, a => a.keyA, b => b.foreignkeyA,
(ca, cb) => new { ca, cb })
.Where(q => q.cb.propBC == 'some value')
.Select(????);
How can I do that??
One example
var query = from obj1 in db.Object1
join obj2 in db.Object2 on obj1.key equals obj2.somekey
where obj1.something = "something"
select obj1
This will join the two but only bring back obj1 items
reference: https://msdn.microsoft.com/en-us/library/bb311040.aspx

Does Entity Framework see property change inside object which is already in database?

I have two model classes:
public class Person {
public int Id { get; set; }
public string FirstName { get; set; }
public virtual List<Desert> Deserts {get;set;}
}
public class Desert{
public int Id { get; set; }
public string Name { get; set; }
public virtual List<Person> Persons{get;set}
}
If I add some persons to the database in Seed method and add their favourite deserts to them:
var deserts = new List<Desert>{new Desert{Name = "IceCream"}, new Desert{Name = "Cake"}}
var persons = new List<Person> {
new Person{FirstName = "James", Deserts = deserts},
new Person{FirstName = "Joanna"}
};
persons.ForEach(person => context.Persons.AddOrUpdate(person));
context.SaveChanges();
Then somewhere in controller invoke method which removes desert from person(connection/relationship between person and desert) :
//removes desert from a person
public void RemoveDesert(int personId, int desertToRemoveId) {
Person person = db.Persons.Find(personId);
foreach(Desert desert in person.Deserts){
if(desert.Id == desertToRemoveId){
person.Deserts.Remove(desert);
}
}
context.saveChanges();
}
Will Entity Framework see that property public virtual List<Desert> FavouriteDeserts {get;set;} changed and update it?
If not how to remove a desert which has Id=desertToRemoveId from a person which has Id=personId ans save changes in database? Can it be done without Lambda expressions?
Yes, it keeps track of child objects unless you tell it not to.
foreach(Desert desert in person.Deserts){
if(desert.Id == desertToRemoveId){
person.Deserts.Remove(desert);
}
}
I don't think C# will allow you to modify a collection while iterating it.
That said, you can always query the object you want to remove and then remove it:
var desertToRemove = person.Deserts.FirstOrDefault(x=> x.Id == desertToRemoveId);
if(desertToRemove != null) person.Deserts.Remove(desertToRemove );
Note that this however, only removes the relation (between person and desert), not the child entity. Read up on this subtle difference in this excellent answer here.
Also, you need to change your classes to use ICollection instead of List:
public virtual ICollection<Person> Persons {get; set; }
public virtual ICollection<Desert> Deserts {get; set;}

Categories