Pass Many Classes To Method - c#

Good time of the day!
I'm trying to pass few classes, which inherit from one base class.
The structure looks as follows:
Permission Class - Permisions for user
Edit : Permission - Ability to edit record
Delete : Permission - Ability to delete record
Create : Permission - Ability to create record
User Class - User class
private Permission[] permissions - permissions available for user
public void AddDefaultPermissions(Permission[] permissions)
What i can't figure out, is how to pass multiple permissions to this function and check if permission has already been added. What i was thinking of is to allow something like the following method call:
AddDefaultPermissions(Edit, Delete, Create);
Permission Class:
using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
public class Permission {
private int permissionID;
private string permissionName;
private string permissionDescription;
private int accessStartTimestamp;
private int accessEndTimestamp;
public int PermissionID {
get { return permissionID; }
set { permissionID = value; }
}
public string PermissionName {
get { return permissionName; }
set { permissionName = value; }
}
public string PermissionDescription {
get { return permissionDescription; }
set { permissionDescription = value; }
}
public int AccessStartTimestamp {
get { return accessStartTimestamp; }
set { accessStartTimestamp = value; }
}
public int AccessEndTimestamp {
get { return accessEndTimestamp; }
set { accessEndTimestamp = value; }
}
}
Edit Class:
using System;
using System.Collections;
using System.Collections.Generic;
public class Edit : Permission {
public Edit() {
PermissionID = 1;
PermissionName = "Edit";
PermissionDescription = "Allow user to edit records";
AccessStartTimestamp = 1488369600;
AccessEndTimestamp = 1491048000;
}
}
For checking if permission is already exists, PermissionID variable can be used. This variable is inherited by all of the permission classes and unique for the permission (Edit = 1, Create = 2, Delete = 3);
I saw some examples with LINQ, but couldn't make it to work, since actually never worked with LINQ.
Is it possible to achieve this functionality + to check if permission has already been added to avoid duplicates?

