I am currently trying to get the given user's list of Roles and am having some trouble fitting this into the context we are using it in. I was able to get a list of all available roles with this API function earlier,
[HttpGet]
[Route("GetRoles")]
public async Task<ApiResponse<List<RoleViewModel>>> GetRoles()
{
try
{
//Get Roles
var roles = await (from r in _db.AspNetRoles
select new RoleViewModel { Id = r.Id, Name = r.Name}).ToListAsync();
return new ApiResponse<List<RoleViewModel>>{ Success = true, Result = roles };
}
catch(Exception ex)
{
return new ApiResponse<List<RoleViewModel>> { Success = false, Message = ex.Message };
}
}
But can't seem to figure out what I need to throw into this one to get a list of the roles for the user. We went with Entity Frameworks Code First from Existing Database approach and are pulling from those tables. Strangely though there is no AspNetUserRoles table since I guess it is just relating the two tables AspNetUsers and AspNetRoles. Anyway, here is the function in question,
[HttpGet]
[Route("GetUserRoles")]
public async Task<ApiResponse<List<RoleViewModel>>> GetUserRoles(string userName)
{
try
{
var userRoles = await (_db.AspNetUsers.FirstOrDefault(u => u.UserName == userName).AspNetRoles).ToListAsync();
}
catch (Exception ex)
{
return new ApiResponse<List<RoleViewModel>> { Success = false, Message = ex.Message };
}
}
The current error I am getting is that AspNetRole does not contain a definition for ToListAsync(). I think the async stuff is throwing me a little. And lastly here is the RoleViewModel for reference,
public class RoleViewModel
{
public string Id { get; set; }
[Required]
[StringLength(256)]
public string Name { get; set; }
}
And the ApiResponse class,
public class ApiResponse<TResult>
{
public bool Success { get; set; }
public string Message { get; set; }
public TResult Result { get; set; }
}
I feel like there should be a simple fix, but I just can't quite grasp what it is.
Just found the answer to my problem. The main thing I was missing was utilization of the User Manager which made things so much easier. Then I just had to fit things into the functions I had already defined. Here is the code.
[HttpGet]
[Route("GetUserRoles")]
public async Task<ApiResponse<List<RoleViewModel>>> GetUserRoles(string userName)
{
try
{
// Get the user in question
var aspUser = (from u in _db.AspNetUsers
where u.UserName == userName
select u).FirstOrDefaultAsync();
// Check if the user was found
if (aspUser == null)
{
throw new Exception("User was not found");
}
// Get the roles associated with that user
var userRoles = await UserManager.GetRolesAsync(aspUser.Result.Id.ToString());
// Setup a RoleViewModel list of roles and iterate through userRoles adding them to the list
List<RoleViewModel> roleList = new List<RoleViewModel>();
foreach (var u in userRoles)
{
var item = new RoleViewModel { Name = u };
roleList.Add(item);
}
return new ApiResponse<List<RoleViewModel>> { Success = true, Result = roleList };
}
catch (Exception ex)
{
return new ApiResponse<List<RoleViewModel>> { Success = false, Message = ex.Message };
}
}
Related
How to load a related entity after calling AddAsync?
I have a repository method that looks like this
public virtual async Task<TEntity> AddAsync(TEntity entity)
{
if (entity == null)
throw new ArgumentNullException(nameof(entity));
try
{
entity.CreatedOn = entity.UpdatedOn = DateTime.Now;
var newEntity = await Entities.AddAsync(entity);
var newEntityToRet = newEntity.Entity;
_context.SaveChanges();
return newEntityToRet;
}
catch (DbUpdateException exception)
{
//ensure that the detailed error text is saved in the Log
throw new Exception(GetFullErrorTextAndRollbackEntityChanges(exception), exception);
}
}
Trying to insert an Order for example that looks like this, and only passing the StatusId and the TradingActionId makes the Add safe
public class Order
{
public int Id { get; set; }
public bool IsDeleted { get; set; }
public string CreatedBy { get; set; }
public string UpdatedBy { get; set; }
public DateTime? CreatedOn { get; set; }
public DateTime? UpdatedOn { get; set; }
public string Symbol { get; set; }
public int Quantity { get; set; }
public decimal Price { get; set; }
public int StatusId { get; set; }
public OrderStatus Status { get; set; }
public int TradingActionId { get; set; }
public TradingAction TradingAction { get; set; }
public string Notes { get; set; }
}
var order = new Order
{
TradingActionId = 1,
StatusId = 1,
Notes = source.Notes,
Price = source.Price,
Symbol = source.Symbol,
Quantity = source.Quantity,
CreatedOn = dateTimeNow,
UpdatedOn = dateTimeNow,
UpdatedBy = "test",
CreatedBy = "test"
};
The problem with this is that if I need to return the new entity with certain navigation properties. My following approach doesn't work but shows the idea of what I need to save the instance and at the same time return the child properties.
public virtual async Task<TEntity> AddAsync(TEntity entity, string[] include = null)
{
if (entity == null)
throw new ArgumentNullException(nameof(entity));
try
{
entity.CreatedOn = entity.UpdatedOn = DateTime.Now;
var newEntity = await Entities.AddAsync(entity);
var newEntityToRet = newEntity.Entity;
_context.SaveChanges();
if(include != null)
{
foreach (var navProp in include)
{
try
{
var memberEntry = _context.Entry(newEntityToRet).Member(navProp);
}
catch (Exception e)
{
Console.WriteLine(e);
throw;
}
if (memberEntry is DbCollectionEntry collectionMember)
collectionMember.Load();
if (memberEntry is DbReferenceEntry referenceMember)
referenceMember.Load();
}
}
return newEntityToRet;
}
catch (DbUpdateException exception)
{
//ensure that the detailed error text is saved in the Log
throw new Exception(GetFullErrorTextAndRollbackEntityChanges(exception), exception);
}
}
The version I am using of EF Core is Microsoft.EntityFrameworkCore.SqlServer 3.1.4
Any suggestions of how to create the generic repo method and return the data needed without making another roundtrip to the database?
After inserting a new entity you can always load its related entities explicitly with the Load() or LoadAsync() method. But even though it doesn't execute a typical LINQ method/query (like the one you would write to fetch some related data), EF needs to submit new query to the database for each explicit loading. Therefore, it really doesn't save you any database trip.
Since you are already trying to use the Load() method in your code, I'm assuming your intention is just to avoid writing the new LINQ queries (required to fetch the related data), and not to avoid a database trip. If that is the case, you can try something like bellow -
public async Task<TEntity> AddAsync(TEntity entity, params string[] includes)
{
entity.CreatedOn = entity.UpdatedOn = DateTime.Now;
var newEntry = Entities.Add(entity);
await _context.SaveChangesAsync(); // trip to database
foreach (var navProp in includes)
{
if (newEntry.Navigation(navProp).Metadata.IsCollection())
{
await newEntry.Collection(navProp).LoadAsync(); // trip to database
}
else
{
await newEntry.Reference(navProp).LoadAsync(); // trip to database
}
}
return newEntry.Entity;
}
which you can use like -
var addedOrder = await orderRepository.AddAsync(order, "Status", "TradingAction");
Notice -
I'm using params to pass one or more parameters
To add/insert a new entity use the Add or Attach method. Unless you are dealing with value generators like SequenceHiLo, you really don't need to use the AddAsync method. For details - AddAsync<TEntity>
A type-safe implementation would be -
public async Task<TEntity> AddAsync(TEntity entity, params Expression<Func<TEntity, object>>[] includes)
{
entity.CreatedOn = entity.UpdatedOn = DateTime.Now;
var newEntry = Entities.Add(entity);
await _context.SaveChangesAsync(); // trip to database
foreach (var navProp in includes)
{
string propertyName = navProp.GetPropertyAccess().Name;
if (newEntry.Navigation(propertyName).Metadata.IsCollection())
{
await newEntry.Collection(propertyName).LoadAsync(); // trip to database
}
else
{
await newEntry.Reference(propertyName).LoadAsync(); // trip to database
}
}
return newEntry.Entity;
}
so that you can pass the navigation properties like -
var addedOrder = await orderRepository.AddAsync(order, p => p.Status, p => p.TradingAction);
I'm saving properties in my database with the CreateAMeeting method. When I check the database I can see all properties saved except my list, I can't see my list properties in my database. I have been googling but I can't find if it's possible to see them or if I'm just not saving them properly. If I use a breakpoint I can see that the meeting(db.Meetings.Add(meeting);) model have the times i added in the Times list.
When I later try to retrieve my list in the ChooseTimes method, it's null. I don't know if the problem is that I'm saving it wrong or its something else. What am I doing wrong and is it possible to see saved lists in the database?
I'm not done with the fooreachloop, pay no attention to it.
public class MeetingController : BaseController
{
private ApplicationSignInManager _signInManager;
private ApplicationUserManager _userManager;
public static Meeting NewMeeting;
public static List<DateTime> TempTimes = new List<DateTime>();
public ActionResult CreateMeeting()
{
return View();
}
public ActionResult CreateAMeeting(Meeting meeting)
{
var userName = User.Identity.Name;
var user = db.Users.Where(u => u.UserName == userName).SingleOrDefault();
var model = new MeetingPeopleViewModel();
meeting.Creator = user;
meeting.Invited.Add(user);
meeting.Times = TempTimes;
meeting.Start = DateTime.Now;
meeting.End = meeting.Start.AddMinutes(meeting.Minutes);
db.Meetings.Add(meeting);
db.SaveChanges();
return View("AddToMeeting", model);
}
public ActionResult AddTempTime(DateTime Start, Meeting meeting)
{
TempTimes.Add(Start);
meeting.Times = TempTimes;
return View("CreateMeeting", meeting);
}
public ActionResult ChooseTimes()
{
var userName = User.Identity.Name;
var user = db.Users.Where(u => u.UserName == userName).SingleOrDefault();
Meeting meeting = new Meeting();
foreach(var item in db.Meetings)
{
if(item.Invited.Contains(user))
{
meeting = item;
}
}
return View(meeting);
}
This is my Meeting model.
public class Meeting
{
public int Id { get; set; }
public string Title { get; set; }
public ApplicationUser Creator { get; set; }
public ICollection<ApplicationUser> Invited { get; set; }
public double Minutes { get; set; }
public DateTime Start { get; set; }
public DateTime End { get; set; }
public ICollection<DateTime> Times { get; set; }
public bool AllDay { get; set; }
public ICollection<ApplicationUser> User { get; set; }
public ApplicationUser UserNotis { get; set; }
}
As far as Im aware you can not save a literal list to a database, you can save all the items in a list to a database, by iterating through the list and saving each unique record.
public class MeetingController : BaseController
{
private ApplicationSignInManager _signInManager;
private ApplicationUserManager _userManager;
public static Meeting NewMeeting;
public static List<DateTime> TempTimes = new List<DateTime>();
public ActionResult CreateMeeting()
{
return View();
}
public ActionResult CreateAMeeting(Meeting meeting)
{
var userName = User.Identity.Name;
var user = db.Users.FirstOrDefault(u => u.UserName == userName);
var model = new MeetingPeopleViewModel();
meeting.Creator = user;
meeting.Invited.Add(user);
meeting.Times = TempTimes;
meeting.Start = DateTime.Now;
meeting.End = meeting.Start.AddMinutes(meeting.Minutes);
db.Meetings.Add(meeting);
db.SaveChanges();
return View("AddToMeeting", model);
}
public ActionResult AddTempTime(DateTime Start, Meeting meeting)
{
TempTimes.Add(Start);
meeting.Times = TempTimes;
return View("CreateMeeting", meeting);
}
public ActionResult ChooseTimes()
{
var userName = User.Identity.Name;
var user = db.Users.FirstOrDefault(u => u.UserName == userName);
Meeting meeting = new Meeting();
List<Meeting> Meetings = db.Meetings.ToList();
foreach (var item in Meetings)
{
if (item.Invited.Contains(user))
{
meeting = item;
}
}
return View(meeting);
}
There are better ways to do multiple things you are trying to do, but this will solve your issue.
You have to create a list of the meetings populated from the database so it is an Enumerable at the time you iterate rather than an query able,
I tried to save my entire list in one field, wich obviusly isn't possible. So i had to create a new model with a database to save evrything in my list. Here is my save method if anyone else is having problems saving items in a list.
public void SaveTimes(Meeting meeting)
{
foreach (var time in TempStart)
{
var mt = new MeetingTimes
{
MeetingId = meeting.Id,
Meeting = meeting,
Time = time
};
db.MeetingTimes.Add(mt);
db.SaveChanges();
}
}
I have an Employee object, I'm trying to update a record (i.e., Update / Remove) using a multiple task (Parallel Execution) using single DB Entity Context. But I'm getting the following exception
Message = "Object reference not set to an instance of an object."
Consider the following DTO's
public class Employee
{
public int EmployeeId { get; set; }
public string FirstName { get; set; }
public string LastName { get; set; }
public List<ContactPhone> ContactPhoneNumbers { get; set; }
public List<ContactEmail> ContactEmailAddress { get; set; }
}
public class ContactPhone
{
public int ContactId { get; set; }
public string Type { get; set; }
public string Number { get; set; }
}
public class ContactEmail
{
public int ContactId { get; set; }
public string Type { get; set; }
public string Number { get; set; }
}
Employee Table:
EmployeeId FirstName LastName
_________________________________
1 Bala Manigandan
ContactPhone Table:
ContactId EmployeeId Type Number
__________________________________________
1 1 Fax 9123456789
2 1 Mobile 9123456789
ContactPhone Table:
ContactId EmployeeId Type EmailAddress
______________________________________________
1 1 Private bala#gmail.com
2 1 Public bala#ymail.com
In-Coming API Object is
DTO.Employee emp = new DTO.Employee()
{
EmployeeId = 1,
FirstName = "Bala",
LastName = "Manigandan",
ContactPhoneNumbers = new List<DTO.ContactPhone>
{
new DTO.ContactPhone()
{
Type = "Mobile",
Number = "9000012345"
}
},
ContactEmailAddress = new List<DTO.ContactEmail>()
{
new DTO.ContactEmail()
{
Type = "Private",
EmailAddress = "bala#gmail.com"
},
new DTO.ContactEmail()
{
Type = "Public",
EmailAddress = "bala#ymail.com"
}
}
};
I'm getting an API request to update Mobile number and to remove the Fax number for a specified Employee.
Consider the task methods:
public void ProcessEmployee(DTO.Employee employee)
{
if(employee != null)
{
DevDBEntities dbContext = new DevDBEntities();
DbContextTransaction dbTransaction = dbContext.Database.BeginTransaction();
List<Task> taskList = new List<Task>();
List<bool> transactionStatus = new List<bool>();
try
{
Employee emp = dbContext.Employees.FirstOrDefault(m => m.EmployeeId == employee.EmployeeId);
if (emp != null)
{
Task task1 = Task.Factory.StartNew(() =>
{
bool flag = UpdateContactPhone(emp.EmployeeId, employee.ContactPhoneNumbers.FirstOrDefault().Type, employee.ContactPhoneNumbers.FirstOrDefault().Number, dbContext).Result;
transactionStatus.Add(flag);
});
taskList.Add(task1);
Task task2 = Task.Factory.StartNew(() =>
{
bool flag = RemoveContactPhone(emp.EmployeeId, "Fax", dbContext).Result;
transactionStatus.Add(flag);
});
taskList.Add(task2);
}
if(taskList.Any())
{
Task.WaitAll(taskList.ToArray());
}
}
catch
{
dbTransaction.Rollback();
}
finally
{
if(transactionStatus.Any(m => !m))
{
dbTransaction.Rollback();
}
else
{
dbTransaction.Commit();
}
dbTransaction.Dispose();
dbContext.Dispose();
}
}
}
public async Task<bool> UpdateContactPhone(int empId, string type, string newPhone, DevDBEntities dbContext)
{
bool flag = false;
try
{
var empPhone = dbContext.ContactPhones.FirstOrDefault(m => (m.EmployeeId == empId) && (m.Type == type));
if (empPhone != null)
{
empPhone.Number = newPhone;
await dbContext.SaveChangesAsync();
flag = true;
}
}
catch (Exception ex)
{
throw ex;
}
return flag;
}
public async Task<bool> RemoveContactPhone(int empId, string type, DevDBEntities dbContext)
{
bool flag = false;
try
{
var empPhone = dbContext.ContactPhones.FirstOrDefault(m => (m.EmployeeId == empId) && (m.Type == type));
if (empPhone != null)
{
dbContext.ContactPhones.Remove(empPhone);
await dbContext.SaveChangesAsync();
flag = true;
}
}
catch (Exception ex)
{
throw ex;
}
return flag;
}
I'm getting following exception:
Message = "Object reference not set to an instance of an object."
Here with I'm attaching the screenshot for your reference
My requirement is to do all the database UPSERT processes in parallel execution, kindly assist me how to achieve this without any exception using Task
1st)Stop using the context in different threads.
DbContext is NOT thread safe,that alone can cause many strange problems ,even a crazy NullReference exception
Now,are you sure your Parallel code is faster than a non parallel implementation?
I very much doubt that.
From what I see you are don't even changing your Employee object so I don't see why you should load it (twice)
I think all you need is
1)Load the phone which you need to update and set the new Number
2)Delete the unused Mobile
DON'T have to load this record.Just use the default constructor and set the Id.
EF can handle the rest (Of course you need to attach the newly created object)
3)Save your changes
(Do 1,2,3 in 1 method using the same context)
If for some reason you do decide to go with multiple tasks
Create a new context within each Task
Wrap your code in a TransactionScope
Update
I just noticed this:
catch (Exception ex) { throw ex; }
This is bad (you lose the stacktrace)
Either remove the try/catch or use
catch (Exception ex) { throw ; }
Update 2
Some sample code (I assume your input contains the Ids of the entities you want to update/delete)
var toUpdate= ctx.ContactPhones.Find(YourIdToUpdate);
toUpdate.Number = newPhone;
var toDelete= new ContactPhone{ Id = 1 };
ctx.ContactPhones.Attach(toDelete);
ctx.ContactPhones.Remove(toDelete);
ctx.SaveChanges();
If you go with the parallel approach
using(TransactionScope tran = new TransactionScope()) {
//Create and Wait both Tasks(Each task should create it own context)
tran.Complete();
}
Possible places where this error could occur are - employee.ContactPhoneNumbers.FirstOrDefault().Type, employee.ContactPhoneNumbers.FirstOrDefault()
The employee.ContactPhoneNumbers will be possibly null as you are not eager loading it nor you have marked the property as virtual so that it would lazy load.
So to fix this issue:
1. Mark the navigational properties as virtual so that lazy load
public virtual List<ContactPhone> ContactPhoneNumbers { get; set; }
public virtual List<ContactEmail> ContactEmailAddress { get; set; }
Or Eager load the entities using .Include
Or Explicit load the entities
dbContext.Entry(emp).Collection(s => s.ContactPhoneNumbers).Load();
dbContext.Entry(emp).Collection(s => s.ContactEmailAddress ).Load();
Suppose I have following table:
public class User
{
public User()
{
}
public User(string name, string pass)
{
addUser(name, pass);
}
public void addUser(string name, string pass)
{
//todo cryptography
this.login = name;
this.password = pass;
}
[PrimaryKey, AutoIncrement]
public int ID { get; set; }
[Unique, MaxLength(20)]
public string login { get; set; }
[MaxLength(20)]
private string password { get; set; }
public string group { get; set; }
}
I have unique keyword in login field. If I add another person with same login, exception will be thrown or this insert command will be skiped?
What is a best way to get user name from User table?
To get all users depending on some name condition I use this example function for my test purpose.
public async void GetRow(string name)
{
var query = dbConnection.Table<User>().Where(x => x.login.Contains(name));
var result = await query.ToListAsync();
foreach (var item in result)
{
User u = item as User;
MessageDialog msgbox = new MessageDialog(u.login);
await msgbox.ShowAsync();
}
}
Suppose I want to get only 1 record from Users table based on a given name, what would be best way to do that.
I tried something like this:
from u in dbConection.Table<User> select u.Login where u.Login = name;
How to return from GetRow function described in 2 question user password? I can recieve only list of items, I search on the web and I find FirstOrDefault function but is there any better way to do it?
Edit:
ad 1. Throws exception
ad 2.This works only if record exists in table, in other case throws exception
var query = (from s in dbConnection.Table<User>() where s.login == name && s.password == password select s).FirstAsync();
User qr = query.Result;
I find solution. This is a simple validation function. Thank You for Your help.
try
{
var query = (from s in dbConnection.Table<User>() where s.login == name && s.password == password select s).FirstAsync();
User x = await query;
if (x != null) return true;
else return false;
}
catch (Exception)
{
return false;
}
var some_strings = new List<string> {"Szcz","epan"};
string first_string = some_strings.FirstOrDefault();
//first_string = "szcz";
some_strings = new List<string>();
first_string = some_strings.FirstOrDefault();
//first_string = null;
if (first_string != null)
// do your stuff here.
if you were dealing with Int's, then the default would have been zero. IF it's a custom class, it'll be whatever your default for the class is.
I am submitting a form that contains a user id and a pipe-delimited set of numbers.
The model looks like this:
public class SeasonPassData
{
public int UserID { get; set; }
public string SpaceNumbers { get; set; }
}
That is getting handed off to this controller:
[HttpPost]
public ActionResult SignUp(SeasonPassData data)
{
var user = db.Users.Find(data.UserID);
List<SeasonPass> passes = new List<SeasonPass>();
char[] delimiter = { '|' };
var numbers = data.SpaceNumbers.Split(delimiter).Select(n => Convert.ToInt32(n)).ToArray();
foreach(int number in numbers)
{
passes.Add(new SeasonPass { SpaceNumber=number, User = user });
}
passes.ForEach(p => db.SeasonPasses.Add(p));
db.SaveChanges();
return RedirectToAction("Confirmation", "Home");
}
And should be creating and saving SeasonPasses:
public class SeasonPass
{
public int ID { get; set; }
public int SpaceNumber { get; set; }
public virtual User User { get; set; }
}
However, this - passes.ForEach(p => db.SeasonPasses.Add(p)); keeps raiding this exception:
An exception of type 'System.InvalidOperationException' occurred in
System.Data.Entity.dll but was not handled in user code
Additional information: The relationship between the two objects
cannot be defined because they are attached to different ObjectContext
objects.
How do I fix that?
I tried it with these changes based on a comment. It still doesn't seem to work...
[HttpPost]
public ActionResult SignUp(SeasonPassData data)
{
using(var context = new TTUContext())
{
var user = context.Users.Find(data.UserID);
List<SeasonPass> passes = new List<SeasonPass>();
char[] delimiter = { '|' };
var numbers = data.SpaceNumbers.Split(delimiter).Select(n => Convert.ToInt32(n)).ToArray();
foreach (int number in numbers)
{
passes.Add(new SeasonPass { SpaceNumber = number, User = user });
}
foreach (var pass in passes)
{
context.SeasonPasses.Add(pass);
}
context.SaveChanges();
}
return RedirectToAction("Confirmation", "Home");
}
I'm surprised that what you are doing does not work, but here is a way to sidestep the issue.
Add public int UserID { get; set; } to SeasonPass and set it to assign the relationship instead of setting User.