I need help with accessing class properties within a given class.
For example, take the below class:
public partial class Account
{
private Profile _profile;
private Email _email;
private HostInfo _hostInfo;
public Profile Profile
{
get { return _profile; }
set { _profile = value; }
}
public Email Email
{
get { return _email; }
set { _email = value; }
}
public HostInfo HostInfo
{
get { return _hostInfo; }
set { _hostInfo = value; }
}
In the class "Account" exists a bunch of class properties such as Email or Profile.
Now, when I want to access those properties at run-time, I do something like this
(for Email):
_accountRepository = ObjectFactory.GetInstance<IAccountRepository>();
string username = Cryptography.Decrypt(_webContext.UserNameToVerify, "verify");
Account account = _accountRepository.GetAccountByUserName(username);
if(account != null)
{
account.Email.IsConfirmed = true;
But, I get "Object reference not set..." for account.Email... Why is that?
How do I access Account such that account.Email, account.Profile, and so on
returns the correct data for a given AccountId or UserName.
Here is a method that returns Account:
public Account GetAccountByUserName(string userName)
{
Account account = null;
using (MyDataContext dc = _conn.GetContext())
{
try
{
account = (from a in dc.Accounts
where a.UserName == userName
select a).FirstOrDefault();
}
catch
{
//oops
}
}
return account;
}
The above works but when I try:
account = (from a in dc.Accounts
join em in dc.Emails on a.AccountId equals em.AccountId
join p in dc.Profiles on em.AccountId equals p.AccountId
where a.UserName == userName
select a).FirstOrDefault();
I am still getting object reference exceptions for my Email and Profile
properties. Is this simply a SQL problem or is there something else I need to be
doing to be able to fully access all the properties within my Account class?
Thanks!
Your getting this because Email is another class which has not been assigned yet. What you can do is in your constructor default the properties that link to other classes as new items. For example in your ctor:
public Account()
{
// Set Defaults
Email = new Email();
Profile = new Profile();
HostInfo = new HostInfo();
}
Then you can set their values as desired.
This looks like a case of handling null values on your properties. You should initialize the Email property to something other than null if you expect to store or query against it, or alter the queries so that they can expect to deal with null values. Also if you get a null value from the database, and your property cannot be set to null, the reverse problem occurs.
Are you declaring these properties yourself, or are you trying to indicate something like auto-generated code from like Linq-to-SQL?
If this is auto-generated where the Account table references the Email table, etc., then you probably just need to specify that you want those objects to load as well in the load options:
using (MyDataContext dc = _conn.GetContext())
{
var options = new DataLoadOptions();
options.LoadWith<Account>(a => a.Email);
options.LoadWith<Account>(a => a.Profile);
options.LoadWith<Account>(a => a.HostInfo);
dc.LoadOptions = options;
try
{
account = (from a in dc.Accounts
where a.UserName == userName
select a).FirstOrDefault();
}
catch
{
//oops
}
}
Just wanted to add: there is now a shorter form for declaring trivial properties:
public Profile Profile { get; set; }
public Email Email { get; set; }
public HostInfo HostInfo { get; set; }
Related
I made a custom selector that only displays the customers of the current user but when I select a customer I get the error: 'Customer' Cannot be found in the system.
The code for the Custom selector and how I implemented it on the DAC:
[PXNonInstantiatedExtension]
public class SO_SOOrder_ExistingColumn : PXCacheExtension<PX.Objects.SO.SOOrder>
{
#region CustomerID
[PXMergeAttributes(Method = MergeMethod.Merge)]
[PXForeignReference(typeof(Field<SOOrder.customerID>.IsRelatedTo<BAccount.bAccountID>))]
[SalesRepCustomer]
public int? CustomerID { get; set; }
#endregion
}
public class SalesRepCustomer : PXCustomSelectorAttribute
{
public SalesRepCustomer() : base(typeof(Customer.acctCD))
{
this.DescriptionField = typeof(Customer.acctCD);
}
protected virtual IEnumerable GetRecords()
{
foreach (Customer pc in PXSelect<Customer>.Select(this._Graph))
{
//Getting Current UserID
var cache1 = _Graph.Caches[BqlCommand.GetItemType(typeof(AccessInfo.userName))];
AccessInfo currentCacheObjecta = (AccessInfo)cache1.Current;
var userName = currentCacheObjecta.UserName;
SalesPerson person = PXSelect<SalesPerson, Where<SalesPerson.descr, Equal<Required<AccessInfo.userName>>>>.Select(_Graph, userName);
if (person != null)
{
CustSalesPeople custSalesPeople = PXSelect<CustSalesPeople, Where<CustSalesPeople.salesPersonID, Equal<Required<SalesPerson.salesPersonID>>, And<CustSalesPeople.bAccountID, Equal<Required<CustSalesPeople.bAccountID>>>>>.Select(_Graph, person.SalesPersonID, pc.BAccountID);
//return all customers related to this SalesPersonID in the CustSalesPeople table
if (!(custSalesPeople is null))
{
yield return pc;
}
}
else
{
//current user is not a sales person
//return all of the customers
yield return pc;
}
}
}
}
Screenshot of the selector on the Sales Order Screen:
Any help on this will be appreciated
You should have the constructor look something like
public SalesRepCustomer() : base(typeof(Customer.bAccountID))
{
this.DescriptionField = typeof(Customer.acctName);
this.SubstituteKey = typeof(Customer.acctCD);
}
The first type that you pass to the base constructor is the type of the value that will be used for the field.
In this case you want the customer id(an int) and you are currently using the AcctCD(a string) field. The description field would typically be the name of the customer account and the substitute key will make it so that the users see the AcctCD instead of the the customer ID(the actual value) which is just an integer.
I have a method that creates a new user and then insert a row in the User Permission table, but nothing is happening. Here is my code:
// before: creates user
var permission = new UserPermission()
{
UserId = user.Id,
UserName = user.UserName,
Assets = createUserModel.Assets
};
AccountDb.UserPermissions.Add(permission);
var saveChangesResult = AccountDb.SaveChanges();
if (saveChangesResult == 0) // the result is always 0
{
AppUserManager.Delete(user);
return BadRequest("User permission could not be saved");
}
// then: add user to role and return ok
SaveChanges always returns 0 and doesn't update the database, I've already googled for it and tried the following actions:
// no success
AccountDb.UserPermissions.Attach(permission);
// no success either
AccountDb.Entry(permission).State = EntityState.Added;
And I tried in async method too, but no luck.
Here is my "UserPermission" model:
public class UserPermission
{
public int Id { get; set; }
public string UserId { get; set; }
public string UserName { get; set; }
public string _Assets { get; set; }
[NotMapped]
public List<int> Assets
{
get
{
if (_Assets == null) return null;
return Array.ConvertAll(_Assets.Split(';'), Int32.Parse).ToList();
}
set
{
_Assets = String.Join(";", value.Select(x => x.ToString()).ToArray());
}
}
}
It's curious because this method was working lately, but after some changes in "AccountContext" and some rolebacks, I notice that the method wasn't working anymore.
-- edit --
here is the full create method
[HttpPost]
[Route("create")]
public IHttpActionResult CreateUser(CreateUserBindingModel createUserModel)
{
if (!ModelState.IsValid)
{
return BadRequest(ModelState);
}
if (createUserModel.RoleName is null)
{
return BadRequest("There is no role assigned to user");
}
var user = new UserModel()
{
UserName = createUserModel.UserName,
Email = createUserModel.Email,
FirstName = createUserModel.FirstName,
LastName = createUserModel.LastName
};
var addUserResult = AppUserManager.Create(user, createUserModel.Password);
if (!addUserResult.Succeeded)
{
return GetErrorResult(addUserResult);
}
var permission = new UserPermission()
{
UserId = user.Id,
UserName = user.UserName,
Assets = createUserModel.Assets
};
AccountDb.UserPermissions.Add(permission);
var saveChangesResult = AccountDb.SaveChanges();
if (saveChangesResult == 0)
{
AppUserManager.Delete(user);
return BadRequest("User permission could not be saved");
}
var addRoleResult = AppUserManager.AddToRole(user.Id, createUserModel.RoleName);
if (!addRoleResult.Succeeded)
{
AppUserManager.Delete(user);
return GetErrorResult(addUserResult);
}
return Ok(TheModelFactory.Create(user));
}
-- edit 2 --
I was using an Azure db string and I changed to a local db string, but the problem still is the same, UserPermissions Table does not update and no error is emiting.
You are creating a new user, but you never use the result. Your user.Id is always null (or the respective default).
I don't know why your database fails silently (constraints or foreign keys should throw an exception), but you should fix that first. It will probably result in real records being written.
I found a solution!
For some reason, my local instance of AccountContext was the problem, it gets all UserPermissions but doesn't update. So I created a new instance and tried to add the permission:
using (var db = new AccountContext())
{
db.UserPermissions.Add(permission);
var saveChangesResult = db.SaveChanges();
if (saveChangesResult == 0)
{
AppUserManager.Delete(user);
return BadRequest("User permission could not be saved");
}
}
now it's working well. But I don't know why the local AccountDb is not working now if it was working in the past
I created a class User, which contain simple variables as shown below:
public class User
{
public string username; //Unique usernames
public string password;
}
I then instantiate a list of an object in another class:
List<User> user = new List<User>();
user.Add(new User {username = "admin", password = "123"});
How is it possible for me to retrieve the password's value by searching for the username using a foreach loop? I am probably just confused but this is what I came up with:
foreach(var item in user)
{
if(item.Equals(username_input))
{
//I try to store the password into a string pass_check
pass_check = item.password;
}
}
if (user_input.Equals(pass_check))
{
Console.WriteLine("Login successful");
}
Sorry if this seems like a dense question to anyone out there, still a beginner trying to learn!
You're pretty close..
if(item.username.Equals(username_input))
You need to check the property of the item in this case which is username.
You could even shorten it to:
foreach(var item in user)
{
if(item.username.Equals(username_input)
&& user_input.Equals(item.password))
{
Console.WriteLine("Login successful");
break; // no need to check more.. or is there?
}
}
You can get really fancy using Linq:
if (user.Any(i => i.username.Equals(username_input)
&& user_input.Equals(i.password))
{
Console.WriteLine("Login successful");
}
As juharr noted in the commend, best practices for exposing values from class/objects is to use Properties not Fields..
public class User
{
// not best practices
public string username;
// best practices
public string password { get; set; }
}
Even fancier:
using System.Linq;
public static class Extensions
{
// Extension method that works on List<User> to validate user && PW
// - returns true if user exists and pw is ok, else false
public static bool CheckUserPassword(this List<User> users, string user_name, string pw)
{
// add null checks for users, user_name and pw if you are paranoid of know your customers ;o)
return users.Any(u => u.username == user_name && u.password == pw);
}
}
public class User
{
public string password;
public string username; //Unique usernames
}
internal class Program
{
private static List<User> users = new List<User> { new User { username = "admin", password = "123" } };
static void Main(string[] args)
{
// use extension method like this:
var isValid = users.CheckUserPassword("Hello","Jeha");
Console.WriteLine($"user 'admin' with pw '8888' => {users.CheckUserPassword("admin", "8888")}");
Console.WriteLine($"user 'admin' with pw '123' => {users.CheckUserPassword("admin", "123")}");
Console.ReadLine();
}
}
Extension Methods can be executed on the this-part - in this case only on List<User>s. The Extensionmethod uses Linq to find if Any (at least 1) user of this name and pw exists.
I'm using the code below to get various information about the logged in user and it works, but I want to turn it into a method where I can pass the attribute I want (DisplayName, EmailAddress, etc) and it returns that value for whoever is logged in. I couldn't figure out how to get this to work though.
using (PrincipalContext context = new PrincipalContext(ContextType.Domain))
{
using (UserPrincipal user = UserPrincipal.FindByIdentity(context, User.Identity.Name))
{
if (user != null)
{
loggedInUserfullName = user.DisplayName;
loggedInUserEmail = user.EmailAddress;
}
}
}
While the above works, I can't use any of those variables outside of the scope of the method, which isn't helpful.
I tried the method below to just get the DisplayName but got errors due to FindByIdentity expecting a string (I don't have a string with the user's name - that's what I'm trying to find out!)
public string getUserFullName(object user)
{
user = UserPrincipal.FindByIdentity(ContextType.Domain, user);
string loggedInUserDisplayName = user.DisplayName;
return loggedInUserDisplayName ;
}
UserPrincipal loggedInUser = new UserPrincipal(ContextType.Domain);
// line above throws cannot convert from 'System.DirectoryServices.AccountManagement.ContextType' to 'System.DirectoryServices.AccountManagement.PrincipalContext'
getUserDetails(loggedInUser);
Is there a way to do this? Am I using the wrong library to achieve what I want?
This is a web application using windows authentication, if that makes any difference.
Thanks
I haven't used the DirectoryServices.AccountManagement stuff in a long time, but I'll give this a shot.
This line in your code is throwing an exception:
UserPrincipal loggedInUser = new UserPrincipal(ContextType.Domain);
The exception is telling you that the constructor for UserPrincipal expects a System.DirectoryServices.AccountManagement.PrincipalContext, but you're giving it a System.DirectoryServices.AccountManagement.ContextType. These lines in your working code are correct:
using (PrincipalContext context = new PrincipalContext(ContextType.Domain))
{
using (UserPrincipal user = UserPrincipal.FindByIdentity(context, User.Identity.Name))
{
I'm not sure I fully understand what your intention is, but if you are looking for a reusable way to get info about the logged in user, try something like this:
public static class UserManager
{
public static string GetDisplayName(string name)
{
using (UserPrincipal user = UserPrincipal.FindByIdentity(new PrincipalContext(ContextType.Domain), name))
{
if (user != null)
{
return user.DisplayName;
}
throw new Exception("error");
}
}
}
You can call it by doing this:
var dn = UserManager.GetDisplayName(User.Identity.Name);
Obviously, you'll want to handle the error better. If I'm missing something, let me know and I'll try to update my answer.
Hope this helps!
Edit
To return an object containing multiple fields, you could do something like:
public static UserInfo GetDisplayName(string name)
{
using (UserPrincipal user = UserPrincipal.FindByIdentity(new PrincipalContext(ContextType.Domain), name))
{
if (user != null)
{
return new UserInfo
{
FullName = user.DisplayName,
Email = user.EmailAddress,
GivenName = user.GivenName,
SamAccountName = user.SamAccountName,
Surname = user.Surname
//any other things you may need somewhere else
};
}
throw new Exception("error");
}
}
Here is the UserInfo class:
public class UserInfo
{
public string FullName { get; set; }
public string Email { get; set; }
public string Surname { get; set; }
public string GivenName { get; set; }
public string SamAccountName { get; set; }
}
Having this model class
public class Usuario
{
#region Atributos
private int _intID = 0;
private Perfil _Perfil_FK = null;
private String _strNombre = "";
private String _strPassword = "";
#endregion
#region Propiedades
public int ID
{
get { return _intID; }
set { _intID = value; }
}
public virtual Perfil Perfil_FK
{
get { return _Perfil_FK; }
set { _Perfil_FK = value; }
}
public int PerfilID { get; set; }
public String Nombre
{
get { return _strNombre; }
set { _strNombre = value; }
}
public String Password
{
get { return _strPassword; }
set { _strPassword = value; }
}
#endregion
}
I'm trying to use this query, the data base table have data, so i can't see the problem?
Usuario user = (from U in _db.Usuario
where ((U.Nombre == model.UserName) && (U.Password == encripPassword))
select U).First();
If you need more info about the data base let me know to update my question
Does the table actually have a row with that name and password?
When you run the generated SQL statement in SQL Management Studio, do you get a result?
Suggest checking your two values in UserName and encripPassword for valid values.
Usuario user = _db.Usuario
.FirstOrDefault(x=>x.Nombre == model.UserName
&& x.Password == encripPassword);
string sql = (user as ObjectQuery).ToTraceString(); //the SQL query generated.
if(user==null)
{
//doesn't exist.
}
Obviously you're not pointing to the right database, or your don't have a user name with that password (I'm guessing the encrypted password doesn't match what's in the DB. Try using FirstOrDefault(), then you can check for Null if the password is wrong...
Check if your class has the proper attributes that tell Linq to SQL how to match it against a database table.
Your class should have
[Table(Name="Nombres")]
attribute, and properties should have
[Column]
attributes, some should also be primary keys etc.
If you don't already have this, I suggest you generate the class using the Entity Class Generation tool: http://msdn.microsoft.com/en-us/library/bb425822.aspx#linqtosql_topic32