Problem handling custom Active Directory date attribute - c#

I have extended our schema with a new attribute of type large integer/interval to be used as a date. There are issues here. One is that in ADUC, the attribute shows as a number while other dates in the system (with same underlying type) show up as a date. That seems to be ok, actually, since it is integer. Maybe ADUC has preset some attributes that it forces to show as dates?
The other is that I am not able to handle this in a correct way with System.DirectoryServices.Accountmanagment. I have a class that extends UserPrincipal which works fine, but adding this new attribute won't work. It fails when I try to pass date and also if I pass long.
I feel I have done something wrong in the AD schema process, although I am not sure what it could be. I have tried googling around, but so far I haven't found any similar issues in any articles.
I am very scared to do something that will mess up my AD so I am asking here for some tips regarding this, especially with dates.
EDIT:
I did some more testing and I have a solution for the AccountManagment.UserPrincipal extension. Though it "feels" strange:
public DateTime? MyCustomDate
{
get
{
object[] result = this.ExtensionGet("my-custom-date");
if (result != null && result.Length > 0)
{
if (result[0].GetType() == typeof(string))
{
long l = 0;
return (long.TryParse(result[0].ToString(), out l) ? DateTime.FromFileTimeUtc(l) : (DateTime?)null);
}
else
{
ActiveDs.IADsLargeInteger li = (ActiveDs.IADsLargeInteger)result[0];
return DateTime.FromFileTimeUtc(((long)li.HighPart << 32) + li.LowPart).ToLocalTime();
}
}
else
return null;
}
set
{
if (value != null)
ExtensionSet("my-custom-date", ((DateTime)value).ToFileTimeUtc().ToString());
else
ExtensionSet("my-custom-date", null);
}
}
I tried different approaches, but it seems only the ToString() method works.
So I basically got it working. Also, I tried this with extending the allready built-in property for accountExires. I get the exact same behaviour so I believe my property in AD is created correctly.
EDIT #2:
The type check in the getter section of the property is there for when the property value sits in memory, if it has been changed by the property it self by the setter. The value is no longer of type com, but string (obviously)

As I have been working with this some more today, it seems my solution in the edit does the job.
public DateTime? MyCustomDate {
get
{
object[] result = this.ExtensionGet("my-custom-date");
if (result != null && result.Length > 0)
{
if (result[0].GetType() == typeof(string))
{
long l = 0;
return (long.TryParse(result[0].ToString(), out l) ? DateTime.FromFileTimeUtc(l) : (DateTime?)null);
}
else
{
ActiveDs.IADsLargeInteger li = (ActiveDs.IADsLargeInteger)result[0];
return DateTime.FromFileTimeUtc(((long)li.HighPart << 32) + li.LowPart).ToLocalTime();
}
}
else
return null;
}
set
{
if (value != null)
ExtensionSet("my-custom-date", ((DateTime)value).ToFileTimeUtc().ToString());
else
ExtensionSet("my-custom-date", null);
}
}

Related

Checking if all values are null inside an initialised object