You can easily eliminate the duplicaets from your input using Distinct:
AddDefaultPermissions(params Permission[] permissions)
{
var actualPermissions = permissions.Distinct();
}
This assumes your Permission-class implements Equals and GetHashCoe appropriately by comparing their PermissionID-property, e.g:
class Permission
{
public override bool Equals(object obj)
{
var p = obj as Permission;
if(p == null) return false;
return this.PermissionID == p.PermissionID;
}
public override int GetHashCode()
{
return this.PermissionID .GetHashCode();
}
}
If you can´t change the implementation of this class you can also use your own implementation for IEqualityComparer within Distinct:
var actualPermissions = permissions.Distinct(myEqualityComparer);
If you just want to check if the item was allready added you can just check for a Contains which will rely on Equals to be implemented as shown above:
AddDefaultPermissions(params Permission[] permissions)
{
var actualPermissions = permissions.Distinct();
foreach(var p in permissions)
if(!listOfAllreadyAddedPermissions.Contains(p)
listOfAllreadyAddedPermissions.Add(p);
}
Or alternativly again - if you don´t want to change the class - use Any:
if(!listOfAllreadyAddedPermissions.Any(x => x.PermissionId == p.PermissionId)
listOfAllreadyAddedPermissions.Add(p);

You can pass an arbitrary number of permissions using params syntax:
AddDefaultPermissions(params Permission[] permissions) {
}
To check which permission is used more than once, group and check counts:
foreach (var pg in permissions.GroupBy(p => p).Where(g => g.Count() > 1) {
Console.WriteLine("Permission {0} is used {1} times.", pg.Key, pg.Count());
}
Important: the above will work only if Permission class properly implements Equals and GetHashCode methods.

Related

c# GroupPrincipal.FindByIdentity finds the group, but when using GetMembers getting error - there is no such object on the server

this code used to work fine for the past year,
now it is still working, but i have only 4 groups that generate this error...
the code is simple:
using (var context = new PrincipalContext(ContextType.Domain, domName))
{
foreach (string grp in myGroups)
{
using (var group = GroupPrincipal.FindByIdentity(context, IdentityType.Name, grp))
{
PrincipalSearchResult<Principal> usersList;
usersList = group.GetMembers(true);
int usersListCount = usersList.Count();
}}}
when these specific groups come to search , i get the group and can see its description in the group object variable, but when getting its members i get an error massage :
base: "There is no such object on the server.\r\n"
ErrorCode: -2147016656
again,this happens only with 4 specific groups from the same domain, and same OU.
this just started a few days ago without me changing anything, not permissions, nothing in the code, very strange...
any ideas ?
When I encountered this problem I could not have an empty group. I had to produce "best possible" results while the network people were working to resolve the "foreign SID" issue.
I know it is a lot extra but it satisfied the auditors so maybe it will help you. This is what I did:
Precursor: I had already built a class that held all the properties of the AD Entity.
Got a list of users and all their group memberships.
Wrapped the call to get members in a try... catch and when this error occurred I inserted a "Group Membership" property of "Error Retrieving members"
When I had iterated through all the Groups I grabbed a list of all groups that had the error message as a group member then queried the Users list to get a list of all the users who were members of that group.
Then inserted Property records with the found users names.
Since this answer is more about solution structure I will only give a very brief outline of the classes used. While far from elegant it gave me a reusable container that was easy to understand and share and provided a solution that was durable across several networks. It probably lacks in many ways but it passes test #1 - it worked.
public class ADPropEntry : IComparable<ADPropEntry>
{
#region Properties
public string Name { get { return _name; } set { _adName = value; SetPropVals(_adName); } }
public string Value { get { return _v; } set { _v = value; DoValConversion(); } }
public bool IsVisible { get { return _isVis; } set { _isVis = value; } }
public string ConvertTo { get { return _convertVal; } set { _convertVal = value; } }
public int ID { get { return _id; } set { _id = value; } }
#endregion
private void SetPropVals(string s)
{
switch (s)
{
case "accountexpires": _name = "Account Expires"; _isVis = false; _convertVal = "FromFileTime"; break;
... more handles each property conversion
}
}
}
public class ADEntity : IComparable<ADEntity>
{
#region Properties
public string Name { get { return _name; } set { _name = value; } }
public List<ADPropEntry> MyProperty { get { return _ade; } set { _ade = value; } }
public string EntityType { get { return _entT; } set { _entT = value; } }
public string ADName { get { return GetProperty("SAM Account Name"); } }
#endregion
}
This formed provided me a durable data container and then I used another class to query AD in whatever method makes sense. This was packaged in a DLL that the client application could use.
class ADAccess
{
#region Properties
public bool HasErrors { get { return (bool)(_errMsg.Length > 10); } }
public string ErrorMsg { get { return _errMsg; } }
public List<ADEntity> GroupEntries { get { return _lstGrps; } }
public List<ADEntity> UserEntries { get { return _lstUsrs; } }
public List<ADEntity> PrinterEntries { get { return _lstPrts; } }
public List<ADEntity> ComputerEntries { get { return _lstCmps; } }
#endregion
public List<ADEntity> GetADListByMSO(string groupType)
{
if (groupType == "")
{
// get them all return an empty list populating properties
}
else
{
// set the context and fetch return populated list
}
}
Used the same structure to report on SQL server permissions as well.
i found out the issue,
the problematic groups contained users from different domains,
once removed these users from the groups , everything went back to work.
thanks.

Getting list of attributes for all methods in class

I'm attempting to use a custom attribute to generate a list of commands (string) that a user would issue into my console application and the corresponding method will be executed. I'm currently stuck, my command list is always empty.
Here's my attribute:
public class ImporterAttribute : Attribute
{
public string Command { get; set; }
}
Here's the class:
public class DataProcessor
{
public List<ImporterAttribute> Commands { get; set; }
public DataProcessor()
{
//Use reflection to collect commands from attributes
Commands = GetCommands(typeof(DataProcessor));
}
public static List<ImporterAttribute> GetCommands(Type t)
{
var commands = new List<ImporterAttribute>();
MemberInfo[] MyMemberInfo = t.GetMethods();
foreach (MemberInfo member in MyMemberInfo)
{
var att = (ImporterAttribute)Attribute.GetCustomAttribute(member, typeof(ImporterAttribute));
if (att == null) continue;
var command = new ImporterAttribute();
command.Command = att.Command;
commands.Add(command);
}
return commands;
}
[Importer(Command = "?")]
private string Help()
{
return "Available commands: " + (from c in Commands select c.Command).Aggregate((a, x) => a + " " + x);
}
[Importer(Command = "Q")]
private void Quit()
{
Environment.Exit(0);
}
}
Then I use a switch statement to check user input against the command list and run the requested method. So my question is: why is my command list always null? I imagine I just misunderstood something in the docs.
Bonus question: does anyone have a better/more practical approach that they use/have used to tackle this feature?
The problem with your code is that your methods are private. GetMethods by default only retrieve public methods, so if you change your Help and Quit method signature to public, you'll get 2 commands.
If you want to keep them private,you can use BindingFlags like this:
t.GetMethods(BindingFlags.NonPublic | BindingFlags.Instance);

Accessing Property of Class in List<Class>

I see a lot of similar questions but none with a direct answer. I have a List<ClientEntry>. I want to access properties in ClientEntry. My code looks like this:
class ClientEntry
{
private string _clientName;
private string _clientEmail;
public void ClientEntry(string name, string email)
{
this._clientName = name;
this._clientEmail = email;
}
public string ClientName
{
get
{
return _clientName;
}
set
{
_clientName = value;
}
}
public string ClientEmail
{
get
{
return _clientEmail;
}
set
{
RegexUtilities Validator = new RegexUtilities();
if (Validator.IsValidEmail(value))
{
_clientEmail = value;
}
}
}
}
Later:
private List<ClientEntry> clientList;
I then add a bunch of ClientEntry's to the List.
How can I access the ClientName and ClientEmail properties for items in clientList? Also, how can I check for the existance of a certain ClientName or ClientEmail property within the List? Is this even possible with a list of objects? I know a dict would probably serve better, but I wanted to see if this could be done with a List and a class with properties.
You can use Linq to look for values inside of a list using Any()
Eg.
bool emailExists = clientList.Any(x=>x.ClientEmail == <email>);
To access values, you can use a index accessor if you know it, loop the collection, or use Where() to search it:
var email = clientList[index].ClientEmail
or
foreach (var client in clientList)
{
var email = client.ClientEmail
}
or
var email = clientList.Where(x=>x.ClientName == <clientName>).FirstOrDefault();
you can explore your list as below
foreach (ClientEntry client in clientList)
{
//client.ClientName
//client.ClientEmail
}
to find a particular record you can search it as
clientList.Where(p=> p.ClientEmail == "email#domain.com").FirstOrDefault();
To access a specific of item in the list, you input the index / using foreach
string name = clientList[index].ClientName;
foreach(var client in clientList)
{
name = client.ClientName; // access the item one by one
}
To check the existence of certain value of a property, use linq
bool isExist = clientList.Any(i => i.ClientName == "John");
Use Extension Methods !
Something like this, you can write unit test against the extension class easily and also it's straightforward to read.
public static class ClientEntriesExtension
{
public static bool ExistEmail(this IEnumerable<ClientEntry> entries, string targetEmail)
{
return entries.Any(x=>x.ClientEmail == targetEmail);
}
}
bool exist = clientList.ExistEmail(targetEmail)

Entity Framework - Many to Many relationship not saving to database

I have stumbled upon a problem with Entity Framework this morning.
I have following code mapping a modified entity and saving it into database.
public Group Save(Group x)
{
using (var db = new HostContext())
{
db.Projects.Attach(x.Project);
if (x.ID != 0)
{
db.AttachableObjects.Attach(x);
var manager = ((IObjectContextAdapter)db).ObjectContext.ObjectStateManager;
manager.ChangeObjectState(x, EntityState.Modified);
}
else
{
db.AttachableObjects.Add(x);
}
db.SaveChanges();
return x;
}
}
I call Save method with existing group as a parameter. Group contains one user I want to add as a member.
The method finishes successfully, however the relationship is not persisted in database.
Any help is very appreciated.
EDIT: These are my classes
class User : AttachableObject
{
...
private List<Group> memberof;
[DataMember]
[InverseProperty("Members")]
public List<Group> MemberOf
{
get { return memberof; }
set { memberof = value; }
}
...
}
class Group : AttachableObject
{
...
private List<User> members;
[DataMember]
[InverseProperty("MemberOf")]
public List<User> Members
{
get { return members; }
set { members = value; }
}
...
}
EDIT2: This is where the Save method is called
public Group AcceptInvite(int id)
{
var mapper = new InviteMapper();
var userMapper = new UserMapper();
var groupMapper = new GroupMapper();
var invite = mapper.Find(id);
if (invite != null)
{
var group = groupMapper.Find(invite.GroupID);
var user = userMapper.Find(invite.InviteeID);
group.Members.Add(user);
mapper.Delete(invite.ID);
return groupMapper.Save(group);
}
return null;
}
EDIT3: My mappers
public class GroupMapper
{
public Group Find(int id)
{
using (var db = new HostContext())
{
return db.AttachableObjects
.Include("Project")
.OfType<Group>().FirstOrDefault(x => x.ID == id);
}
}
}
The rest of the mappers is the same, only using their own tables.
You are not changing the relationship info of Project, you are only setting x to modified, relationship info must be changed explicitly.
So x.Project must have some property that points back to Group, you need to set it so the change is recorded.
I am guessing that x is resurrected via some deserialization process?

Need a refresher course on property access

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

Categories