Using a custom method in LINQ to Entities - asp.net

I have a LINQ expression:
var users = db.Relationships.Where(i => i.RelationshipId== relId)
.Select(s => s.User).Distinct().Select(s => new UserViewModel() {
Username = s.Username,
LastActiveDateTime = s.LastActive, // DateTime, I want it to be a string filtered through my custom GetFriendlyDate method
}).ToList();
// convert all DateTimes - yuck!
foreach (var userViewModel in users) {
userViewModel.LastActive = DateFriendly.GetFriendlyDate(userViewModel.LastActiveDateTime);
}
This solution is working, but it feels wrong to
have to iterate over all users after getting them from the db, just to reformat a property on every single one
have a DateTime property on my ViewModel just so that it can later be converted to a string and never touched again
Is there any way I can use the GetFriendlyDate method directly within the query?

Possible solutions, worth to mention:
Have a getter property of your ViewModel, which would return transformed string, something like:
public string LastActive
{
get
{
return DateFriendly.GetFriendlyDate(LastActiveDateTime);
}
}
Though it not solves your problem with existing LastActiveDateTime column, transformation will be applied only at moment of usage (in your view, most likely - anyways if you will try use it somewhere latter in query, it will not work for reason you already know), so no need to iterate manually.
Create View, which will transform data on server side; so your data will already be returned in format you need, if you're using DBFirst, probably, it's easiest and fastest solution;
Finally, you can use ToList() twice, once before selecting new ViewModel() (or call AsEnumerable(), or find other way to materialize query). It will fetch data from database and will allow you perform any C#-side functions you want directly in query after ToList(). But, as mentioned before - it's about getting all data, which matched criteria up to ToList() - in most cases it's not appropriate solution.
And here is some additional readings:
How can I call local method in Linq to Entities query?

I tested it in LINQpad and it works.
It still kinda iterates over users (with LINQ) but you don't have to add DateTime property to your viewmodel class. Also you could convert collection of Users to collection of UserViewModel objects with Automapper. It would still iterate over users of course but you wouldn't see it.
Had to create some setup code of course because I don't have your database.
void Main()
{
var db = new List<User> {
new User { LastActive = DateTime.Now, Username = "Adam", Lastname = "Nowak" },
new User { LastActive = DateTime.Now.AddYears(1), Username = "Eve", Lastname = "Kowalska"}
};
// select only properties that you need from database
var users = db
.Select(user => new { Username = user.Username, LastActive = user.LastActive})
.Distinct()
.ToList();
var usersVM = from u in users
select new UserViewModel { Username = u.Username, LastActiveDateTime = DateFriendly.GetFriendlyDate(u.LastActive)};
usersVM.Dump();
}
class User
{
public DateTime LastActive;
public string Username;
public string Lastname;
};
class UserViewModel
{
public string Username;
public string LastActiveDateTime;
}
static class DateFriendly
{
public static string GetFriendlyDate(DateTime date)
{
return "friendly date " + date.Year;
}
}
And this outputs
Username LastActiveDateTime
Adam friendly date 2013
Eve friendly date 2014

There is no direct Concert.ToDate method available for LINQ. But you can try using the DateAdd method from the SqlFunctions class:
var users = db.Relationships.Where(i => i.RelationshipId== relId)
.Select(s => new
{
s.User.Username,
LastActive=SqlFunctions.DateAdd("d",0, s.LastActive)
})
.ToList().Select(s => new UserViewModel()
{
Username = s.Username,
LastActiveDateTime = s.LastActive
});

Wouldn't the following work?
var users = db.Relationships.Where(i => i.RelationshipId== relId)
.Select(s => s.User).Distinct().Select(s => new UserViewModel() {
Username = s.Username,
LastActiveDateTime = DateFriendly.GetFriendlyDate(s.LastActive)
}).ToList();

Related

Create a entity framework LINQ group join query to return a subset of properties from DTO and a list of another DTO, ASP.net core

