How to give DTO to select or SelectMany
public class PersonDTO
{
public int Id { get; set; }
public string Name { get; set; }
public string UserName { get; set; }
public string Email { get; set; }
}
var person = _context.Persons.Where(x=>x.PersonId==1).SelectMany(PersonDTO);
I want to build a service that I give model or DTO and it returns only those raws that are given in the model or DTO but only from the person table or user detail table
which means something like graphql that get data by DTO, and DTO could be Only Name or UserName or Email or all of them, any Idea?
_context.Persons.Where(x => x.PersonId == 1).Select(x => typeof(PersonDTO)); won't automagically map your type.
What you probably want is something like this, when doing it "manually":
_context.Persons
.Where(x => x.PersonId == 1)
.Select(x => new PersonDto(){
Id = x.Id,
Name = x.Name,
UserName = x.UserName,
Email = x.Email
});
Related
I have this method for my API where I'm trying to filter out a user (userId) and group by EventId. Then I want to select three columns from the table: name, eventId and UserId.
This is my code but it's not working:
[HttpGet("[action]/{userId}")]
public IActionResult EventsMatchFiltered(int userId)
{
var events = _dbContext.Events.Where(event1 => event1.UserId != userId)
.GroupBy(g => new {g.EventId })
.SelectMany(g => g.Select(h => new
{
Name = h.Name,
h.EventId,
h.UserId
}));
return Ok(events);
}
Firstly, events would be an IQueryable rather than a materialized collection. Second, it would be an anonymous type rather than a proper DTO/ViewModel serializable class. Thirdly, you are grouping by an EventId, but then not "selecting" anything based on that grouping. EventId sounds like the Primary Key of the Event you are supposedly grouping.. Are you sure you don't actually mean to Group by User??
Normally for something like a Grouped result you would have a view model something like:
[Serializable]
public class UserEventSummaryViewModel
{
public int UserId { get; set; }
public ICollection<EventSummaryViewModel> Events { get; set; } = new List<EventSummaryViewModel>();
}
[Serializable]
public class EventSummaryViewModel
{
public int EventId { get; set; }
public string Name { get; set; }
}
then to query and fill:
var otherUserEvents = _dbContext.Events
.Where(e => e.UserId != userId)
.GroupBy(g => g.UserId )
.Select(g => new UserEventSummaryViewModel
{
UserId == g.Key,
Events = g.Select(e => new EventSummaryViewModel
{
EventId = e.EventId,
Name = e.Name
}).ToList()
}).ToList();
This would provide a structure of events grouped by each of the other Users. If you want UserId and UserName then add the UserName property to the UserEventSummary view model and in the GroupBy clause:
.GroupBy(g => new { g.UserId, g.Name } )
and populate these in the viewmodel with:
UserId == g.Key.UserId,
UserName = g.Key.Name,
I have four classes Offer, Section, Field, and Option:
Where the offer has sections and every section has some fields and each field has some options as shown:
public class Offer
{
public int Id { get; set; }
public string Name { get; set; }
public ICollection<Section> Sections { get; set; }
}
public class Section
{
public int Id { get; set; }
public string Name { get; set; }
public ICollection<Field> Fields { get; set; }
}
public class Field
{
public int Id { get; set; }
public string Type { get; set; } //[question, group]
public ICollection<Option> Options { get; set; }
}
public class Option
{
public int Id { get; set; }
public string Name { get; set; }
}
I tried to get the offer by id including the nested entities and this code works perfectly:
var offer = _context.Offers
.Include(o => o.Sections
.Select(s => s.Fields
.Select(f => f.Options)))
.FirstOrDefault(o => o.Id == offerId);
The problem is when I try to filter the Fields by 'type' like this:
var offer = _context.Offers
.Include(o => o.Sections
.Select(s => s.Fields.Where(f => f.Type == "question")
.Select(f => f.Options)))
.FirstOrDefault(o => o.Id == offerId);
and I get this error:
The Include path expression must refer to a navigation property defined on the type. Use dotted paths for reference navigation properties and the Select operator for collection navigation properties.
Parameter name: path
I've reviewed lots of questions and still cannot achieve that :(
Linq: query with three nested levels
EF LINQ include nested entities [duplicate]
Using LINQ to query three entitites. - Include path expression must refer to a navigation property defined on the type
Include() is used for Eager loading. It is the process whereby a query for one type of entity also loads related entities as part of the query, so that we don't need to execute a separate query for related entities. Where() is currently not supported inside Include.
If you want to just filter the result you can do something like this :
var offer = _context.Offers
.Include(o => o.Sections
.Select(s => s.Fields
.Select(f => f.Options)))
.FirstOrDefault(o => o.Id == offerId);
var filtered_offer =
new Offer
{
Sections = offer.Sections.Select(S => new Section
{
Id = S.Id,
Name = S.Name,
Fields = S.Fields.Where(f => f.Type == "question").ToList()
}).ToList(),
Id = offer.Id,
Name = offer.Name
};
I'm trying to get related tables Adress and PlzOrt into an object User.
The relationships are as follows:
User 1:1 Adress
Adress n:1 PlzOrt
Entities Scaffolded from the DB
public partial class User
{
//No hash sets for Adress Scaffolded (probably unnecessary since the is
//only ever one address per user
public int IdUser { get; set; }
public int? FidAdresse { get; set; }
public string Email { get; set; }
public string Vorname { get; set; }
public string Nachmname { get; set; }
[ForeignKey("FidAdresse")]
[InverseProperty("User")]
public virtual Adresse FidAdresseNavigation { get; set; }
}
public partial class Adresse
{
public Adresse()
{
User = new HashSet<User>();
}
public int IdAdresse { get; set; }
public int FidPlzOrt { get; set; }
public string Strasse { get; set; }
public string Hausnr { get; set; }
[ForeignKey("FidPlzOrt")]
[InverseProperty("Adresse")]
public virtual PlzOrt FidPlzOrtNavigation { get; set; }
[InverseProperty("FidAdresseNavigation")]
public virtual ICollection<User> User { get; set; }
}
public partial class PlzOrt
{
public PlzOrt()
{
Adresse = new HashSet<Adresse>();
}
public int IdPlzOrt { get; set; }
public string Plz { get; set; }
public string Ort { get; set; }
[InverseProperty("FidPlzOrtNavigation")]
public virtual ICollection<Adresse> Adresse { get; set; }
}
Here´s the linq that does not work.
return _context.User
.Include(u => u.FidPermissionNavigation)
.Include(c => c.FidAdresseNavigation)
.ThenInclude(c => c.FidPlzOrtNavigation)
.FirstOrDefault(c => c.IdUser == id);
The linq work when I don´t include the "ThenInclude(c => c.FidPlzOrtNavigation)" statement, but I want this information in my object.
Here´s the C# that gives me the expected results:
public User GetUser(int id)
{
foreach (User user in _context.User)
{
if (user.IdUser == id)
{
foreach (Adresse adresse in _context.Adresse)
{
if (adresse.IdAdresse == user.FidAdresse)
{
user.FidAdresseNavigation = adresse;
foreach (PlzOrt plzOrt in _context.PlzOrt)
{
if (plzOrt.IdPlzOrt == adresse.FidPlzOrt)
{
user.FidAdresseNavigation.FidPlzOrtNavigation = plzOrt;
break;
}
}
break;
}
}
return user;
}
}
return null;
}
Translating this linq statement would be of great help. Thanks in advance.
Generated db_context code in case you are interested or this helps
modelBuilder.Entity<User>(entity =>
{
entity.HasKey(e => e.IdUser)
.HasName("PRIMARY");
entity.HasIndex(e => e.FidAdresse)
.HasName("fk_User_Adresse1_idx");
entity.HasOne(d => d.FidAdresseNavigation)
.WithMany(p => p.User)
.HasForeignKey(d => d.FidAdresse)
.HasConstraintName("fk_User_Adresse1");
});
modelBuilder.Entity<Adresse>(entity =>
{
entity.HasKey(e => e.IdAdresse)
.HasName("PRIMARY");
entity.HasIndex(e => e.FidPlzOrt)
.HasName("fk_Adresse_plz_ort1_idx");
entity.HasOne(d => d.FidPlzOrtNavigation)
.WithMany(p => p.Adresse)
.HasForeignKey(d => d.FidPlzOrt)
.OnDelete(DeleteBehavior.ClientSetNull)
.HasConstraintName("fk_Adresse_plz_ort1");
});
modelBuilder.Entity<PlzOrt>(entity =>
{
entity.HasKey(e => e.IdPlzOrt)
.HasName("PRIMARY");
});
So you have an id, and you want the one and only user that has this id as primary key, together with his Address and his PlzOrt.
Whenever you query, use Select to fetch the data. Only use Include if you want to update the fetched data.
The reason for Select is that you have greater freedom of what you select. Besides you can limit the fetched data: if you query Schools with their Students, you know that every Student of School 10 will have a foreign key SchoolId equal to 10. So why fetch this foreign key for every of the Schools 1000 Students?
I'm not familiar with the possibilities of ef-core. Does it know that if you Select one of the virtual properties that a (group-)join is needed? In that case it is easier to use Select.
If you'll have to do your joins yourself:
var requestedUserWithAddresAndPlz = dbContext.Users.
// keep only the user with id
.Where(user => user.IdUser == id)
// every User has exactly one Address: use a normal join
.Join(dbContext.Addresses,
user => user.IdUser, // from every User take IdUser
address => addres.IdAddress, // from every Address take IdAddress
// result selector: take the user with its address to make one new
(user, address) => new
{
// Select only the User properties you plan to use
Id = user.IdUser,
Name = user.Name,
...
Address = new
{
// again: select only the properties you plan to use
// no need to select IdAddress, you know the value!
Street = address.Street,
HouseNumber = address.HouseNumber,
// to fetch the Plz: fetch the one-and-only PlzOrt with address.FidPlzOrt
Plz = dbContext.PlzOrts
.Where(plzOrt => plzOrt.PlzOrdIt == address.FidPlzOrt)
.FirstOrDefault(),
})
.FirstOrDefault();
Note: I used anonymous types to have greater freedom in selecting only the properties I actually plan to use. I can also give my properties the names that I want.
Disadvantage: you can't use anonymous types as return values. If you really need this outside your function, use create a class that contains your data, and use new SomeClass(). Advantage: if your database changes, SomeClass doesn't have to change, and thus your callers won't notice the change in your database.
I have a Category class. It is also a self-join. I have used repository pattern. Using the repository works fine for get, insert or of update data. But when I want to get data from a relational entity, it does not work. It throws this error:
Object reference not set to an instance of an object
But, when I get data using DbContext it works fine.
public class Category
{
public int Id { get; set; }
[Required]
public string Name { get; set; }
public int? CategoryId { get; set; }
//Navigation
public Category Parent { get; set; }
public virtual ICollection<Category> Children { get; set; }
}
Configuration for self join
builder.HasMany(x => x.Children)
.WithOne(x => x.Parent)
.HasForeignKey(g => g.CategoryId);
Repository class for getting all data
public IEnumerable<TEntity> GetAll()
{
return Context.Set<TEntity>().AsEnumerable();
}
[HttpGet]
public JsonResult LoadCategory()
{
var categories = unitOfWork.Category
.GetAll()
.ToList()
.Select(x => new
{
Id = x.Id,
Name = x.Name,
CategoryName = x.Parent.Name
}).ToList();
return Json(categories);
}
In that last method, I get the error, but when I use
ProductDbContext db = new ProductDbContext();
then it works.
LoadCategory not working right?
Suppose that you create new instance of db as general, on top;
and use like this if it is work? Try this;
[HttpGet]
public JsonResult LoadCategory()
{
var categories = unitOfWork.Category
.GetAll()
.Select(x => new
{
Id = x.Id,
Name = x.Name,
CategoryName = x.Parent.Name
}).ToList();
return Json(categories);
}
I find the problem. CategoryId is null-able. So if any item CategoryId is null then it throw error. I just check if null then neme will be blank.
var categories = unitOfWork.Category
.GetAll()
.Select(x => new
{
Id = x.Id,
Name = x.Name,
CategoryName = x.CategoryId == null ? "" : x.Parent.Name
}).ToList();
I am having two different list coming from same object. I want to get those two at same time as separate list or join those two same list when returning JSON object.
Here is my code.
List<User> userEntity = users.Where(s => s.Id == Id).ToList();
var GetUserNames = userEntity.SelectMany(s => s.Names.Select(u =>
new
{
Name = u.Name,
Id = u.Id
})).ToList();
var GetProfile = userEntity.SelectMany(s => s.Profile.Select(u =>
new
{
Name = u.Name,
Id = u.Id
})).ToList();
return Json(GetUserNames, JsonRequestBehavior.AllowGet);
I would prefer to do this differently: the returned items are different types. Rather than returning a bare JSON list with a type discriminator, return a multiproperty JSON object:
return Json(new
{
Names = GetUserNames,
Profiles = GetProfile
}, JsonRequestBehavior.AllowGet);
Your returned JSON will have the {Name, Id} objects separated into their types, in sketch form:
{
Names: [
{Name:"UserName", Id:"3"},
{Name:"OtherUser", Id: "4"}
],
Profiles: [
{Name:"Normal", Id:"1"},
{Name:"Admin", Id: "99"}
]
}
In most JSON.Net client side parsing scenarios (i.e. consuming a WebAPI from a WPF smart client), you automatic mapping (such as from RestSharp) would allow you to deserialize this into a class of the form
public class NameId
{
public int Id {get; set;}
public string Name {get; set;}
}
public class UserNamesResponse
{
public List<NameId> Names {get; set;}
public List<NameId> Profiles {get; set;}
}
This may be more convenient and clearer to work with than an interleaved list which must be filtered into separate lists for binding anyway.... (or fed through filtering type converters at a performance penalty in the UI binding layer...)
You could use GetUserNames.Concat(GetProfile).
List<User> userEntity = users.Where(s => s.Id == Id).ToList();
var GetUserNames = userEntity.SelectMany(s => s.Names.Select(u =>
new
{
Name = u.Name,
Id = u.Id
})).ToList();
var GetProfile = userEntity.SelectMany(s => s.Profile.Select(u =>
new
{
Name = u.Name,
Id = u.Id
})).ToList();
return Json(GetUserNames.Concat(GetProfile) , JsonRequestBehavior.AllowGet);
Enumerable.Concat Method
Update as per comments
Create a class and enum:
public class NameId
{
public string Name {get;set;}
public string Id {get;set;}
public ThisType Type { get; set; }
}
public enum ThisType
{
Username,
Profile
}
Then return that instead:
List<User> userEntity = users.Where(s => s.Id == Id).ToList();
var GetUserNames = userEntity.SelectMany(s => s.Names.Select(u =>
new NameId
{
Name = u.Name,
Id = u.Id,
Type = ThisType.Username
})).ToList();
var GetProfile = userEntity.SelectMany(s => s.Profile.Select(u =>
new NameId
{
Name = u.Name,
Id = u.Id,
Type = ThisType.Profile
})).ToList();
return Json(GetUserNames.Concat(GetProfile) , JsonRequestBehavior.AllowGet);