I have some code that reads data from Excel sheets & put to List.
Now here comes a scenario that user copy some valid data then leaves a lot of rows empty & again then copy valid data. This reads a lot of empty rows. I am using some old code for excel read & that might be used somewhere else so I don't wish to mess up with that.
The problem is I have a lot of object inside List that are initialised but all values are null. So I ended up checking all attribuets in if like:
if (lstConsolidatedData != null)
{
foreach (var lst in lstConsolidatedData)
{
if(lst.a!=null && lst.b!=null && lst.c!=null //& so on....)
{
//some stuff...
}
}
}
I know this is not wither best or maintainable way since excel sheets can be modified & thus whole columns list code need to be changed again & again for a single column added or removed from excel.
Is there any way to check all values inside lst for null in a single statement?
some thing like lst.all !=null
Any idea if not code will also work for me.
Please note lst==null is false
EDIT: using answers from below I am getting this error
EDIT:
o.GetType().GetProperties().Any(c => c.GetValue(o) == null) works for any null but What I actually want to delete rows which contains no data in all columns not for particular one or two column. Issue is not resolved by o.Item1 != null && ... also since name/number of columns changes frequently.
You can use reflection to do this, but beware that it carries performance losses over doing it explicitly.
public bool IsAnyPropertyNull(object o)
{
return o.GetType().GetProperties().Any(c => c.GetValue(o) == null);
}
There is, however, no built-in expression to reduce the o.Item1 != null && o.Item2 != null && ... type syntax.
Also, I'm assuming the values you're talking about are properties. You could also use GetFields or whatever you need if that's more appropriate.
As Mathew Huagen pointed out using reflection to get properties and then checking their values seems to be fine. However, this has a big caveat that is there may be some properties you don't want to be checked. To solve this,you can either put them inside a list (as Tim Schmelter mentioned), or you can decorate desired properties of ConsolidatedData with a custom attribute to distinguish them:
public class ConsolidatedData
{
public int Id { get; set; }
[NotNull]
public object PropertyOne { get; set; }
[NotNull]
public object PropertyTwo { get; set; }
}
public class NotNull : Attribute { }
Then checking against null is safe when you filter out the properties not having NotNull attribute:
ConsolidatedData v = new ConsolidatedData();
bool allNotNull = v.GetType().GetProperties()
.Where(p => null != (Attribute.GetCustomAttribute(p, typeof(NotNull)) as NotNull))
.All(p=>p.GetValue(v) !=null );
Here property Id is not get checked.
You could write an extension method for your ConsolidatedData like this;
public static bool IsAnyPropNull(this ConsolidatedData cdata)
{
return cdata.GetType().GetProperties().Any(p => p.GetValue(this) == null);
}
And you could then filter your list with a one-liner;
var filteredList = lstConsolidatedData.Where(cd => !cd.IsAnyPropNull()).ToList();
This will return a list of all object without nulls. If you want the nulls, remove the !, ofcourse.
Edit;
Wow, my answer is almost the same as Matthew Haugen's... both should work fine, even though the implementation is slightly different.
I ended up digging down into old code & before turning dataset into List I deleted empty rows using code:
//Deleting empty records from Datset
foreach (DataTable source in result.Tables)
{
for (int i = 0; i < source.Rows.Count; i++)
{
DataRow currentRow = source.Rows[i];
bool isEmpty = false;
foreach (var colValue in currentRow.ItemArray)
{
if (!string.IsNullOrEmpty(colValue.ToString()))
{
isEmpty = false;
break;
}
else
isEmpty = true;
}
if (isEmpty)
source.Rows[i].Delete();
}
}
result.AcceptChanges();
Don't forget .AcceptChanges() since it actually save the changes done to dataset without which no records are removed.

Using a variable for a LINQ property

I want to pass a property into a method and search for records that match the given value. The following code does not throw an error, but also does not return any data - ever. I don't know if the problem is setting the column / property like this, or if something else is wrong... the code looks like:
public virtual IList<ReceivingInspection> GetRecordsBySupplier(string property, string value) {
if (property.Length < 1 || value.Length < 1) return null;
var result = from ri in _receiveInspectRepository.Table
where ri.property == value
select ri;
return result.ToList();
}
You should iterate over the items in _receiveInspectRepository.Table and use Reflection to get the value of the property
Something like this:
Type t = ri.GetType();
PropertyInfo prop = t.GetProperty(property);
if(prop.GetValue(ri) == value)
{
dostuff();
}

Replacement of switch statement - getting properties via a string name

Basically, i have a system in place where my datagrid marks cells that have changed with a new background colour, to do this i have a method in the object that contains these properties that receives a string which is the name of the property to check, and then a switch statement that takes that string to check the correct property.
public Color HasChanged(string value)
{
switch (value)
{
case "CILRef":
if (_localShipment.cilRef != _originalShipment.cilRef)
{
return Colors.SkyBlue;
}
else
{
return Colors.White;
}
case "ArrivedAtPortDate":
if (_localShipment.arrivedAtPortDate != _originalShipment.arrivedAtPortDate)
{
return Colors.SkyBlue;
}
else
{
return Colors.White;
}
}
}
I've removed the rest of the properties for brevity.
Now i get the nagging sensation that there is a cleaner way to do this string>property without using a switch statement, but i can't for the life of me find anything on google, it's hard to search without some keyword to go on.
I'm also attempting to only save those properties that have changed, i was going to place any changed property name into an array, and then have a loop with yet another switch statement that checked that array and then saved the correct property. However this again seems untidy to me.
is there a cleaner solution to this, hopefully that could handle the addition of new properties without needing to add new code to the switch statements.
I can include the rest of the code that does this checking (namely the WPF binding on the datagrid, and a converter that calls the checking method with the property name as a string parameter) if needed.
You can write an extension method like:
public static object GetPropValue(this object o, string propName)
{
return o.GetType().GetProperty(propName, BindingFlags.Instance | BindingFlags.Public | BindingFlags.IgnoreCase)
.GetValue(o);
}
and use it
if(localShipment.GetPropValue(value).Equals(originalShipment.GetPropValue(value)))
{
}
You could define a common interface to your properties, then create a dictionary of properties like so:
var properties = new Dictionary<string, IProperty>();
and access them like this:
properties["CILRef"]
I would say a switch statement is fine, however, you could do the body of the case in single lines using the condition operator
switch (value)
{
case "CILRef":
return _localShipment.cilRef != _originalShipment.cilRef ? Colors.SkyBlue : Colors.White;
case "ArrivedAtPortDate":
return _localShipment.arrivatedAtPortDate != _originalShipment.arrivedAtPortDate ? Colors.SkyBlue : Colors.White;
...
}
But you would still have repetitive code here, you could take this a step further and have a GetColor method which took a function in
public Colors GetColor(Func<bool> condition)
{
return condition() ? Colors.SkyBlue : Colors.White;
}
...
switch (value)
{
case "CILRef":
return GetColor(() => _localShipment.cilRef != _originalShipment.cilRef);
case "ArrivedAtPortDate":
return GetColor(() => _localShipment.arrivatedAtPortDate != _originalShipment.arrivedAtPortDate);
}
Looking at your code closer, it does appear you are comparing the same property on each shipping and you could literally reduce this to one check using Reflection
public Color HasChanged(string value)
{
var date1 = _localShipment.GetType()
.GetProperty(value)
.GetValue(_localShipment, null);
var date2 = _originalShipment.GetType()
.GetProperty(value)
.GetValue(_originalShipment, null);
return date1 != date2 ? Colors.SkyBlue : Colors.White;
}
For brevity, you could create an extension method to wrap up the Reflection part
public static T Get<T>(this object obj, string name)
{
return obj.GetType().GetProperty(name).GetValue(obj, null);
}
...
return _localShipment.Get<DateTime>(value) != _originalShipment.Get<DateTime>(value) ? Colors.SkyBlue : Colors.White;

Good practice - throw an exception to satisfy a function return

Built a custom IDataReader which goes looking in XML for values that match particular element names and, if found, returns the values. The GetValue function - which has to return an Object, as specified by the interface, looks like this:
public object GetValue(int i)
{
string SearchString = _columns[i];
var searchedAttributeValue = from nm in _el.Attributes(SearchString) select nm;
if (searchedAttributeValue.Count() > 0)
{
return ParseTypes(searchedAttributeValue.First().Value);
}
var searchedElementValue = from nm in _el.Elements(SearchString) select nm;
if (searchedElementValue.Count() > 0)
{
return ParseTypes(searchedElementValue.First().Value);
}
}
This won't build, obviously, because it's possible to reach the end of the function without a valid return.
In this instance, if that happens, it means there's a config error - the user is looking for an element or attribute in the XML that's not there.
I discovered (this was new to me) that you can get round the problem by doing this:
public object GetValue(int i)
{
if (_el == null)
{
_el = XNode.ReadFrom(_reader) as XElement;
}
string SearchString = _columns[i];
var searchedAttributeValue = from nm in _el.Attributes(SearchString) select nm;
if (searchedAttributeValue.Count() > 0)
{
return ParseTypes(searchedAttributeValue.First().Value);
}
var searchedElementValue = from nm in _el.Elements(SearchString) select nm;
if (searchedElementValue.Count() > 0)
{
return ParseTypes(searchedElementValue.First().Value);
}
throw new Exception("oh crap!");
}
So the function doesn't return, it just throws an error.
However, something feels fundamentally wrong with this approach. Is it okay, why is it okay/not okay and is there a better way to handle the situation?
Well unless you really need the catch block, you don't need to introduce try/catch/finally. You can just add a throw statement at the end of your initial method.
Along the way, I'd start using FirstOrDefault(), follow normal C# naming conventions for local variables, stop using query expressions when they're not terribly useful, and throw a more specific exception:
public object GetValue(int i)
{
string searchString = _columns[i];
var searchedAttribute = _el.Attributes(searchString).FirstOrDefault();
if (searchedAttribute != null)
{
return ParseTypes(searchedAttribute.Value);
}
var searchedElement = _el.Elements(searchString).FirstOrDefault();
if (searchedElement != null)
{
return ParseTypes(searchedElement.Value);
}
// Nothing found - oops.
throw new SomeSpecificException("Some message here");
}
If you need the catch block for logging, I'd probably try to catch a specific exception, which probably means moving that into ParseTypes anyway (as you shouldn't get any exceptions from the rest of the calls...) Either way, keep the throw statement at the end.
EDIT: In terms of design, whether you should throw an exception when the value isn't found or not really depends on the expectations. We have no way of knowing whether that indicates something wrong with the system, or just a perfectly normal absence of data. That should determine your choice here. In particular, if every caller is going to try to catch the exception, then that's a design smell and you should either return null or use something like the Dictionary<,>.TryGetValue approach.
I like to make my methods similar to the .NET framework Parse() and TryParse() methods. In your case I would either do:
public object GetValue(int i)
{
// ...
// fail
throw new Exception("Cannot get value");
}
or do:
public bool TryGetValue(int i, out object result)
{
// ...
// fail
result = null;
return false;
}
The try catch is irrelvant. you could simply refactor your code to:
public object GetValue(int i)
{
if (_el == null)
{
_el = XNode.ReadFrom(_reader) as XElement;
}
string SearchString = _columns[i];
var searchedAttributeValue = from nm in _el.Attributes(SearchString) select nm;
if (searchedAttributeValue.Count() > 0)
{
return ParseTypes(searchedAttributeValue.First().Value);
}
var searchedElementValue = from nm in _el.Elements(SearchString) select nm;
if (searchedElementValue.Count() > 0)
{
return ParseTypes(searchedElementValue.First().Value);
}
throw new Exception("oh crap!");
}
this has the same net result.
That said throwing exceptions are expensive (computationaly). If you want this to bomb out (stop processing completly, this is a major problem and it should simply die) then thrown an exception.
If it's simply a way to identify when the if statements are not being met maybe change it to a TryParse type function:
public bool GetValue(int i, out object returnVal)
{
if (_el == null)
{
_el = XNode.ReadFrom(_reader) as XElement;
}
string SearchString = _columns[i];
var searchedAttributeValue = from nm in _el.Attributes(SearchString) select nm;
if (searchedAttributeValue.Count() > 0)
{
returnVal = ParseTypes(searchedAttributeValue.First().Value);
return true;
}
var searchedElementValue = from nm in _el.Elements(SearchString) select nm;
if (searchedElementValue.Count() > 0)
{
returnVal = ParseTypes(searchedElementValue.First().Value);
return true;
}
return false;
}
then
if (GetValue(i, out value))
//success
else
//it's failed.
If it is genuinely an exceptional circumstance that the search won't return anything because the configuration is wrong, then by all means, throw an exception. The problem with this is continuation. Can client code continue if an item isn't found? If it can, client code will need to closely examine any exception thrown from your code to determine if it was thrown because an item didn't exist, or because something else went wrong that can't be continued from.
The other option is to return something that indicates that the value wasn't found, and allow calling code to deal with it. You could return null to indicate to calling code that no item was found, and that might be fine in this instance. A better solution might be to create a simple Optional<T> class, that contains an indication of whether the object existed (perhaps HasValue), and the object itself if it did. Calling code can easily and more concretely check whether an object was returned and deal with a situation where it isn't, and exceptions don't require additional scrutiny.
EDIT: A better alternative altogether might be the Parse and TryParse paradigm suggested by others, but I think this answer might have some use, so I'll leave it here :)
There are quite a few bad practices in this approach: catch (Exception) should always be avoided. Only catch the exceptions you expect there. Also, throw Exception() is bad style. Always throw something more specific. But for what you're looking for: You don't need a finally after all, just place a throw new ArgumentException() or something as the last line of the function. That is good practice, if it is really a programming error if the code ever runs there.