So, I tried searching and couldn't really find an answer that was explicit enough and guided me to my solution so I thought I would add my problem and ultimately my solution for others to benefit.
Pardon my newness to SO (consider this my start of getting my reputation up), let me know if I do anything incorrect or forget anything.
I am trying to:
Create a controller that queries a database for all the users and their roles.
Return the list of unique users id's and email address with a List of roles.
The email address and roles are in separate tables and the pkey/fkey is the user id
The roles are returned as a list containing my AllUserInRolesDto
Every example I looked at on SO or other sites only provided examples of returning anonymous data objects back. I don't have a lot of LINQ query syntax so had me stuck for an hour or so.
Here is my DTO
namespace Lib.Dtos
{
public class AllUserInRolesDto
{
public string FullName { get; set; }
public string Email { get; set; }
public List<RoleDto> Roles { get; set; }
}
public class RoleDto
{
public string RoleName { get; set; }
}
}
I have a business layer that defines the LINQ query
public List<AllUserInRolesDto> UserAllRolesGet()
{
List<AllUserInRolesDto> getAllUsersRoles = (from u in _context.Users
join r in _context.UserInRoles on u.UserId equals r.UserId
into ur
select new Lib.Dtos.AllUserInRolesDto()
{
FullName = u.Fullname,
Email = u.Email,
Roles = ur //this was the problem line and what the docs were describing
}).ToList();
return getAllUsersRoles;
}
...and my controller
[HttpGet("GetAllUserRolesList")]
public IActionResult GetAllUserRolesList()
{
List<Lib.Dtos.AllUserInRolesDto> allUsers = _userBL.UserAllRolesGet();
return new JsonResult(allUsers);
}
my solution
After taking a step back for a second I realized I actually wasn't returning the right object back to my roles property...and so need to iterate over my roles and create a list from them. Here is what worked.
public List<AllUserInRolesDto> UserAllRolesGet()
{
List<AllUserInRolesDto> getAllUsersRoles = (from u in _Context.Users
join r in _context.UserInRoles on u.UserId equals r.UserId
into ur
select new Lib.Dtos.AllUserInRolesDto()
{
FullName = u.Fullname,
Email = u.Email,
Roles = .Select(x => new Lib.Dtos.RoleDto() { RoleName = x.RoleName }).ToList() //Changed to this
}).ToList();
return getAllUsersRoles;
}
Anyway, probably a pretty dumb mistake, but had me stuck for a bit. Maybe this helps someone in my same position or if someone has a comment of how I could have improved this or used a different approach I am open to hearing suggestions.
I assume you're using Entity Framework, and that you have your DB model defined with relationships. This means you don't need to use explicit JOINs in your queries: you can use navigation properties instead.
Your "business layer" (note that you don't necessarily always need a business layer) should only work with Entity types and should not use DTOs (as DTOs belong to your web-service, in the same way that View-Models belong to a web-application).
If your "business layer" just consists of predefined queries, I recommend defining them as static extension methods for your DbContext and returning IQueryable<T> instead of as materialized List<T> as this enables your consumers to perform further operations on them (such as additional filtering or sorting and paging).
I recommend doing it like this instead:
// Queries methods (i.e. "business layer" queries)
public static class QueriesExtensions
{
public static IQueryable<User> GetAllUsersAndTheirRoles( this MyDbContext db )
{
// I assume `UserInRoles` is a linking table for a many-to-many relationship:
return db
.UserInRoles
.Include( uir => uir.User )
.Include( uir => uir.Role )
.Select( uir => uir.User );
}
}
// Web-service controller:
[HttpGet("GetAllUserRolesList")]
[Produces(typeof(List<AllUserInRolesDto>)]
public async Task<IActionResult> GetAllUserRolesList()
{
List<User> usersList = await this.db
.GetAllUsersAndTheirRoles()
.ToListAsync();
List<Lib.Dtos.AllUserInRolesDto> usersListDto = usersList
.Select( u => ToDto( u ) )
.ToList();
return new JsonResult( usersListDto );
}
// Entity-to-DTO mapping functions (if you have a lot of DTOs and entities, consider using AutoMapper to reduce the tedium)
private static AllUserInRolesDto ToDto( User user )
{
return new AllUserInRolesDto()
{
FullName = user.FullName,
Email = user.Email,
Roles = user.Roles.Select( r => ToDto( r ) ).ToList()
};
}
private static RoleDto ToDto( Role role )
{
return new RoleDto()
{
RoleName = role.RoleName
};
}

How to get role name for user in Asp.Net Identity

I am trying to figure out how to find user role name in identity framework.
I have such configuration that there will be only one role assigned to a user.
So, I tried using
public string GetUserRole(string EmailID, string Password)
{
var user = await _userManager.FindAsync(EmailID, Password);
var roleid= user.Roles.FirstOrDefault().RoleId;
}
But what I get is just RoleId and not RoleName.
Can anyone help me to find the RoleName for the user?
In your code, user object represents the AspNetUsers table which has a navigation property Roles which represents the AspNetUserInRoles table and not the AspNetRoles table.
So when you try to navigate through
user.Roles.FirstOrDefault()
it gives you the AspNetUserInRoles which stores UserId and RoleId.
Instead you could try UserManger's GetRoles method which will return you List<string> of roles user is assigned. But as you mentioned it will be only one role hence you can take first value from the result of GetRoles method.
Your function should be similar to one given below:
public async string GetUserRole(string EmailID, string Password)
{
var user = await _userManager.FindAsync(EmailID, Password);
string rolename = await _userManager.GetRoles(user.Id).FirstOrDefault();
return rolename;
}
If you has a list of users you has to do this:
var usuarioManager = Request.GetOwinContext().GetUserManager<UserManager<Usuario>>();
var roleManager = Request.GetOwinContext().Get<RoleManager<IdentityRole>>();
var roles = roleManager.Roles.ToList();
var usuarios = usuarioManager.Users.Include(x => x.Roles).ToList();
usuarios.ForEach((x) =>
{
if (x.Roles.Any())
{
var roleDb = roles.FirstOrDefault(r => r.Id == x.Roles.FirstOrDefault().RoleId);
if (roleDb != null)
x.RoleName = roleDb.Name;
}
});

How can I get Object from Table in Entity Framework

I have a database table and corresponding class in entity framework. Please tell me how to get data from database and directly set into class object. Here is my code.
It's giving an error like cannot implicitly convert type Jobportel.Member to Jobportel.DTO.MemberDTO.
public MemberDTO getMember(String email) {
MemberDTO member= null;
using (var db = new jobportaldatabaseEntities()) {
member= db.Members.SingleOrDefault(e => e.email == email);
}
return member;
}

Unit Test .Net on Simple EF Function

I have 3 layers in Asp.Net Web API. The controller layer, service layer and repository layer implemented using EF.
I am new to unit testing and have a simple function that gets a person by their id in the database and nothing else.
Basically the service layer calls
Unit_Work.Person_Repository.GetPersonByID(id);
and the Repository does this:
return context.chapters.Where(p=>p.chapterID==id).SingleOrDefault();
What kind of Unit Test would i write on this.
should i use the database or a mock implementation.
I thought of using Sql Server Compact populating it with a mock person and then trying to get that person by ID is this correct.?
Thanks in advance to all those that answer.
If you are using entity framework you can't unit test your data access layer.
Solution provided by Erik Alsmyr is very wrong!
Look here why - What's the point of in memory IDbSet?.
When you use in memory db sets you are running Linq to Objects. When you use EF's DbContext your Linq is converted to SQL. Those are two different things!
It is very easy to write code that will work with in memory db set (all your unit tests will pass and you will be happy) just to notice runtime error first time you try to hit database.
Let's modify this code a bit. I don't think FULLNAME should have setter if we are using FIRSTNAME, LASTNAME. It should be calculated from FIRSTNAME and LASTNAME.
class User
{
public string FIRSTNAME { get; set; }
public string LASTNAME { get; set; }
public string FULLNAME
{
get { return string.Format("{0}, {1}", LASTNAME, FIRSTNAME }
}
User(string firstName, string lastName)
{
this.FIRSTNAME = firstName;
this.LASTNAME = lastName;
}
}
Now you can write test like this and it will pass (of course after you implement it in controller)
public IMyEntities GetRepoWithUsers(params User[] users)
{
var inMemoryUsers = new InMemoryDbSet<User>();
var mockData = new Mock<IMyEntities>();
mockData.Setup(m => m.Users).Returns(inMemoryUsers);
return mockData.Object;
}
[Test]
public void GetUserByFullname()
{
var ankaArne = new User("Arne", "Anka");
var bjornBertil = new User("Bertil", "Björn");
var repo = GetRepoWithUsers(ankaArne, bjornBertil);
var usersController = new UsersController(repo);
var found = usersController.GetUser("Anka, Arne");
Assert.NotNull(found);
Assert.AreEqual("Anka", found.LASTNAME);
Assert.AreEqual("Arne", found.FIRSTNAME);
}
But when you run it against 'real' DbContext and 'real' DbSet it will throw because you can't do Linq queries on calculated properties. Only on those that are mapped to database columns. So what's the point of that test?
You can use the xml/ .csv file for data. ie, you need to fetch the ID, chapter details from the xml file inside the unit test project. Then you have to pass the id as the parameter then check the return values with the data fetch from xml file. if you dont understand let me know. You cn create unit test project by add-new project options. then on vs2010 ther r options to add xml file for fetching the data to be tested.
your 3rd question is also correct. u cn populate the data from database and check the data with the return value
I recommend mocking and injecting an Entity framework context into your repository.
We do this using something similar to http://nuget.org/packages/FakeDbSet/
Then our unit tests look like this:
[TestFixture]
class UsersControllerTester
{
private Mock<IMyEntities> mockData = null;
[SetUp]
public void Setup()
{
// Create fake data
var inMemoryUsers = new InMemoryDbSet<User>();
inMemoryUsers.Add(new User { ID = 1, FIRSTNAME = "Arne", LASTNAME = "Anka", EMAIL = "arne.anka#email.com", FULLNAME = "Anka, Arne", USERNAME = "arne.anka" });
inMemoryUsers.Add(new User { ID = 2, FIRSTNAME = "Bertil", LASTNAME = "Björn", EMAIL = "bertil.bjorn#email.com", FULLNAME = "Björn, Bertil", USERNAME = "bertil.bjorn" });
inMemoryUsers.Add(new User { ID = 3, FIRSTNAME = "Carl", LASTNAME = "Cool", EMAIL = "carl.cool#email.com", FULLNAME = "Cool, Carl", USERNAME = "carl.cool" });
inMemoryUsers.Add(new User { ID = 4, FIRSTNAME = "David", LASTNAME = "Dûsk", EMAIL = "david.dusk#email.com", FULLNAME = "Dûsk, David", USERNAME = "david.dusk" });
// Create mock unit of work
mockData = new Mock<IMyEntities>();
mockData.Setup(m => m.Users).Returns(inMemoryUsers);
}
[Test]
public void GetUser()
{
// Test
var usersController = new UsersController(mockData.Object);
// Invoke
User user1 = usersController.GetUser("1");
// Assert
Assert.NotNull(user1);
Assert.AreEqual(1, user1.ID);
Assert.AreEqual("Anka", user1.LASTNAME);
}

JsonResult in services layer

In my MVC3 solution I'm wondering how to move the logic that returns Json out of the controller and into the service layer. Say I have the following action in my controller to get the Json needed for a JQueryUI autocomplete control:
public JsonResult ClientAutocompleteJSON(string term)
{
NorthwindEntities db = new NorthwindEntities();
var customers = db.Customers
.Where(c => c.ContactName.Contains(term))
.Take(25)
.Select(c => new
{
id = c.CustomerID,
label = c.ContactName,
value = c.ContactName
});
return Json(customers, JsonRequestBehavior.AllowGet);
}
How would I move this into the service layer? I would prefer not to reference System.Web.MVC in my service layer. I've also thought of returning the customers but I'm not sure how to return the anonymous type - would I have to create a class?
I would not couple your service implementation to a specific (UI) format. It would be better to return a strongly typed customer object and then format this how you want within your Action method.
// Service method
public IEnumerable<Customer> FindCustomers(string term) {
NorthwindEntities db = new NorthwindEntities();
return db.Customers
.Where(c => c.ContactName.Contains(term))
.Take(25)
.ToList();
}
// Action method
public JsonResult ClientAutocompleteJSON(string term) {
var customers = customerService.FindCustomers(term)
.Select(c => new
{
id = c.CustomerID,
label = c.ContactName,
value = c.ContactName
});
return Json(customers, JsonRequestBehavior.AllowGet);
}
This code is much more reusable - for example, you could use the same service method to provide a simple HTML search form.
Create a DTO object: http://martinfowler.com/eaaCatalog/dataTransferObject.html
I know about a feature in Ruby on Rails, there you can define that your method is capable of returning JSON or XML or HTML based on client preference, it will be a good feature if you can find a library that can do this for you. It could be an aspect which by dynamic proxifying your services can do.

Resources