You will be appreciated to share your experience how to show the data list view being filtered according to the logged user.
Regards
Arun Sahani, Kathmandu
It really depends on what credentials you use to filter the data. Nevertheless, you need to add a field in the model that you want o retrieve the list of.For example:
public class Product
{
[Key]
[Required]
[Display(Name = "Product ID")]
public int productID { get; set; }
[Required]
[Display(Name = "Product Name")]
public string productName { get; set; }
[Required]
[Display(Name = "Product Price(RM)")]
public double productPrice { get; set; }
[Required]
public string productAddedBy{ get; set; }
}
Notice the 'productAddedBy' column in Product table as you need to assign the value of logged user of either id or name when you create a product:
BY ID
//Initialise product instance
Product p = new Product();
p.productAddedBy= User.Identity.GetUserId();
BY Name
//Initialise product instance
Product p = new Product();
p.productAddedBy= User.Identity.GetUserName();
After saving the product into database, you can do a LINQ query to filter the data from DbSet and pass the result to a view. Of course, you will need to initialise an instance of your db context class and the user must be logged in.
List<Product> productList = new List<Product>();
productList = 'your_db_context_instance'.Product.Where(x=> x.productAddedBy == User.Identity.GetUserId());
return View(productList);
The above codes will get all the products added by user based on the id of logged user. The final thing is to pass the 'productList' to a strongly typed view and you're good to go.
Related
I have registration form where user can input username and password and also create company at the same time(or chose one of existing companies). User have multiple companies and company have multiple users. How can I correctly choose User or Company to be my aggregate root(or both of them?).
I set Company as an aggregate root, it is ok in registration process I'm creating company and adding user to the company (company.addUsers(...)). But when I want to update user profile info(such as name, surname), I cannot do it in company aggregate root. I can create UserAggreageRoot and update user there but in this case it would affect to registration process(because in one transaction UserAggregate and CompanyAggregate would be updated, which is wrong).
An aggregate can be viewed as an entity that covers a consistency boundary for a particular action.
If Company and User are in the same domain then you could use Company as an aggregate for creating users:
public class Company
{
public List<User> Users { get; set; }
public void AddUser(string forename, string surname)
{
User user = new User(forename, surname);
}
}
public class User
{
public string Forename { get; set; }
public string Surname { get; set; }
public User(string forename, string surname)
{
Forename = forename;
Surname = surname;
}
}
Your command handler can then create the company and call AddUser and then add the company to the unit of work via a repository. You are only changing one aggregate (consistency boundary). When the unit of work is committed your infrastructure will add the company and user within one transaction.
But then if you just want to change user's name (and there is no need for the Company to know about that) then you can retrieve a User an aggregate.
A domain class can be an aggregate in one context but not in another.
public class User
{
public string Forename { get; set; }
public string Surname { get; set; }
public User(string forename, string surname)
{
Forename = forename;
Surname = surname;
}
public void SetForename(string forename)
{
Forename = forename;
}
public void SetSurname(string surname)
{
Surname = surname;
}
}
The command handler will retrieve the User from the repository (and add the user to the unit of work) and make the changes to user before committing the unit of work.
Neil W, if I understand correctly you suggest something like this
class User : Entity, IAggregateRoot
{
public string Forename { get; protected set; }
public string Surname { get; protected set; }
//methods like Update(), ChangeStatus(), etc
}
class Company : Entity, IAggregateRoot
{
public string Name { get; protected set; }
public List<User> Users { get; protected set; }
//...methods like AddUser(), ChangeName(), etc
}
But doesn't this mean that in registration process both User and Company AGGREGATES are created?
So I'm creating a simple MVC app that uses the Absence class as a model which holds different properties including an object from the Employee class-another model that holds various properties:
public class Absence
{
public int Id { get; set; }
public string Reason { get; set; }
public int Day { get; set; }
public int Month { get; set; }
public bool isApproved { get; set; }
public int employee_Id { get; set; }
public virtual Employee employee { get; set; }
public Absence()
{
employee = new Employee();
}
}
And I created a controller that has an ActionResult function for the create View:
[HttpGet]
public ActionResult Create()
{
Absence abs = new Absence();
return View(abs);
}
[HttpPost]
public ActionResult Create(Absence abb)
{
Employee emp = database.Employees.FirstOrDefault(z => z.Id == abb.employee_Id);
System.Diagnostics.Debug.WriteLine(emp.Name);
abb.employee.Name = emp.Name;
abb.employee.Surname = emp.Surname;
System.Diagnostics.Debug.WriteLine(abb.employee.Name);
database.Absences.Add(abb);
database.SaveChanges();
return Redirect("/Absence");
}
The idea is to talk to the database find an Employee object with the same EmployeeId and set the name and surname of the employee object of the abb object to be the same and after testing it with the debugger I can see that it works.
However when I want to display all the added absences including the name and surname of their employee like this:
public ActionResult Index()
{
return View(database.Absences.ToList());
}
The name and surname of all the employees don't show.
It seems that all the properties are saved in the database using entity framework except for the Employee object.
Any ideas for how to save it?
Can you try saving the Absences first before updating the employee details? I don't exactly know how your database models are configured and this is just an assumption. The absence that is referencing your employee is not yet available when you are supplying the employee details.
Employee emp = database.Employees.FirstOrDefault(z => z.Id == abb.employee_Id);
database.Absences.Add(abb); //Moved adding here
database.SaveChanges(); // Perform the saving
abb.employee.Name = emp.Name;
abb.employee.Surname = emp.Surname;
database.SaveChanges();
I have some problems in my ASP.NET Core 2.0 application which I am trying to render some links with information based on each user. The menus are in the _Layout.cshtml file and the properties are in another table which has a relation one-to-one with ASPNETUsers table by the ID (Id of the user).
Here is my Model Class for the extra table that I am using:
public class UserDetail
{
[Key]
public int DetailId { get; set; }
// user ID from AspNetUser table.
[ForeignKey("ApplicationUser")]
public string OwnerID { get; set; }
[Display(Name = "First name")]
[Required]
[StringLength(120, ErrorMessage = ("First name is required"))]
public string Name { get; set; }
[Display(Name = "Last name")]
[Required]
[StringLength(120, ErrorMessage = ("Last name is required"))]
public string LastName { get; set; }
[Required]
[ForeignKey("Company")]
public int CompanyId { get; set; }
[Required]
[ForeignKey("Store")]
public int StoreId { get; set; }
public Company Company { get; set; }
public Store Store { get; set; }
public ApplicationUser User { get; set; }
}
This is my _Layout.cshtml which contains some links that the asp-route-id needs to change base on each user StoreId attribute.
<ul class="dropdown-menu">
<li><a asp-controller="StockOnHand" asp-action="List" asp-route-id="1">List current Stock</a></li>
<li><a asp-controller="StockOnHand" asp-action="TakeInSerial" asp-route-id="1">Receive a product</a></li>
</ul>
My problem is as you seen in my layout file the asp-rout-id is hardcoded; what I need to do is that to be map to the StoreId proprerty based on the UserDetail which is linked to the ApplicationUser identity.
How can I do that? is this possible?
Thanks in advance for your help.
Julio.
There are many ways to achieve this. One way would be to use a View Component.
Plase see Microsoft Docs - View Component
You can inject your database context directly into the View Component similar to what you do with a controller.
public class StoreLinksViewComponent : ViewComponent
{
private readonly yourDbContext db;
public PriorityListViewComponent(yourDbContext context)
{
db = context;
}
public async Task<IViewComponentResult> InvokeAsync(
string email)
{
var storeId= db.UserDetails.Where(x => x.User.Email == email).FirstOrDefault()?.StoreId;
//create a model or ViewData here to send to your default view which is really a partial view
//then return the partial view to display in the layout view.
}
}
Once created you can invoke it directly from the Layout View or any other place where it is required in the application. You can even send the current users email as an argument:
#await Component.InvokeAsync("StoreLinks", new {email= User.Identity.Name})
We have a DTO - Employee - with many (> 20) related DTOs and DTO collections. For "size of returned JSON" reasons, we have marked those relationships as [Ignore]. It is then up to the client to populate any related DTOs that they would like using other REST calls.
We have tried a couple of things to satisfy clients' desire to have some related Employee info but not all:
We created a new DTO - EmployeeLite - which has the most-requested fields defined with "RelatedTableNameRelatedFieldName" approach and used the QueryBase overload and that has worked well.
We've also tried adding a property to a request DTO - "References" - which is a comma-separated list of related DTOs that the client would like populated. We then iterate the response and populate each Employee with the related DTO or List. The concern there is performance when iterating a large List.
We're wondering if there a suggested approach to what we're trying to do?
Thanks for any suggestions you may have.
UPDATE:
Here is a portion of our request DTO:
[Route("/employees", "GET")]
public class FindEmployeesRequest : QueryDb<Employee> {
public int? ID { get; set; }
public int[] IDs { get; set; }
public string UserID { get; set; }
public string LastNameStartsWith { get; set; }
public DateTime[] DateOfBirthBetween { get; set; }
public DateTime[] HireDateBetween { get; set; }
public bool? IsActive { get; set; }
}
There is no code for the service (automagical with QueryDb), so I added some to try the "merge" approach:
public object Get(FindEmployeesRequest request) {
var query = AutoQuery.CreateQuery(request, Request.GetRequestParams());
QueryResponse<Employee> response = AutoQuery.Execute(request, query);
if (response.Total > 0) {
List<Clerkship> clerkships = Db.Select<Clerkship>();
response.Results.Merge(clerkships);
}
return response;
}
This fails with Could not find Child Reference for 'Clerkship' on Parent 'Employee'
because in Employee we have:
[Ignore]
public List<Clerkship> Clerkships { get; set; }
which we did because we don't want "Clerkships" with every request. If I change [Ignore] to [Reference] I don't need the code above in the service - the List comes automatically. So it seems that .Merge only works with [Reference] which we don't want to do.
I'm not sure how I would use the "Custom Load References" approach in an AutoQuery service. And, AFAIKT, the "Custom Fields" approach can't be use for related DTOs, only for fields in the base table.
UPDATE 2:
The LoadSelect with include[] is working well for us. We are now trying to cover the case where ?fields= is used in the query string but the client does not request the ID field of the related DTO:
public partial class Employee {
[PrimaryKey]
[AutoIncrement]
public int ID { get; set; }
.
.
.
[References(typeof(Department))]
public int DepartmentID { get; set; }
.
.
.
public class Department {
[PrimaryKey]
public int ID { get; set; }
public string Name { get; set; }
.
.
.
}
So, for the request
/employees?fields=id,departmentid
we will get the Department in the response. But for the request
/employees?fields=id
we won't get the Department in the response.
We're trying to "quietly fix" this for the requester by modifying the query.SelectExpression and adding , "Employee"."DepartmentID" to the SELECT before doing the Db.LoadSelect. Debugging shows that query.SelectExpression is being modified, but according to SQL Profiler, "Employee"."DepartmentID" is not being selected.
Is there something else we should be doing to get "Employee"."DepartmentID" added to the SELECT?
Thanks.
UPDATE 3:
The Employee table has three 1:1 relationships - EmployeeType, Department and Title:
public partial class Employee {
[PrimaryKey]
[AutoIncrement]
public int ID { get; set; }
[References(typeof(EmployeeType))]
public int EmployeeTypeID { get; set; }
[References(typeof(Department))]
public int DepartmentID { get; set; }
[References(typeof(Title))]
public int TitleID { get; set; }
.
.
.
}
public class EmployeeType {
[PrimaryKey]
public int ID { get; set; }
public string Name { get; set; }
}
public class Department {
[PrimaryKey]
public int ID { get; set; }
public string Name { get; set; }
[Reference]
public List<Title> Titles { get; set; }
}
public class Title {
[PrimaryKey]
public int ID { get; set; }
[References(typeof(Department))]
public int DepartmentID { get; set; }
public string Name { get; set; }
}
The latest update to 4.0.55 allows this:
/employees?fields=employeetype,department,title
I get back all the Employee table fields plus the three related DTOs - with one strange thing - the Employee's ID field is populated with the Employee's TitleID values (I think we saw this before?).
This request fixes that anomaly:
/employees?fields=id,employeetypeid,employeetype,departmentid,department,titleid,title
but I lose all of the other Employee fields.
This sounds like a "have your cake and eat it too" request, but is there a way that I can get all of the Employee fields and selective related DTOs? Something like:
/employees?fields=*,employeetype,department,title
AutoQuery Customizable Fields
Not sure if this is Relevant but AutoQuery has built-in support for Customizing which fields to return with the ?fields=Field1,Field2 option.
Merge disconnected POCO Results
As you've not provided any source code it's not clear what you're trying to achieve or where the inefficiency with the existing solution lies, but you don't want to be doing any N+1 SELECT queries. If you are, have a look at how you can merge disconnected POCO results together which will let you merge results from separate queries based on the relationships defined using OrmLite references, e.g the example below uses 2 distinct queries to join Customers with their orders:
//Select Customers who've had orders with Quantities of 10 or more
List<Customer> customers = db.Select<Customer>(q =>
q.Join<Order>()
.Where<Order>(o => o.Qty >= 10)
.SelectDistinct());
//Select Orders with Quantities of 10 or more
List<Order> orders = db.Select<Order>(o => o.Qty >= 10);
customers.Merge(orders); // Merge disconnected Orders with their related Customers
Custom Load References
You can selectively control which references OrmLite should load by specifying them when you call OrmLite's Load* API's, e.g:
var customerWithAddress = db.LoadSingleById<Customer>(customer.Id,
include: new[] { "PrimaryAddress" });
Using Custom Load References in AutoQuery
You can customize an AutoQuery Request to not return any references by using Db.Select instead of Db.LoadSelect in your custom AutoQuery implementation, e.g:
public object Get(FindEmployeesRequest request)
{
var q = AutoQuery.CreateQuery(request, Request);
var response = new QueryResponse<Employee>
{
Offset = q.Offset.GetValueOrDefault(0),
Results = Db.Select(q),
Total = (int)Db.Count(q),
};
return response;
}
Likewise if you only want to selectively load 1 or more references you can change LoadSelect to pass in an include: array with only the reference fields you want included, e.g:
public object Get(FindEmployeesRequest request)
{
var q = AutoQuery.CreateQuery(request, Request);
var response = new QueryResponse<Employee>
{
Offset = q.Offset.GetValueOrDefault(0),
Results = Db.LoadSelect(q, include:new []{ "Clerkships" }),
Total = (int)Db.Count(q),
};
return response;
}
I have a table of links, and some links will be child links, referencing the parent links ID
however i can not get my head around servicestack ormlite and populating a property of children, will all the child links when getting a list of all links.
Here is my model:
public partial class Navigation
{
[Alias("Id"), AutoIncrement]
public int Id { get; set; }
[Alias("ParentId")]
[Display( Name = "ParentId")]
[References(typeof(Navigation))]
public int? ParentId { get; set; }
[Alias("LinkText")]
[StringLength(50, ErrorMessage = " Must be no more than 50 characters long!")]
[Display( Name = "LinkText")]
public string LinkText { get; set; }
[Alias("Action")]
[StringLength(50, ErrorMessage = " Must be no more than 50 characters long!")]
[Display( Name = "Action")]
public string Action { get; set; }
[Alias("Controller")]
[StringLength(50, ErrorMessage = " Must be no more than 50 characters long!")]
[Display( Name = "Controller")]
public string Controller { get; set; }
[Alias("Area")]
[StringLength(50, ErrorMessage = " Must be no more than 50 characters long!")]
[Display( Name = "Area")]
public string Area { get; set; }
[Alias("Visible")]
[Display( Name = "Visible"),Required(ErrorMessage = " is required" )]
public bool Visible { get; set; }
[Alias("Sequence")]
[Display( Name = "Sequence")]
public int? Sequence { get; set; }
[ForeignKey(typeof(Navigation))]
public virtual ICollection<Navigation> Children { get; set; }
}
any ideas ?
You can do that using inheritance. The parent class will contain a reference to the child class. I had to use it to get which user have created each user. Here is a sample:
public class UserCommon
{
[References(typeof(User))] // Self reference workaround for User ;)
public Guid CreatedBy { get; set; }
}
public class User : UserCommon
{
public Guid Uid { get; set; }
public String Username { get; set; }
public String Password { get; set; }
}
Something you need to pay attention to is to include the Id in the child class not the parent. The table that will be generated is as follow. The foreign key is a self reference
Getting the list of children should be as easy as a simple LINQ query that will fetch all children for a certain parent Guid. CreatedBy is also a property of User becuase of inheritance.
db.Select<User>(q => q.CreatedBy == '734FD814-024D-4795-AFD0-34FECF89A13A');
// Just a sample Guid, you should be able to select
// the Guid you need and insert it here.
Tables in OrmLite are strictly a 1:1 mapping with the underlying db tables.
This means all complex type properties are blobbed into a db text field with the property name, they're never used to auto-map to child relations as you're expecting to do here.
Here's an early answer that shows how you could map many to many relations with OrmLite.