C# if statement syntax with list of objects

I have a list of objects, called Attributes, that, essentially, i need to do the following in C#
<pseudocode>
if (list.Contains(Attribute where getName() == "owner"))
{
do stuff
}
</pseudocode>
the problem I'm having is the nested bracket bit of the if - "Attribute where getName() == "owner". This is my code - it doesn't work, obviously, but most of the if should be right, it's just getting that i need to do the bit in forward slashes and i don't know how.
if (attributes.Contains(Attribute /where/ attribute.getName() == "Owner"))
{
string value = attr.getValue();
value = value.Replace(domain, "");
user = value;
UserExists(value);
}
I'm probably being dense, but I had to restart 3 days development to change everything to using Attribute objects, so my brain is rather destroyed. Sorry.
If you are using a version of .NET that supports LINQ (3.5 or higher), try
if(attributes.Any(attribute=>attribute.getName()=="Owner"))
{
do stuff
}
This has the nice advantage of being fairly readable by whoever has to maintain this code.
if(list.Exists(e=>e.getName() == "owner")) {
//yup
}
You can use LINQ to objects
if (attributes.Count(a => a.getName() == "Owner") > 0)
Without LINQ!:
if (list.Exists(delegate(Attribute a) { return a.GetName() == "Owner"; }))
Please have a look:
var filteredAttributes = from attribute in Attributes
where string.Compare(attribute.getName() ,"Owner",true)==0
select attribute;
foreach(var attribute in filteredAttributes)
{
string value = attr.getValue();
value = value.Replace(domain, "");
user = value;
UserExists(value);
}
You can use LINQ to separate out the required attributes...
IEnumerable<TAttribute> ownerAttributes =
attributes.Where(attribute => attribute.getName() == "Owner");
... and then iterate over those attributes, applying the relevant logic...
foreach(TAttribute attribute in ownerAttributes)
{
// Do stuff...
}
are you using .NET 3.5 or above, if so and presuming that 'attributes' is derived from IEnumerable, you could do the following :
if (attributes.Any(attrib=>attrib.GetName() == "Owner"))
{
//Do code here...
}
Ok, i worked it out, I found a much better way to do it:
for (int i = 0; i < attributes.Count; i++)
{
if (attributes[i].getName() == "Owner")
{
string value = attributes[i].getValue();
value = value.Replace(domain, "");
user = value;
UserExists(value);
}
}
which makes more sense, and actually works.

Categories

Resources