Get all users from WEB API - c#

I want to create a service in a WEB API MVC5 project the returns users created.
This works
[Route("getUsers")]
// GET: User
public List<ApplicationUser> getUsers()
{
List<ApplicationUser> users = DbContext.Users.ToList();
return users;
}
but it returns all the data from the Users, where I'm only interested in returning FullName and Id.
Any suggestions on how to limit the result?

Three suggestions/enhancements:
Web API is meant to be used to create RESTful services. Check that you're calling the whole operation using HTTP/GET and the route itself says getXXX. Change the route to, at least, /users.
Don't return domain objects over the wire. You need to implement data-transfer objects. If you've few use cases to return users or one user, design a class which only have these properties. Also, this is important because objects returned by a DbContext are heavy: they also contain tracking information and other things since they're object proxies. You'll avoid some overhead and errors while serializing and deserializing.
Your code should look as follows: DbContext.Users.Select(u => new UserDto { Id = u.ID, Name = u.Name }).ToList(). In order to avoid manually mapping your objects, I would suggest that you take a look at AutoMapper library.
Design/implement a domain layer which can be injected/instantiated in your WebAPI in order to encapsulate this complexity and let the WebAPI call something like userService.ListUsers().

Change DbContext.Users.ToList() to DbContext.Users.Select(u => new ApplicationUser() {ID = u.ID, Name = u.Name}).ToList();

You don't even need to use List, Do this:
[Route("getUsers")]
// GET: User
public IQueryable<object> getUsers()
{
var users = DbContext.Users.Select( u => new {Id = u.Id, Fullname = u.Fullname});
return users;
}
If you don't have a direct Fullname property, you could get if from other properties like Firstname and Lastname like so:
var users = DbContext.Users.Select( u => new {Id = u.Id, Fullname = u.Firstname + " " + u.Lastname});

You can project your data using the .Select extension method for IEnumerable.
You could do something like this
var users = DbContext.Users(u=> new { Id = u.Id, Fullname = u.Fullname });
return users;

Related

Need help in getting data from multiple table using EF

