I'm using the SimpleMembership.MVC3 package with my MVC3 application and I want to be able to access users from the table through Entity Framework
In examples for doing this with MVC4, you can simply create a POCO to mirror the User table that's been generated, add your DbSet in your DbContext implementation and then query the DbSet like you normally would, ie: context.Users.
This collection is always returning 0 items for me even though there are rows in the table. What am I doing wrong? Here's what I got so far:
[Table("User")]
public class User
{
public int Id { get; set; }
public string UserName { get; set; }
public string FirstName { get; set; }
public string LastName { get; set; }
}
public class TestContext : DbContext
{
public DbSet<User> Users { get; set; }
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
modelBuilder.Conventions.Remove<PluralizingTableNameConvention>();
base.OnModelCreating(modelBuilder);
}
}
In my service:
model.Accounts = context.Users.ToList();
Thanks!
You do not create your a POCO that mirrors the User table in order to access it directly from EF. There is already a POCO created by the Internet template when you created the project, which you can customize as described here. This same article also shows how you can access the user information by accessing EF directly. You do not create your own context, there is one already in place that you use. Here is a code snippet from that article.
var context = new UsersContext();
var username = User.Identity.Name;
var user = context.UserProfiles.SingleOrDefault(u => u.UserName == username);
var email = user.Email;
The article also has links to download the source code that demonstrates the details on how to do this.
I circumvented the membership classes entirely and implemented a pure EF membership system. I leveraged the System.Web.Helpers Crypto helpers to handle password hashing and just create the AuthCookie when needed.
Related
I'm having trouble implementing the many-to-many relationship using the Entity Framework Core 5 in Visual Studio.
I have the classes:
public class Medico
{
public Medico()
{
this.Especialidades = new HashSet<Especialidade>().ToList();
}
public int Id { get; set; }
public string Nome { get; set; }
public int CRM { get; set; }
public List<Especialidade>Especialidades { get; set; }
public class Especialidade
{
public int Id { get; set; }
public string Descricao { get; set; }
public IList<Medico>Medicos { get; set; }
}
And the Create method:
[HttpPost]
[ValidateAntiForgeryToken]
public async Task<IActionResult> Create([Bind("Id,Nome,CRM")] Medico medico)
{
var lstTags = Request.Form["chkTags"];
if (!string.IsNullOrEmpty(lstTags))
{
int[] splTags = lstTags.ToString().Split(',').Select(Int32.Parse).ToArray();
if (splTags.Count() > 0)
{
var medicoEspecialidades = await _context.Especialidades.Where(t => splTags.Contains(t.Id)).ToListAsync();
foreach (var me in medicoEspecialidades)
{
medico.Especialidades.Add(me);
}
}
}
if (ModelState.IsValid)
{
_context.Medicos.Add(medico);
await _context.SaveChangesAsync();
return RedirectToAction("Index");
}
return View(medico);
}
But when I run Create, it returns me with the following error:
"Cannot insert explicit value for identity column in table 'Especialidades' when IDENTITY_INSERT is set to OFF."
If I turn off the Identity_Insert of the Specialty table in the bank, it even inserts, but duplicates the records in the Specialty table.
I've been researching and trying to find a solution for 2 days now. Can someone who has been through this give me a hand?
The application source code is here: https://www.dropbox.com/s/xn6b95h7amfpuqa/AppCompleta%205.0.rar?dl=1
The approach looks Ok, though I would check to ensure that the medico being passed in does not have any Especialidade somehow coming in from the client as these would be detached entities. The error seems to imply that Medico may have a detached Especialidade in its collection. If the checked values represents everything that should be tracked, then this collection should be cleared and the Especialidade references added.
Do you have any explicit mapping configuration for either of these entities? If not I would highly recommend using one for Many-to-Many relationships as sometimes EF can default to unexpected schema assumptions when working off convention, especially in Code First if that is the case. I would look at your schema carefully to ensure it is matching what would be expected for a Many-To-Many. For example, what is the linking table name for Medico-Especialidade? Is there an entity defined for it in the configuration? This is entirely optional and EF should work it out, however if you do have explicit mapping that might not be configured correctly, tripping up the relationships.
One other detail giving off a smell:
public Medico()
{
this.Especialidades = new HashSet<Especialidade>().ToList();
}
public List<Especialidade>Especialidades { get; set; }
This should be:
public ICollection<Especialidade> { get; set; } = new HashSet<Especialidade>();
EF can work with lists, but when it comes to proxies and the behind the scenes EF is doing with entities it is generally better to declare your collection references as ICollection rather than concrete classes. ToListing a HashSet merely produces a List, so either = new HashSet<Especialidade>() or = new List<Especialidade>() will do. The difference would merely be the behaviour of the collection when you are populating it after "newing" up a Medico, or deserializing one.
I want to add 4 eyes principle to ASP.NET Boilerplate framework. That means every change on Role, User,.. need to be approved (by another admin) before applied to the system. I have searched for some time but no answer. So what is the best solution for this flow?
Can I create the same tables with Abp tables (dbo.AbpUser_Temp, etc) and the all the changes will be stored in these tables? Is there any better solution?
Example: In the application, Admin1 has created a user named User1. But this user cannot login to the application until he was approved by Admin2.
Simple Workflows
Example: In the application, Admin1 has created a user named User1. But this user cannot login to the application until he was approved by Admin2.
Simple workflows like these can be appropriately handled by a property and a method:
public class User : AbpUser<User>
{
public bool IsApproved { get; set; }
public void Approve(User approver)
{
if (approver.Id != CreatorUserId)
{
IsApproved = true;
}
}
}
Complex Workflows
Complex workflows like "every change" can do this instead of _Temp tables:
public abstract class ChangeBase : Entity<long>, IExtendableObject
{
public string EntityTypeAssemblyQualifiedName { get; set; }
public string EntityIdJsonString { get; set; }
public long ProposerUserId { get; set; }
public long? ApproverUserId { get; set; }
public string ExtensionData { get; set; }
}
public class Change : ChangeBase
{
[NotMapped]
public Type EntityType => Type.GetType(EntityTypeAssemblyQualifiedName);
[NotMapped]
public object EntityId => JsonConvert.DeserializeObject(EntityIdJsonString, EntityHelper.GetPrimaryKeyType(EntityType));
[NotMapped]
public bool IsApproved => ApproverUserId.HasValue && ApproverUserId != ProposerUserId;
[NotMapped]
public IDictionary<string, string> ChangedPropertyValuePairs => JObject.Parse(ExtensionData).ToObject<Dictionary<string, string>>();
public Change(EntityIdentifier changedEntityIdentifier, long proposerUserId, IDictionary<string, string> changedPropertyValuePairs)
{
EntityTypeAssemblyQualifiedName = changedEntityIdentifier.Type.AssemblyQualifiedName;
EntityIdJsonString = changedEntityIdentifier.Id.ToJsonString();
ProposerUserId = proposerUserId;
ExtensionData = JObject.FromObject(changedPropertyValuePairs).ToString(Formatting.None);
}
public bool Approve(long approverUserId)
{
if (approverUserId != ProposerUserId)
{
ApproverUserId = approverUserId;
return true;
}
return false;
}
}
Usage:
public class UserAppService // ...
{
private readonly IRepository<Change, long> _changeRepository;
public UserAppService(
IRepository<User, long> repository,
IRepository<Change, long> changeRepository) // : base(repository)
{
_changeRepository = changeRepository;
}
public void ChangeUserName(long userId, string newUserName)
{
// Validation, etc.
var changedPropertyValuePairs = new Dictionary<string, string> {
{ nameof(User.UserName), newUserName }
};
var change = new Change(
new EntityIdentifier(typeof(User), userId),
AbpSession.GetUserId(),
changedPropertyValuePairs
);
_changeRepository.Insert(change);
}
public void ApproveChange(long changeId)
{
// Validation, etc.
var change = _changeRepository.Get(changeId);
if (change.EntityType == typeof(User) && change.Approve(AbpSession.GetUserId()))
{
var user = Repository.Get((long)change.EntityId);
var changedPropertyValuePairs = change.ChangedPropertyValuePairs;
foreach (var changedProperty in changedPropertyValuePairs.Keys)
{
switch (changedProperty)
{
case nameof(User.UserName):
user.UserName = changedPropertyValuePairs[changedProperty];
break;
// ...
default:
break;
}
}
}
}
For development
Separate staging and production environments. Develop on one box, test it, get it reviewed and then deploy to a production box. Simple, effective and language agnostic advice.
Since ASP.NET Boilerplate framework included Entity Framework. You could also leverage migrations.
After you do your development work, and requires you to "update-database", then your SOP should be to have the admin review the (relatively simple) migrations that will be committed.
I hope that helps.
For application flow
There are probably quite a few ways to actually implement this so I'll cover a simple one get your idea's flowing, but keep in mind: The way you need to implement two person integrity must fit how your operating procedures should work, and not the other way around. Development doesn't drive business operations, business use-cases drive development.
Extending existing Identity* classes. Example: The ApplicationUser class (it may be named differently, but it derives from IdentityUser
Create 2 flags (boolean fields) that must be, and can only be turned 'on' by an administrator
a single administrator can only turn on 1 flag. (Which means you also have to store which administrator turned on which flag.)
The flags can be stored in the existing Abp* tables, or you can create a new table
Add logic so that the user is not allowed to log in unless those 2 flags are both on.
Example: default IdentityUserRole has identified and registered, but can not log in. Once both admin's switch the flags on, elevate the users IdentityUserRole to a role that is allowed to log in.
Sorry for the big title.
So I've been doing some research for making login systems. I've already made my own, but discovered a more secure way to do it.
As far as I know, the four basic components of this login system are:
FormsAuthentication
MembershipProvider
RoleProvider
Principal
I have this as my basic user model:
public class User
{
public int ID { get; set; }
public string Name { get; set; }
public string Password { get; set; }
public string PictureUrl { get; set; }
public string Role { get; set; }
public string AccessToken { get; set; }
}
This is just for retaining the data from the database.
I still want to use this model with the components listed above.
Does anyone know a good and thorough tutorial that explains how to create a custom login system using the above components in MVC 3 Razor?
Thanks in advance.
You can build a custom 'login system' by implementing a custom version of MembershipProvider and RoleProvider that uses your own database. Then you can re-use all the rest of the built in authentication and authorization stuff.
MSDN has some details on how to build a MembershipProvider here and details on a custom RoleProvider here. Samples implementations are included.
You can use this open source project :
https://github.com/TroyGoode/MembershipStarterKit
I am trying to implement a foreign key connection between the built-in User model and my models in ASP.NET MVC 3.
How to assign ownership or some other roles to various entries represented with my models. Example of how my models look like:
public class Entry
{
public int Id { get; set; }
public string Value { get; set; }
public User Owner { get; set; }
public User SomeoneElse { get; set; }
}
Where to find the model for users, what do I need to import? Or is there a better approach to accomplish this?
Do you use Entity Framework ?? If so...
Simple solution
You could simply keep the Guid from the Built-In User model. You won't have a "real relationship" but it will do the trick for what you want to do. You can always get the UserId with Membership.GetUser().ProviderUserKey
Other more complex
Completely rewrite and override the MembershipProvider and login module. That way you can use your own User object and add other properties to it aswell.
Not Sure about this one
Not sure if this one will work with the auto generated tables from the MembershipProvider but you can add the Foreign Key Property this way:
[ForeignKey("User")]
public Guid UserId { get; set; }
In an effort to further abstract my repository layer I have attempted to follow the code-first approach as described here: http://msdn.microsoft.com/en-us/magazine/ee236639.aspx.
I have a many-to-many relationship between Account and Subscription entities. A Navigation property exists on each entity pointing to the other (e.g. Account.Subscriptions).
Before I created my own model I was using the Entity generated model and the below worked fine ("db" is the entity context) :
public IQueryable<Account> GetBySubscriptionId(int subId)
{
return from a in db.Accounts
where a.Subscriptions.Any(s => s.SubscriptionId == subId)
select a;
}
Now the model for Account looks like this:
public class Account
{
public int AccountId { get; set; }
public string Name { get; set; }
// nav properties
public virtual List<Subscription> Subscriptions { get; set; }
}
And when I try to run the same LINQ query now I get this error:
"The specified type member
'Subscriptions' is not supported in
LINQ to Entities. Only initializers,
entity members, and entity navigation
properties are supported."
Any suggestions greatly appreciated.
Try changing the signature from
// nav properties
public virtual List<Subscription> Subscriptions { get; set; }
to
// nav properties
public virtual ICollection<Subscription> Subscriptions { get; set; }
Shamelessly nicked from Scott Hanselmann's demo here - http://www.hanselman.com/blog/SimpleCodeFirstWithEntityFramework4MagicUnicornFeatureCTP4.aspx which uses this pattern, also here's a Scott Guthrie demo using the same idea http://weblogs.asp.net/scottgu/archive/2010/07/23/entity-framework-4-code-first-custom-database-schema-mapping.aspx .
List<T> is a concrete implementation of various interfaces (ICollection, IQueryable, IEnumerable etc), Entity Framework uses proxy objects when it retrieves things from the database, hence the virtual declarations, which use different implementations of these interfaces which is where your error is coming from.