User profile service using dynamic - c#

In a system I'm working on there is a core User table. There are also roles designating various user types and application level access.
To keep the user table succinct, there is going to be a number of tables (or possibly just 1 table) holding the extra fields needed for each user type Profile.
Rather than have a number of methods returning each Profile type, would there be any issue with returning an ExpandoObject from the ProfileService? This would allow a simple FetchProfile(foo userType) function.
e.g:
public ExpandoObject FetchProfile(UserType userType)
{
dynamic x = new ExpandoObject();
switch(stuff)
{
case UserType .Type1:
//The ExpandoObject will be pulled from DB using Dapper.Net
return x;
case UserType .Type2:
//The ExpandoObject will be pulled from DB using Dapper.Net
return x;
default:
return null;
}
}
This ProfileService will be used in multiple levels of the framework (n tier.)
Are there any issues with doing this? Or would it be better to use single classes for each profile type and perhaps use generics to pull out the needed Profile class:
public T FetchProfile<T>(UserType userType) where T : IProfileObject ...
The advantage of the ExpandoObject is that the database could be constructed in such a way that new ProfileTypes can be added when the site is live without needing to add extra ProfileType classes.

The solution I arrived at was quite different to my initial ideas, so will document it here for archival purposes.
I built a form manager which allowed the addition of fields and multi-select fields (to be displayed as select lists, check-box lists etc. depending on user configuration.)
I allowed forms to be associated to roles, with fields shared across multiple forms as not to duplicate data.
This allowed me to simply pull out a form based upon role:
var form = formManager.Fetch(User.Username, Role.Name);
And loop through the form elements to generate the fields needed on user profile pages, the form manager being used to save updated data.

Related

How to handle different category of logged in user in asp.met mvc application

