Showing specific data based on the currently logged in user - sql-server

I have a question that deals with the logistics of returning rows of data in a SQL database (Entity Framework) based on the user that is logged in; I have mainly focused on desktop C# applications and while making the switch to ASP.NET MVC 4 I'm having a bit of difficulty when it comes to figuring this out (I've searched around and none of the answers seem to provide exactly what I'm looking for):
I would like to use the authorization built in to ASP.NET (MVC4), and allow users to post data about their websites (site category, url, age, etc.) with a form, and have the form store the data (using Entity Framework) to a database (called PrimaryDomainsDb) that is tied to their Id in the UserProfile table.
When the user clicks a button to show their list of domains, how can I make the application pull their list of domains (relevant rows of data) while ignoring other users rows?
Again, I'm mainly looking for the logistics and concepts (using foreign keys, for example) and psuedocode rather than actually spoonfeeding me a bunch of code.
If anyone has any best practice ideas (i.e. link the UserProfile to the PrimaryDomainDb this way, and use EF to call the rows matching their Id this way to return the rows to the View), it would be much appreciated.
Some sample code:
I currently have my PrimaryDomain code first set up like this (this doesn't have the decorators that specify min/max length, etc.):
public class PrimaryDomain
{
public virtual int Id { get; set; }
public virtual string SiteName { get; set; }
public virtual string SiteURL { get; set; }
public virtual SitePlatforms SitePlatform { get; set; }
public virtual decimal? SiteDA { get; set; }
public virtual decimal? SitePA { get; set; }
public virtual string SiteAge { get; set; }
public virtual DateTime? LastStatusUpdate { get; set; }
public virtual string SiteIP { get; set; }
}
And I have a User class that is different than the one provided by ASP.NET WebSecurity, that looks like this: (also, I know that "password" should not be in string formatting, this is just for initial set-up purposes - and password should probably be removed altogether and handled by WebSecurity, I think).
public class User
{
public virtual int Id { get; set; }
public virtual string Username { get; set; }
public virtual string Password { get; set; }
public virtual string Email { get; set; }
public virtual string FirstName { get; set; }
public virtual string LastName { get; set; }
public virtual string MozAccessID { get; set; }
public virtual string MozKey { get; set; }
public virtual decimal AccuountBalance { get; set; }
public virtual PrivateProxy PrivateProxies { get; set; }
public virtual PrimaryDomain PrimaryDomains { get; set; }
}
When pulling the data for Views I run everything through a repository using direct injection:
public interface IUserDataSource
{
IQueryable<User> Users { get; }
IQueryable<PrimaryDomain> PrimaryDomains { get; }
void Save();
}
This is my UserDb class, which is fed in whenever the code calls for an IUserDataSource (via direct injection):
public class UserDb : DbContext, IUserDataSource
{
public UserDb()
: base("DefaultConnection")
{
}
public DbSet<User> Users { get; set; }
public DbSet<PrimaryDomain> PrimaryDomains { get; set; }
IQueryable<User> IUserDataSource.Users
{
get { return Users; }
}
IQueryable<PrimaryDomain> IUserDataSource.PrimaryDomains
{
get { return PrimaryDomains; }
}
void IUserDataSource.Save()
{
SaveChanges();
}
}
And this is, for example, how I would pass the PrimaryDomains model to the View:
public class NetworkController : Controller
{
//
// GET: /Network/
private IUserDataSource _db;
public NetworkController(IUserDataSource db)
{
_db = db;
}
public ActionResult ListDomains()
{
var allDomains = _db.PrimaryDomains;
return View(allDomains);
}
}
But instead of pulling the entire PrimaryDomains list from the data source, I would like to add a way to reference the currently logged in user id to make the application only show the domains for that specific user, not all domains, and when adding a new domain via the form to reference the User Id and add it into the table as well.

My original question may have caused some confusion as to what I'm trying to achieve; It's my fault for posing the wrong way of going about what I'm trying to do. After much research and learning, I've found that exactly what I'm looking for is a multi-tenant data architecture approach.

This is probably what you are looking for. If I understood you correctly you want to use WebSecurity to login or register users but you want to use entity framework to store some user-specific data. Code below connects WebSecurity tables with your database CodeFirst created using EntityFramework.
You create class below (from tutorial).
[AttributeUsage(AttributeTargets.Class | AttributeTargets.Method, AllowMultiple = false, Inherited = true)]
public sealed class InitializeSimpleMembershipAttribute : ActionFilterAttribute
{
private static SimpleMembershipInitializer _initializer;
private static object _initializerLock = new object();
private static bool _isInitialized;
public override void OnActionExecuting(ActionExecutingContext filterContext)
{
// Ensure ASP.NET Simple Membership is initialized only once per app start
LazyInitializer.EnsureInitialized(ref _initializer, ref _isInitialized, ref _initializerLock);
}
private class SimpleMembershipInitializer
{
public SimpleMembershipInitializer()
{
try
{
if(!WebSecurity.Initialized)
WebSecurity.InitializeDatabaseConnection("ConnectionString", "DbUsers", "UserId", "Email", autoCreateTables: true);
}
catch (Exception ex)
{
throw new InvalidOperationException("The ASP.NET Simple Membership database could not be initialized. For more information, please see http://go.microsoft.com/fwlink/?LinkId=256588", ex);
}
}
}
}
It creates necessary tables for registering and logging your users. The magic is in second, third and fourth parameter. It is respectively table, userId column and userName column from YOUR database that you can create by EntityFramework. WebSecurity uses that table along with other self-generated tables to manage your users and let them register, login and so on.
Then in your code first you simply create table
public class DbUser
{
[Key]
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public int UserId { get; set; }
[MaxLength(40)]
public string Email { get; set; }
[MinLength(3)]
[MaxLength(30)]
[Required]
public string FirstName { get; set; }
[MinLength(3)]
[MaxLength(50)]
[Required]
public string LastName { get; set; }
}
Then you can simply query data from controller. In example below I use UserId stored by WebSecurity membership to retrieve account info from database.
public ActionResult AccountInfo()
{
if (FormsAuthentication.CookiesSupported == true && Request.Cookies[FormsAuthentication.FormsCookieName] != null)
{
var userId = WebSecurity.CurrentUserId;
var userInfo = context.Users.FirstOrDefault(x => x.UserId == userId);
userInfo.Password = "";
return View(userInfo);
}
else
{
ModelState.AddModelError("", "Wystąpił bląd autoryzacji, zaloguj się jeszcze raz.");
return RedirectToAction("Login", "Account");
}
}
EDIT:
Regarding your edited question as I understand besides the fact that you need to integrate WebSecurity with EF as above (I also forgot to mention that after creating InitializeSimpleMmebershipAttribute class as above you need to decorate your controller with that attribute) you also have problems with implementing generic repository. If that line is a problem:
var allDomains = _db.PrimaryDomains;
Then i suggest to read this article about implementing generic repository:
http://www.asp.net/mvc/tutorials/getting-started-with-ef-using-mvc/implementing-the-repository-and-unit-of-work-patterns-in-an-asp-net-mvc-application
If you want thing realy simple all you need is just add to your interface method
GetDomainByUserId(int userId)
and just implement that interface like that:
public class UserDb : DbContext, IUserDataSource
{
public UserDb()
: base("DefaultConnection")
{
}
public DbSet<User> Users { get; set; }
public DbSet<PrimaryDomain> PrimaryDomains { get; set; }
IQueryable<User> IUserDataSource.Users
{
get { return Users; }
}
IQueryable<PrimaryDomain> IUserDataSource.PrimaryDomains
{
get { return PrimaryDomains; }
}
IQueryable<PrimaryDomain> GetDomainByUserId(int userId)
{
return PrimaryDomains.Where(x => x.Id == userId).ToQueryable();
}
void IUserDataSource.Save()
{
SaveChanges();
}
}
But this is very bad approach and I strongly recommend reading that article.

Related

Null data returned using a 1:1 model

I have two SQL tables, User and UserType joined with UserType as a foreign key, with their respective models in ASP. To my understanding, this should be a 1:1 relationship (correct me if I'm wrong). One unique user, set as a type of user (being admin, super admin, user etc).
When I try and retrieve a list of users, it returns a null on the property UserType.
I used Google to get this far, but I'm struggling to get this particular issue fixed.
At one point I got an error stating: "Unable to determine the principal end of an association". To get around that, I included a Required annotation (didn't work) and a ForeignKey annotation (didn't work either) in both models, both simultaneously and separately.
This is what I have so far.
[Table("Users", Schema = "dbo")]
public class Users
{
[Key, ForeignKey("UserType")]
public Guid Id { get; set; }
public string FirstName { get; set; }
public string LastName { get; set; }
public string ContactNumber { get; set; }
public UserType UserType { get; set; }
public string IsActive { get; set; }
}
[Table("UserType", Schema = "dbo")]
public class UserType
{
[Key]
public Guid Id { get; set; }
public string Type { get; set; }
public string Description { get; set; }
public string IsActive { get; set; }
public Users Users { get; set; }
}
I'm using the below LINQ method to retrieve the data:
public PagedTables<Users> GetAllUsers(Pagination pagination)
{
using (var db = new DbContext())
{
var user = new PagedTables<Users>()
{
Data = db.Users.OrderBy(U => U.Id).Skip(pagination.Page).Take(pagination.Limit).ToList(),
Count = db.Users.Count()
};
return user;
}
}
A break point on the users var shows that the property UserType returns null. I would expect the assigned user type to be joined onto the user.
Any help would be appreciated. Thanks
My EF background is database-first but if you are eager loading (i.e. not lazy loading) then are you missing an Include to tell LINQ to go and get the UserType? Something like;
Data = db.Users.OrderBy(U => U.Id).Skip(pagination.Page).Take(pagination.Limit).Include(U => U.UserType).ToList(),

include only returning one row and not closing json

I'm having difficulty with my .NET Core Api 2.1.
I set up a database in SQL Server 2017 and created my tables with all the proper conventions, FK's, PK's and so on.
The tables are structured as follows:
Contacts:
namespace ContactsApi
{
public partial class Contacts
{
public Contacts()
{
Addresses = new List<Addresses>();
Emails = new List<Emails>();
Numbers = new List<Numbers>();
}
public int Id { get; set; }
public string FirstName { get; set; }
public string LastName { get; set; }
public string Company { get; set; }
public string ProfileImage { get; set; }
public DateTime? Birthday { get; set; }
public string Notes { get; set; }
public List<Addresses> Addresses { get; set; }
public List<Emails> Emails { get; set; }
public List<Numbers> Numbers { get; set; }
}
}
Addresses:
namespace ContactsApi
{
public partial class Addresses
{
public int Id { get; set; }
public string AddressLine1 { get; set; }
public string AddressLine2 { get; set; }
public string AddressLine3 { get; set; }
public string CityRegion { get; set; }
public string StateProvince { get; set; }
public string ZipPostalCode { get; set; }
public string Country { get; set; }
public string Category { get; set; }
public int ContactId { get; set; }
public Contacts Contact { get; set; }
}
}
Numbers:
public partial class Numbers
{
public int Id { get; set; }
public string Category { get; set; }
public string PhoneNumber { get; set; }
public int ContactId { get; set; }
public Contacts Contact { get; set; }
}
Emails
public partial class Emails
{
public int Id { get; set; }
public string Email { get; set; }
public string Category { get; set; }
public int ContactId { get; set; }
public Contacts Contact { get; set; }
}
All entities have a many to one relationship except for one, which has a one to many relationship.
I then scaffolded my project using Entity Framework Core 2.1.
When initially testing, I did not .Include() my Addresses, Numbers, and Emails. The contacts returned just fine until I tried to .Include() the other properties and my data was truncated at the first data row without closing the JSON. This happens no matter what I .Include().
I also get back the following error message in Chrome
Failed to load resource: net::ERR_SPDY_PROTOCOL_ERROR
Here's my original project, which works except id does not return the relational tables:
[HttpGet]
public IActionResult GetContact()
{
var joinedContacts = _context.Contacts;
return new ObjectResult(joinedContacts) { StatusCode = 200 };
}
It returns this JSON:
[{"id":1,"firstName":"Ryan","lastName":"Peterson","company":"RK Peterson Media","profileImage":"ryanpeterson.jpg","birthday":"2018-06-24T00:00:00","notes":"Technically sophisticated Full Stack Web Developer with solid history of innovative solutions for a wide range of clients and businesses. Demonstrated success in web and application development with proficiency in front end, back end, UI/UX, coding, software, and application design. Excel at SEO-based web design, Google Analytics, PCI compliance, project management, and full life cycle software development (SDLC). Skilled trainer and project leader; able to concurrently lead the creation and launch of various websites for a diverse clientele.","addresses":[],"emails":[],"numbers":[]},{"id":2,"firstName":"Walter","lastName":"White","company":"Grey Matter","profileImage":"noimage.jpg","birthday":"2018-06-24T00:00:00","notes":"Seems a lot more irritable since the diagnosis... who can blame him trying to feed a family on a teachers salary?","addresses":[],"emails":[],"numbers":[]},{"id":3,"firstName":"Alejandro","lastName":"Rose-Garcia","company":"Pan American Drums","profileImage":"noimage.jpg","birthday":"2018-06-24T00:00:00","notes":"This guy!","addresses":[],"emails":[],"numbers":[]},{"id":4,"firstName":"Justin","lastName":"Trudeau","company":"Canada","profileImage":"noimage.jpg","birthday":"2018-06-24T00:00:00","notes":"Can explain quantum physics! Not clear on his policy, but at least he''s super cool!","addresses":[],"emails":[],"numbers":[]}]
Here's my include statement:
[HttpGet]
public IActionResult GetContact()
{
var joinedContacts = _context.Contacts
.Include(a => a.Emails)
.Include(a => a.Addresses)
.Include(a => a.Numbers)
.ToList();
// add Count of results for validation
if (joinedContacts.Count > 0)
return new ObjectResult(joinedContacts) { StatusCode = 200 };
else
return new ObjectResult(null) { StatusCode = 404 };
}
This call returns:
[{"id":1,"firstName":"Ryan","lastName":"Peterson","company":"RK Peterson Media","profileImage":"ryanpeterson.jpg","birthday":"2018-06-24T00:00:00","notes":"Technically sophisticated Full Stack Web Developer with solid history of innovative solutions for a wide range of clients and businesses. Demonstrated success in web and application development with proficiency in front end, back end, UI/UX, coding, software, and application design. Excel at SEO-based web design, Google Analytics, PCI compliance, project management, and full life cycle software development (SDLC). Skilled trainer and project leader; able to concurrently lead the creation and launch of various websites for a diverse clientele.","addresses":[{"id":3,"addressLine1":"223 W Jackson Blvd","addressLine2":null,"addressLine3":null,"cityRegion":"Chicago","stateProvince":"IL","zipPostalCode":"60606-6908","country":"United States","category":"Home","contactId":1
Anyone have an idea why the data is getting truncated? Are there limits I'm unaware of?
Thank you!
Here's a link where I found this nugget. NOTE: there's a very complete description about what happens.
var jsonSerializerSettings = new JsonSerializerSettings
{
PreserveReferencesHandling = PreserveReferencesHandling.Objects
};
GlobalConfiguration.Configuration.Formatters.Clear();
GlobalConfiguration.Configuration.Formatters.Add(new JsonNetFormatter(jsonSerializerSettings));

Json parsing in MVC 4 web api with angularjs

public partial class User
{
public System.Guid UserId { get; set; }
public Nullable<System.Guid> RoleId { get; set; }
public Nullable<long> MembershipNo { get; set; }
public string Username { get; set; }
public string Password { get; set; }
public string FirstName { get; set; }
public string LastName { get; set; }
public string Gender { get; set; }
public string Emaiil { get; set; }
public Nullable<decimal> MobileNo { get; set; }
public string Description { get; set; }
public Nullable<System.Guid> ModifiedBy { get; set; }
public Nullable<System.DateTime> ModifiedDate { get; set; }
public virtual Role Role { get; set; }
}
This is my table in DB named Users which is associated with Roles table of DB (as you can see last virtual row at the end above)
Now My problem is simple. I'm using angulars $http.get() method to call my Web Api in MVC 4. When i call it, it gets connected and fetches desired record but it doesn't throw proper result back to .js file or controller.
At .js side I run into error. Every time, it executes .error(jsonResult,config,header,status) .
When I jump on to JsonResult, it shows me below error.
Object
ExceptionMessage: "The 'ObjectContent`1' type failed to serialize the response body for content type 'application/json; charset=utf-8'."
ExceptionType: "System.InvalidOperationException"
InnerException: Object
ExceptionMessage: "Self referencing loop detected for property 'Role' with type
'System.Data.Entity.DynamicProxies.Role_82CA96EA045B1EB47E58B8FFD4472D86502EEA79837B4AE3AD705442F6236E58'.
Path 'Role.Users[0]'."
ExceptionType: "Newtonsoft.Json.JsonSerializationException"
Message: "An error has occurred."
I don't know what's wrong here. Is it json parsing error or something? if so, I've heard and read the articles that webapi in .net handles or throws json itself.
My call happens through
$http.get(apiUrl).success(function (jsonResult, header, config, status) {
debugger;
var number = parseInt(jsonResult.membershipNo) + 1;
$scope.membershipNo = "M" + number;
})
.error(function (jsonResult, header, config, status) {
debugger;
toastr.error('Something went wrong ! Contact Administrator!!!');
});
Edited:
One more thing to mention, .CS side when I fetch single cell value (from DB/table) , it gets returned back to .success() call but when i fetch particular row or all rows, it gets returned to .error() call. I'm using entity frameworkd 6.1.1. and above class is generated by EF-6.1.1.
public partial class Role
{
public Role()
{
this.Permissions = new List<Permission>();
this.Users = new List<User>();
}
public System.Guid RoleId { get; set; }
public string RoleName { get; set; }
public string Description { get; set; }
public Nullable<System.Guid> ModifiedBy { get; set; }
public Nullable<System.DateTime> ModifiedDate { get; set; }
public virtual ICollection<Permission> Permissions { get; set; }
public virtual ICollection<User> Users { get; set; }
}
Hi you can solve that in 2 easy steps
First Step: Create globalConfig class where you can set ignoring ReferenceLoopHandling (http://james.newtonking.com/json/help/index.html?topic=html/SerializationSettings.htm) and if you crating js app you can set as well to remove xml formaters and always get return from Webapi as JSON string is usefull for debugging. So in your app_start folder add class GlobalConfig like below:
public class GlobalConfig
{
public static void CustomizeConfig(HttpConfiguration config)
{
// Remove Xml formatters. This means when we visit an endpoint from a browser,
// Instead of returning Xml, it will return Json.
//that is optional
config.Formatters.Remove(config.Formatters.XmlFormatter);
GlobalConfiguration.Configuration.Formatters.JsonFormatter.SerializerSettings.ReferenceLoopHandling =
Newtonsoft.Json.ReferenceLoopHandling.Ignore;
}
}
Second Step: In Global.asax set your custom configuration to do that please add code below to method Application_Start():
GlobalConfig.CustomizeConfig(GlobalConfiguration.Configuration);
it sounds like:
the problem is that EF is using lazy loading that is not materialized in time of constructing this, on role. EF from early version has switched lazy loading on by default.
Suggested solution
Create subset of you user class, with the parts that you really need.
=> Its bad practise to fetch too much data that you are not gonna need.

Why do Entity Framework classes need a virtual member of an unrelated class

Easier to show by example -- I'm using code-first to construct a database. I have the following classes:
public class Blog
{
public int Id { get; set; }
public string Title { get; set; }
public string AuthorName { get; set; }
public List<Post> Posts { get; set; }
public string BlogCode
{
get
{
return Title.Substring(0, 1) + ":" + AuthorName.Substring(0, 1);
}
}
}
public class Post
{
public int Id { get; set; }
public string Title { get; set; }
public string Content { get; set; }
public virtual Blog Blog { get; set; }
}
I don't understand why Post needs a public virtual Blog Blog. Does it act as a foreign key in the database to link back to the Blog? It seems like if that were the case you would use the Blog Id.
It does allow the two tables to be related and places a foreign key on Post relating to Blog.
Having public virtual Blog Blog { get; set; } allows you to reference the Blog object from a Post object and then get access to all the properties of the Blog. E.g. myPost.Blog.Id If it used public virtual int BlogId { get; set; }, you would not be able to do this since BlogId would just be an int value.
If your domain objects are lazy loaded, myPost.Blog will not actually be hydrated with data from the database (i.e. no call to the Blog table) until that property is used. As soon as it is used, Entity Framework will make the database call for you and hydrate the object with data from the Blog table. This is part of the beauty of using an ORM... it allows you to focus on the code while it takes care of the database operations.

EF Code First: Many-to-many and one-to-many

This is probably just because my knowledge with the EF Code First fluent API is lacking, but I'm stumped.
I want to model the following:
A Groups collection with Id and Name
A Users collection with Id and Name
Each user is assigned to exactly one primary group
Each user may have zero or many secondary groups
The table structure I'm going for would look like:
Groups
Id
Name
Users
Id
Name
PrimaryGroupId
SecondaryGroupAssignments
UserId
GroupId
I've been beating my head against a wall trying to model this with EF Code First, but I can't get it to accept both relationships between User and Group. Sorry for not posting any .NET code (I'm happy to), but it's probably all wrong anyway.
Is there a way to make EF model this? I'm assuming I have to do some sort of configuration with the Fluent API. Maybe a better question is: is there any good, definitive reference for the Fluent API?
Thanks!
Try this (untested):
public class Group
{
public int Id { get; set; }
public string Name { get; set; }
public virtual ICollection<User> PrimaryUsers { get; set; }
public virtual ICollection<User> SecondaryUsers { get; set; }
}
public class User
{
public int Id { get; set; }
public string Name { get; set; }
public int PrimaryGroupId { get; set; }
public virtual Group PrimaryGroup { get; set; }
public virtual ICollection<Group> SecondaryGroups { get; set; }
}
public class Context : DbContext
{
public DbSet<User> Users { get; set; }
public DbSet<Group> Groups { get; set; }
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
base.OnModelCreating(modelBuilder);
modelBuilder.Entity<User>()
.HasRequired(u => u.PrimaryGroup)
.WithMany(g => g.PrimaryUsers)
.HasForeignKey(u => u.PrimaryGroupId)
.WillCascadeOnDelete(false);
modelBuilder.Entity<User>()
.HasMany(u => u.SecondaryGroups)
.WithMany(g => g.SecondaryUsers)
.Map(m => m.MapLeftKey("UserId")
.MapRightKey("GroupId")
.ToTable("SecondaryGroupAssignments"));
}
}
Based on Ladislav's excellent answer, here's how to do it without using any mappings - just attributes applied to the Model classes themselves:
public class Group
{
public int Id { get; set; }
[MaxLength(300)]
public string Name { get; set; }
public ICollection<User> Users { get; set; }
}
public class User
{
public int Id { get; set; }
[MaxLength(300)]
public string Name { get; set; }
[ForeignKey("PrimaryGroup")]
public int PrimaryGroupId { get; set; }
[Required]
public Group PrimaryGroup { get; set; }
[InverseProperty("Users")]
public ICollection<Group> SecondaryGroups { get; set; }
}
Notes
If you want, you can add the virtual keyword to the 2 ICollections and the Group. This allows lazy-loading. Performance-wise, I don't recommend it, but it is possible.
I included MaxLength attributes with an arbitrary (but safe) length of 300, because putting strings out in EF without a MaxLength gets you low-performance NVarChar(MAX) columns. Totally irrelevant to what's being asked but better to post good code.
I recommend against class names "User" and "Group" for your EF classes. They're going to complicate any SQL you attempt to run later, having to type [User] and [Group] to access them, and complicate using these classes in MVC Controllers where your class User will conflict with the Context property User that gives you access to the Asp.Net Identity library.

Resources