I am getting details in list format using this query, in that I am also getting user id.
reportProblemsList = objEntities.ReportProblems.ToList();
Now I want to get user details based on that user id from users table. So I have tried with foreach loop as per below.
foreach(var item in reportProblemsList)
{
userdetails = objEntities.Users.Where(x => x.UserId == item.Userid).ToList();
}
Now I want to add this all userdetails in list and then access it. Below is my full code.
List<ReportProblem> reportProblemsList = new List<ReportProblem>();
List<User> userdetails = new List<User>();
reportProblemsList = objEntities.ReportProblems.ToList();
foreach(var item in reportProblemsList)
{
userdetails = objEntities.Users.Where(x => x.UserId == item.Userid).ToList();
}
reportProblemsList.Add(userdetails);
But not able to get it working..any idea.
Thanks
If you database is correctly designed from the given information, there is a high probability that your ReportProblem object has also the property User of type User. If this is the case you can instruct EF core to include such nested properties in your query by doing something like this:
var reportProblemsList = objEntities.ReportProblems
.Include(report => report.User)
.ToList();
If you don't have this property you have to create an anonymous type to hold the tuple:
var reportsWithUsers = objEntities.ReportProblems
.Join(objEntities.Users, r => r.UserId, u => u.UserId, (Report, User) => (Report, User)
.ToList();
I assume that you need a list users who reported the issue.
var userIds = objEntities.ReportProblems.Select(q=> q.Userid).ToList();
List<User> userdetails = objEntities.Users.Where(x => userIds.Contains( x.UserId)).ToList();

LINQ - filtering a result set and joining the result set

I have User Table and each user can have multiple Computers... Lets say I am Ram, I can have only one DomainId, multiple UserId and multiple Computers..
I would like to get the DeviceId of all my computers and those should be only Laptops not Desktops
I am getting all the computers details of a given user
var res = (from b in DB.Users
WHERE b.DomainId == 'MS\\aram'
select new {b.UserId, b.DeviceId}).ToList();
From the above deivces I want to get only those devices which are laptops... The device Id should be present in laptops table
My tblLaptops table is having a column called DeviceId.. I tried in the below query but it is throwing error cannot convert int into anonymous type
var devices = (from a in LBDB.tblLaptops where res.contains(a.DeviceId)
select new {a.UserId, a.DeviceId }).ToList();
once the above is returned I also want to get Display Name of users which is in Extended Users table
Regarding the DisplayName, the column name is DisplayName and the table Name is ExtendedUser .. The ExtenderUserDevice table has DeviceId column which can be used to compare and the DisplayName of the device...
You can try a join:
var result = (from laptop in DB.tblLaptops
join user in DB.Users
on user.DeviceId equals laptop.DeviceId
where user.DomainId =='MS\\aram'
select new { user.UserId, laptop.DeviceId }).ToList();
The above query would return the laptop devices and user ids for a specific domain. Regarding the DisplayName, we need some more info, in order to plug it also this in the above query, in order to fetch also this information.
Update
Since the above it is not going to work, since you access these tables through different contexts, here is my thought.
Provided that laptops is not a rather big table, you could fetch it in memory and make the join in memory. Apparently, this is not an optimal solution, but rather a workaround, that it wouldn't hurt you, if the laptops table is not big.
In terms of code:
// fetch the user devices in the specific domain:
var usersDevices = (from user in DB.Users
where user.DomainId == 'MS\\aram'
select new
{
user.UserId,
user.DeviceId
}).ToList();
// fetch **ALL** the laptops:
var laptops = DB.tblLaptops.ToList();
// perform the join:
var userLaptops = (from laptop in laptops
join userDevice in usersDevices
on userDevice.DeviceId equals laptop.DeviceId
select new
{
user.UserId,
laptop.DeviceId
}).ToList();
The correct approach it would be to think about, why these related info are behind different DbContext classes. That essentially means that these data are in different databases. If so, are these databases in the same machine ? If so and you don't plan these databases to be in different machines in the short future, you could quite probably makes all these queries in the database and fetch from the server that your application leaves only the needed data and not all the data and then filter/join them. IMHO just my 2 cents, based on many assumptions :)
given you tag your question with EF, you could potentially opt for .Include() on navigation properties but if we assume you just want the LINQ, here's a method chain version for your consideration:
var result = users
.Join(extendedUsers, u => u.UserId, eu => eu.UserId, (user, extUser) => new { user, extUser }) // you mentioned you want to join to ExtendedUser, so we might as well do it first. doesn't really make a difference
.Join(tblLaptops, u => u.user.DeviceId, l => l.DeviceId, (user, laptop) => new {user, laptop}) // then we get both results from previous join and add your laptop table onto it
.Where(u => u.user.user.DomainId == "MS\\aram") // filter it all down
.Select(x => new {x.laptop.UserId, x.laptop.DeviceId, x.user.extUser.DisplayName}); // get the desired output
I didn't have your databases to verify the code so I've created something quick:
enum SystemType
{
Desktop = 0,
Laptop,
}
struct User
{
public int Id;
public string Name;
}
struct SystemStruct
{
public int Id;
public SystemType Type;
}
private List<User> users = new List<User>()
{
new User() { Id = 0, Name = "John" },
new User() { Id = 1, Name = "Alice" },
new User() { Id = 2, Name = "Bob" }
};
private List<SystemStruct> systems = new List<SystemStruct>()
{
new SystemStruct() { Id = 0, Type = SystemType.Desktop },
new SystemStruct() { Id = 1, Type = SystemType.Laptop },
new SystemStruct() { Id = 2, Type = SystemType.Desktop }
};
It basically creates an enum and structs to map your database results.
And to join your users only where the system type is a laptop you use:
var laptopUsers = users.Join(systems.Where(s => s.Type == SystemType.Laptop), u => u.Id, s => s.Id, (user, system) => user);

I am trying to save information to two data tables using one service method using c#

Writing an app and I am trying to add on to the method where a user creates a new gift card. I added another column in my applicationuser table called TotalBalance that adds in the balances of each cards created. So I can get a new card to save to the gift card table but I want the gift card balance to be added to the 'Users total donation balance' in the application user table. The code I'm working with is below.
public bool CreateGiftCard(GiftCardCreateViewModel model)
{
using (var context = new ApplicationDbContext())
{
var company = context
.Company
.Where(e => e.CompanyName == model.CompanyName)
.FirstOrDefault();
var companyId = company.CompanyId;
var entity =
new GiftCard()
{
OwnerId = _userId,
Amount = model.Amount,
CardNumber = model.CardNumber,
DonationUtc = DateTime.Now,
CompanyId = companyId,
// Optional
ExpirationDate = model.ExpirationDate,
AccessNumber = model.AccessNumber
};
company.CompanyAmount = company.CompanyAmount + entity.Amount;
context.GiftCard.Add(entity);
return context.SaveChanges() == 1;
}
}
You might be doing this in entirely the wrong place. Rather than handling it in the application at the time of update, you could look at creating this as a calculated column in the database itself.
The details will depend upon which RDBMS you're using, you might like to add a tag to indicate this.
To more directly answer your question, it is not possible to update two tables in a single select statement.
Try adding this code to your method (I guessed at some of the attribute names, but you can get the idea):
var appuser = context
.ApplicationUser
.Where(e => e.UserId == _userId)
.FirstOrDefault();
appuser.TotalBalance += model.Amount;
context.ApplicationUser.Update(appuser);

Is it possible to remove a property or at-least set it null from object in a query without changing its type?

So i have this LinQ query
var users = from user in dbContext.Users
where user.IsPublic
select user;
The issue is that this user have other properties i don't want returned such as passwordHash or email.
Also selecting into new User{Id=user.Id, First...} results into an this classic error.. probably because User extends AspNetIdentityUser
The entity or complex type 'MyProject.User' cannot be constructed in a
LINQ to Entities query.
I know i could create a new class and select into it but i want the returned to be of type User
Is it possible to remove a property/field from User at-least set it null when making a query without changing its type?
You can select all the needed properties to an anonymous type, materialize the result and then use the it to build a list of Users.
First query uses SQL like syntax, second - chain syntax.
private static void Main(string[] args)
{
List<User> Users = new List<User>()
{
new User {Id = 1, Name = "N1", IsPublic = true},
new User {Id = 2, Name = "N2", IsPublic = true},
new User {Id = 3, Name = "N3"}
};
var users1 = from u in (from user in Users
where user.IsPublic
select new {user.Id, user.Name}).ToList()
select new User {Id = u.Id, Name = u.Name};
var users2 =
Users.Where(x => x.IsPublic)
.Select(user => new {user.Id, user.Name})
.ToList()
.Select(x => new User {Id = x.Id, Name = x.Name});
Console.ReadLine();
}
Apparently you are not allowed to new up entities that are mapped to database in Linq To Entities statement.
This prevents you from selecting partial results into mapped classes. I would advise you to create separate class and return that instead as by using the same class this function signature becomes very misleading - one might expect that it returns full user object.
You can work around this issue by selecting values you need and then creating users outside the query.
Try this:
public List<User> GetPublicUsers()
{
var existingUsers = dbContext.Users
.Where(u => u.IsPublic);
var result = new List<User>();
foreach(var existingUser in existingUsers)
{
result.Add(new User { Id = existingUser.Id, ...}));
}
return result;
}
Well This draws too much from the two answers.. see the return type had to be IQueryable it is not yet complete as i wished to hide some columns completely, i have just manage to modify their values.. i really hate breezejs for making us expose too much of our schema
public IQueryable<User> Users()
{
var users = from user in _db.Context.Users
where user.IsPublic
select user;
foreach (var user in users)
{
user.PasswordHash = null;
user.PhoneNumber = null;
user.Logins.Clear();
user.Email = null;
user.AccessFailedCount = 0;
user.Roles.Clear();
user.SecurityStamp = null;
user.TwoFactorEnabled = user.EmailConfirmed = user.PhoneNumberConfirmed = false;
user.Claims.Clear();
}
return users;
}
I really wish these columns where invisible to breeze as it just need the Id and Name... but.. i guess this will do.. for now