I am in learning phase of ASP.Net MVC 5 web development and I am stuck at one place. So, I have a website which will have three flow.
Non loggedin user
Authenticated user (Tenants)
Authenticated user (Owner)
Now during registration I have simply added a checkbox to capture if user is going to be a tenant or owner. Now the view file for all three (non loggedin, owner and tenant) needs to be different.
As of now I know how to handle two type of user (non-logged in and (tenant)logged in). In my _ViewStart.cshtml file I did something like below
#{
if(User.Identity.IsAuthenticated)
{
Layout = "~/Views/Shared/_LoggedInLayout.cshtml";
}
else
{
Layout = "~/Views/Shared/_Layout.cshtml";
}
}
So far so good. But you can clearly see there is no identificaiton of "Tenant and Owner User". Given the fact that I will have a property during registration which will be true or false for tenant and owner.
I want to do something like below
#{
if(User.Identity.IsAuthenticated)
{
if(User.IsTenant)
{Layout = "~/Views/Shared/_LoggedTenantLayout.cshtml";
}
else{Layout = "~/Views/Shared/_LoggedInOwnerlayout.cshtml";
}
}
else
{
Layout = "~/Views/Shared/_Layout.cshtml";
}
}
But I read something about Areas, and then Route. So, I am confused, if you all have three type of user Flow, what method you would have followed. I mean do I need to do some route manipulation or something. Can someone please guide me here. I know there is something called Roles too. But I think that is for adding extra privilege in a page by adding may be some edit button or something. As I am beginner, can some one tell me how can I have tenant,owner and non logged in user flow separated out.
Owner needs to see a totally different view file and options and same
goes with tenant. I know this question is kind of broad but if someone can give me a basic small code snippet on how to separate out the flow, I can take it forward from there.
Your path forward depends on the answer to this question: are tenants and owners distinct user types or simply permission sets? People often conflate these two. As it stands, it looks as if you're just looking to treat "tenants" one way and "owners" another, which can be easily solved via roles. Essentially, a tenant gets assigned a role like "Tenant" and an owner gets assigned a role like "Owner". Then, you can protect certain controllers/actions for one role or the other via:
[Authorize(Roles = "Owner")]
Which would then only allow owners to access that. For something like including a different layout. You can branch on User.IsInRole:
if (User.IsInRole("Tenant"))
{
Layout = "~/Views/Shared/_LoggedTenantLayout.cshtml";
}
else if (User.IsInRole("Owner"))
{
Layout = "~/Views/Shared/_LoggedInOwnerlayout.cshtml";
}
else
{
Layout = "~/Views/Shared/_Layout.cshtml";
}
If, however, these are distinct user types, where you need to collect and persist entirely different types of information, then you should handle it via type inheritance. For example, a tenant might have like MonthlyPayment, whereas that type of information makes no sense for a an owner. In that scenario, you'd do:
public class Tenant : ApplicationUser
{
// Tenant-specific properties
}
public class Owner : ApplicationUser
{
// Owner-specific properties
}
public class ApplicationUser: IdentityUser
{
// Shared properties
}
By default, Entity Framework will handle this type of inheritance by creating a single database table with columns for all the user types, along with a Discriminator column. The Discriminator column will hold the class name that was saved, i.e. one of "Tenant", "Owner", or "ApplicationUser", which Entity Framework will then use to instantiate the right type when you query out users.
For the most part, this works just fine, but you must be aware that you cannot have non-nullable columns on any derived types. This is due to the fact that other derived types or the base type won't be able to fill this column, because they won't have that property. You can still enforce that a particular property is required for the purposes of something like a form collecting user input, via a view model, though; it just can't be enforced at the database level.
If that's a deal-breaker or you just don't like having everything in a single table, you can choose to use a table-per-type inheritance strategy. With that, you'd have a table for each type, ApplicationUser, Tenant, and Owner. However, the table for ApplicationUser would hold all the base columns and everything shared between all user types, while the derived-type tables will only hold columns specific to their unique properties. To use this type of inheritance strategy, simply add the Table attribute to your derived types:
[Table("Tenants")]
public class Tenant : ApplicationUser
You have two concepts at play here:
Authentication (logged in or not)
Authorization (Tenant, Owner)
You have already figured out the Authentication piece.
For the Authorization, you can use Roles. The general idea is that you are going to assign your users a role when they sign up (based on your checkbox). From there on, you can check to see which role the user belongs to and route them appropriately.
There are many ways for you to handle how you serve the content. One of them is Areas. You could in theory create an area for Tenants and an area for Owners, although this may prove overkill for your learning purposes.
Check out the following tutorial for implementation details:
https://code.msdn.microsoft.com/ASPNET-MVC-5-Security-And-44cbdb97

Using Hardcoded Types in an ASP.NET MVC Application w/ Entity Framework

What is a good way to handle/organize/use Hardcoded Types with an ASP.NET MVC application using Entity Framework?
By Hardcoded Types I mean static types defined in the business logic that will not be manageable by the User, only the developer. i.e. ProductTypes, CustomerStatuses, etc..
I am trying to achieve a couple of things:
Works well with Entity Framework data binding
Will be able to populate MVC View DropDownLists easily (assuming my ViewModel will be able to have a list of the type)
Will have a reliable valued stored in SQL (whether it is a string or Id)
Will be able to use in if/case statements throughout business logic
Using Enums:
For example lets say I have a Product entity which has a Type field.
My first instinct is to create an Enum directly in the class called ProductType with its values. However if I use enums throughout I am worried that I won't be able to accomplish the 4 above requirements.
Using GlobalConstants
In the past I have also created a GlobalConstants file and just created a bunch of constant strings so I could use them throughout the BusinessLogic. Then in the database I would create a TypeList table (TypeName, TypeValue, FriendlyName, etc..) that stores all of these values. So basically the TypeList table would always have to stay in sync with the GlobalConstants or something would break.
Something is telling me Enums is perfect for these hardcoded types
Just create a model for ProductType. Example:
ProductType: {int id, string name}
In the Product model class add:
public ProductType Type { get; set; }
So basically you can store the types in database.

