Aggregate roots in DDD - domain-driven-design

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?

Related

EF5 Object property won't save in a database

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();

How to render links based on user (extender properties) in ASP.NET MVC Core 2.0

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})

How to get a subset of Users from ASP.NET Identity 2

Using a slightly modified version of the default ASP.NET MVC 5 template (with Individual Accounts), I am trying to get a subset of users based on an intermediary table. I have already built up an administration UI that can return a list of all users, but now I need to limit the set of users returned based on the currently logged in user's access privileges defined in the intermediary table.
Essentially, each user will have access to 1 or more clinics, so there will be one record for each clinic to which they have access.
If the currently logged in user belongs to a given role (e.g., "Clinic Admin"), then they should have the ability to retrieve a list of any users who belong to any of the clinics to which they have access.
Can anyone help point me in the right direction? This is my first Anything.NET application, so please feel free to explain like I'm five. :-)
Thank you in advance for any help you can offer.
Additional information:
Visual Studio 2013 Update 5
Entity Framework 6
MS SQL Server 2008 R2
Here is the intermediary table's class (ClinicUser):
[Table("clinic_users")]
public class ClinicUser
{
[Key]
public virtual ApplicationUser ApplicationUsers { get; set; }
[Required]
public string Id { get; set; }
[Required]
public System.Guid provider_id { get; set; }
[Required]
public System.Guid health_system_id { get; set; }
[Required]
public System.Guid clinic_id { get; set; }
}
Here is my ApplicationUser class:
public class ApplicationUser : IdentityUser
{
public string FirstName { get; set; }
public string LastName { get; set; }
public string FullName
{
get { return FirstName + " " + LastName; }
}
[ForeignKey("ClinicUsers")]
public override string Id
{
get
{
return base.Id;
}
set
{
base.Id = value;
}
}
public virtual ClinicUser ClinicUsers { get; set; }
public IEnumerable<SelectListItem> RolesList { get; set; }
public async Task<ClaimsIdentity> GenerateUserIdentityAsync(UserManager<ApplicationUser> manager)
{
// Note the authenticationType must match the one defined in CookieAuthenticationOptions.AuthenticationType
var userIdentity = await manager.CreateIdentityAsync(this, DefaultAuthenticationTypes.ApplicationCookie);
// Add custom user claims here
userIdentity.AddClaims(ClinicClaimsProvider.GetClaims(userIdentity));
return userIdentity;
}
}
In case it wasn't clear, what I'm really trying to do is narrow the list of ApplicationUsers to return only the list of users to which I have access to based on the clinics we have have in common.
If I were writing this as a SQL query, this would be one way to accomplish what I want (I just can't seem to quite get what I want with LINQ):
SELECT *
FROM AspNetUsers au
WHERE Id IN (
SELECT Id
FROM clinic_users
WHERE clinic_id IN (
SELECT clinic_id
FROM clinic_users
WHERE Id = 'CurrentUserId'
)
)
First of all do not user much properties in ApplicationUser class, you can manage user profiles table and connect it with application user class, so you can put lot of information about user in profile table.
Next task is organize table of clinics, branches etc... and asociate application users with them.
Next you have 2 ways:
1. asociate application users with clinics or branches.
or
2. Manage them with roles.
Here is example with Application users:
[Table("Clinics")]
public class Clinic
{
[Key]
public string Id { get; set; }
public virtual ICollection<ClinicUser> ClinicUsers { get; set; }
}
[Table("ClinicUsers")]
public class ClinicUser
{
[Key]
[Column(Order = 0)]
public string ClinicId { get; set; }
[Key]
[Column(Order = 1)]
public string UserId { get; set; }
}
So next you need Other ViewModels to display them hope this help.
UPDATE
// GET: ClinicUsers by Clinic
public async Task<ActionResult> ViewCurrentClinicUsers(string id) // This is clinis ID
{
if (id == null)
{
return new HttpStatusCodeResult(HttpStatusCode.BadRequest);
}
Clinic clinic = await db.clinic.FindAsync(id); // Get Selectedclinic
if (clinic == null)
{
return HttpNotFound();
}
ClinicUsers model = new ClinicUsers() // ClinicUsers model
{
clinic = clinic, // View Currentclinic
ClinicUsers = await db.ClinicUsers.Were(x => x.clinicid == clinic.id)ToListAsync()) // Get Users that asigned to current clinic
};
return View(model);
}
UPDATE 2
And Finaly if you want display clinics were is assigned current loged user
// GET: Clinics by currentuser
public async Task<ActionResult> ViewClinicsWithCurrentUserAccess()
{
var currentuserId = User.Identity.GetUserId(); // This gets currentloged user id
var currentuser = await db.Users.SingleOrDefaultAsync(x => x.Id == myUserId); // This gets currentloged user virtual
return View(await db.Clinics.Were(x => x.clinicuserid == currentuserId).ToListAsync());
}
I solved this a while back, but I thought I had better come back here and update my question with an answer, in case this might help someone else.
I updated my Clinic and ClinicUser classes accordingly:
Clinic.cs
[Table("clinics")]
public class Clinic
{
[Key]
public System.Guid ClinicId { get; set; }
public List<ClinicUser> ClinicUsers { get; set; }
}
ClinicUser.cs
[Table("clinic_users")]
public class ClinicUser
{
[Key, Column(Order = 0)]
public string UserId { get; set; }
[Key, Column(Order = 1)]
public System.Guid ClinicId { get; set; }
[ForeignKey("UserId")]
public virtual ApplicationUser ApplicationUser { get; set; }
[ForeignKey("ClinicId")]
public Clinic Clinic { get; set; }
}
Also, I updated the following excerpt of my ApplicationUser class from this:
[ForeignKey("ClinicUsers")]
public override string Id
{
get
{
return base.Id;
}
set
{
base.Id = value;
}
}
public virtual ClinicUser ClinicUsers { get; set; }
to this:
public List<ClinicUser> ClinicUsers { get; set; }
Finally, in my ApplicationUsersController's Index() action, I was able to use this:
public async Task<ActionResult> Index()
{
if (User.IsInRole("Admin")) return View(await UserManager.Users.ToListAsync());
var userId = User.Identity.GetUserId();
//Get the Ids of the current user's clinics
var userClinics = db.ClinicUsers.Where(cu => cu.UserId == userId).Select(cu => cu.ClinicId).ToList();
//Get all userIds of the user at the current user's clinics
var clinicUserIds = db.ClinicUsers.Where(cu => userClinics.Contains(cu.ClinicId)).ToList().Select(cu => cu.UserId);
var users = UserManager.Users.Where(u => clinicUserIds.Contains(u.Id));
return View(await users.ToListAsync());
}
In essence, if the user has the "Admin" role, then they will get a list of all users in the database. If they aren't, they will only get a list of the users that also belong to the clinics they have in common.
It may not be perfect, but it works. If anyone has any suggestions on how to improve this, I would be glad to hear it.
Again, my thanks to Archil (https://stackoverflow.com/users/4089212/archil-labadze) for his helpful responses.

asp.net mvc show list vies based in logged user

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.

How can I link ID in ASP.NET MVC5 identity 2.0 default connection with ID in another code first Database?

On my website when someone registers their username, email address and password are stored in the AspNetUsers table in the defaultConnection and they are given an ID. I have created another database so further user details can be stored for the user. How can I link these databases so that when a user registers they have the same id in the defaultConnection and the database which is storing further details for users? 2 databases have to be used
public class ClubMember
{
public int UserID { get; set; }
public string UserFName { get; set; }
public string UserLName { get; set; }
public string UserDescription { get; set; }
public DateTime UserDoB { get; set; }
}//end User Class
These are the further details I want to enter so I would like to match the ID with the default connection ID.

Resources