Is it possible to extend an Entity model with common queries?

Say I have a set of Sites that have a collection of Users. I find myself violating the DRY principal whenever I have to redefine a query to get, say, the last visited User for a given site.
For example, my query may look like this:
from site in Context.Sites
where site.ID == 99
select new {
ID = site.ID,
Name = site.Name,
URL = site.URL,
LastVisitedUser = site.Users.OrderByDescending(u => u.LastVisited).Select(u => new {
ID = u.ID,
Username = u.Username,
Email = u.EmailAddress
})
.FirstOrDefault()
}
That query returns what I want, but I find myself repeating this same select for the LastVisitedUser in multiple places as I'm selecting the site in different ways to populate my various ViewModels.
So, I thought that I would simply extend my Site Entitiy class with a property like so:
public partial class Site {
public LastVisitedUser {
get {
var query = from user in Users
where user.SiteID == this.ID
orderby user.LastVisited descending
select user;
return query.FirstOrDefault()
}
}
}
In this manner, whenever I am selecting a site it would be fairly trivial to grab this property. This almost works, however I am stuck trying to assign an Entity user into my UserViewModel property into the LastVisited property of my return, without an obvious way on how to project the User into my ViewModel version.
Perhaps an example would help explain. What I'm trying to accomplish would be something like this:
from site in Context.Sites
where site.ID == 99
select new SiteViewModel {
ID = site.ID,
Name = site.Name,
URL = site.URL,
LastVisitedUser = site.LastVisitedUser <--- NOTE
}
NOTE = This is my point of failure. LastVisitedUser is a ViewModel with a subset of User data.
Am I going about this in the correct manner? Can I achieve what I'm trying to do, or am I horribly misguided? Am I about to sove this issue and run into others?
Thanks!
Edit: The former answer was not correct. You cannot use extension method on the navigation property but you can still create extension method for the whole Site projection.
Just create simple reusable extension method:
public static IQueryalbe<SiteModel> GetSiteModels(this IQueryable<Site> query)
{
return query.Select(site => new SiteModel {
ID = site.ID,
Name = site.Name,
URL = site.URL,
LastVisitedUser = site.Users
.OrderByDescending(u => u.LastVisited)
.Select(u => new LastVisitedUser {
ID = u.ID,
Username = u.Username,
Email = u.EmailAddress
}});
}
Now you should be able to use that extension in your former query:
Context.Sites.Where(site => site.ID == 99).GetSiteModels();
Your example will not work because your property is not visible for Linq-to-entities.
If you mean that you what to reuse common queries with different extensions, you just nead to write some base query and get different results and some Where lambda expressions. If you profile it, you will see that you have just one query to DB just return IQuerable in a base query

Categories

Resources