How to code an object that is specific to a logged in user

I am coding a C# MVC5 internet application and I have a question about the model structure.
I am wanting a user to register and once registered, the user can create some MapLocation objects. Because each MapLocation object belongs to a user, I wish to be able to find all MapLocations that have been created by a user, how do I code the models?
I am thinking of two screnarios, but either of these could not be correct.
Scenario 1:
I code a UserInfo class, and add this to the ApplicationUser class when a user registers. This UserInfo class has a List of MapLocations. Whenever a MapLocation is created, it is added to the List of MapLocations in the UserInfo class of the specific user.
Scenario 2:
I have a string variable in the MapLocation object that stores the logged in user's name. Whenever I create a MapLocation object, the logged in user's name is added to this MapLocation object. I can then search for MapLocations based on the string variable.
Are either of the above scenarios the one to go with? Is there a better way to do this? Is there a specific design pattern that caters to this situation?
Thanks in advance
I go with the former, from the OOP perspective IMO it seems the better choice. I wouldn't want a MapLocation object to be aware of a user at all. Or i would create a MapLocationManager, which internally holds a LookUp mapping a UserId to the list of MapLocations he created, that way i can elimnate the ApplicationUser from being incharge of managing the MapLocations, which may cause you to move the ApplicationUser class to all the places inside your code the need to access those MapLocations.
Seeing your "comment" on Yuval's answer, here is a third solution which would likely be better in case of getting the data onto a database/server.
Class UserInfo - Credentials/Login Info - Preferences
private list<int> _ownedMapLocations;
MapLocationClass
Class MapLocations
private int _id;
Database concept of keys, will allow you to store them properly/efficiently in the databse (normalization). It would also be possible for an "user" share it's MapLocation easily or to copy someone else into his own set.

Data Entities > Domain Objects > ViewModels, each with drastically different data structures

This is sort of a generic question in regards to mapping between data entities, domain objects, and ViewModels. I may not be asking it right but hopefully I can make some sense of it. Below is a simplified problem.
Pretend I have an Entity Framework model which maps 1:1 to my database tables, but my domain objects may not be identical, and my ViewModel is drastically different again. As a pseudo-example:
Database/EF Entities:
MembershipAccount
MembershipAccountExtraInfo
Domain:
Account
Profile
Preferences
ViewModel:
UserProfileModel
Let's say I need to display a UserProfileModel which has: Username (from MembershipAccount), SignupDate (from MembershipAccount), FullName (from MembershipAccountExtraInfo), and TimeZone (from MembershipAccountExtraInfo)
What sort of relationships might I need here, and what sort of mapping mechanisms? Is it common to have something like an AccountMapper that takes both a MembershipAccount and MembershipAccountExtraInfo and returns an Account? I'm a bit stuck on the mapping when several objects are needed to create a single domain entity, and vice versa.
If it helps: I'm designing an API for managing User Accounts, User Profiles, User Preferences, etc. but the database tables are all over the place. A single User Profile might need to be created from data spanning 4-5 tables and 2 databases. There is no 1:1 mapping between my database tables and any (logical) domain objects.
Thanks!
I like to work keeping my domain objects as close to the objects that they represent as possible. What I mean by this is that if an account has preferences, then the domain Account object should contain a Preferences property, most likely represented by a collection of Preference objects. If nothing else, this helps the users understand the data structure of the application easily.
As for constructing the view models, that's the easiest bit... you add just properties for anything that is required. What types of properties you would need would really depend on how you have structured your domain objects.
If your view has the requirements that you mentioned in your question and you modelled your domain objects closely on the objects that they represent, then by the sounds of it, you would just need an Account object because that would contain the Preference and Profile objects inside it.
Finally, the only 'mapping' that needs to be done can be done with a LinQ query using the Entity Framework. It is at this point that I join the tables and pull whatever data that I need for whichever object I am working on. Here is an example of instantiating objects from data from three tables (using LinQ2SQL):
public AudioTracks GetAudioTracks(AudioTrackSearchOptions searchOptions)
{
AudioTracks audioTracks;
using (MidasDataContext dataContext = DataContext)
{
audioTracks = new AudioTracks(
from audioTrack in dataContext.DbAudioTracks
join masterTrack in dataContext.DbMasterTracks on audioTrack.MasterTrackId equals masterTrack.Id
join masterTrackArtist in dataContext.DbDataLists on masterTrack.ArtistId equals masterTrackArtist.Id
orderby string.Concat(masterTrack.Title, " (", audioTrack.Mix, ") - ", masterTrackArtist.Text)
where (searchOptions.IsInactiveAudioTrackIncluded || audioTrack.IsActive)
&& (searchOptions.IsDeletedAudioTrackIncluded || !audioTrack.IsDeleted)
select new AudioTrack(audioTrack.Id, masterTrack.Id, audioTrack.Isrc, masterTrack.Title, masterTrackArtist.Text, audioTrack.Mix, audioTrack.IsContentExplicit, audioTrack.IsActive, audioTrack.IsDeleted));
}
audioTracks.Sort(a => a.TitleWithMix);
return audioTracks ?? new AudioTracks();
}
UPDATE >>>
Extending my AudioTracks example and working backwards, the GetAudioTracks method is in a project called DataProviders. It is called from a GetAudioTracks method in a DataController class which just adds user feedback and re-try options. That in turn is called by a TracksModel in the Models project which just contains a subsection of methods from the DataController class that relate to the various types of tracks in the application.
Finally, the AudioTracksViewModel in the ViewModels project calls the TracksModel.GetAudioTracks method upon initialisation which happens when the AudioTracksView is loaded by the user. The AudioTracksView has a ListBox on the left containing all of the AudioTrack objects that meet the users search and/or filter selections. The right of the screen has the fields for the selected AudioTrack. Here is what it looks like (if the link seems broken, you can view the image here):
The more transparent fields with an edit Button on the right are read only fields connected to collections. The edit Button opens a dialog to let the user enter multiple items, which are then summarised in the field. All of the objects in the application have similar views of more or less complexity.

NHibernate - Lazy-Loading primitive type

I'm using NHibernate to load some objects into my ASP.NET MVC application.
For example, a submission is submitted by a user, and I want to display the username (but only the username) of a user, like this:
<%= Html.Encode(item.User.UserName) %>
When I load a submission using NHibernate, the User is lazy-loaded from the database, which means that the actual SQL query (to load user information) will only be generated and executed when I call the above line of code (which is what I want).
The problem is that the SQL query also selects other information about the user, like it's password, email, etc. This information is obviously not needed and is discarded.
The SQL query looks like this :
SELECT id, username, password, email FROM User WHERE Id = 1;
I conclude that NHibernate only lazy-load references to other objects that are mapped to tables in my database. It does not seem to lazy-load basic, primitive types like strings, ints, etc.
Can I do that? When the above line a code is selected, I would like the SQL query look something like this:
SELECT username FROM User WHERE Id = 1;
Is this possible? How?
Is there a reason you don't want to load the complete object? Except in rare cases, there's no real performance difference.
I can understand not wanting to load the password into memory, although it should be encrypted and probably shouldn't be in your domain model anyway. What I would do in your case is subclass User into two classes, User and UserProfile (containing the password, etc.), so that you only work with the UserProfile object when you are managing the user account.
You'll need to use a resultTransformer to map the query result to the object you want to store it (which I understand is your normal User DTO class but minus all the other fields)
Check this out http://docs.jboss.org/hibernate/stable/core/reference/en/html/querysql.html (its Hibernate, not NHibernate, but it should still apply) specifically section 16.1.5

Categories