Using a variable for a LINQ property - c#

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

Related

Problem handling custom Active Directory date attribute

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

EF Code First Pre-filter datetime on insert / update

Along the lines of these questions:
'datetime2' error when using entity framework in VS 2010 .net 4.0
How to fix the datetime2 out-of-range conversion error using DbContext and SetInitializer?
I am attempting to use code first to generate a database based on models that I do not own. I.E. I can not modify the models. (I am bringing a desktop application up to speed with a server application.)
I understand that the DateTime value in C# has an invalid MinDate when converting to SqlDateTime. Furthermore, the SQL Express instance created from Entity Framework does not support datetime2. I've tried to apply a filter with default values using a convention:
this.Properties<DateTime>()
.Configure(o => o.HasDatabaseGeneratedOption(DatabaseGeneratedOption.Computed).HasColumnAnnotation("SqlDefaultValue", "GETDATE()"));
However, when I do this, I get the error Cannot insert the value NULL into column 'CreatedDate', table 'blahblahblah.Companies'; column does not allow nulls. INSERT fails.
I'm not sure I understand that error message as the values are never null. But I'm guessing because the DatabaseGeneratedOption is computed, the value isn't being set.
So far, none of these options has worked for the date time. If there is a way to prefilter inserts and updates, I could run a check on the datetime values and set their values to the SqlDateTime min value. However, my Google foo isn't returning any results for that type of operation. If there is no way to do this, I may just make a helper function that uses reflection to auto adjust all datetime objects appropriately.
I ended up writing a helper function to pre-update datetime values.
public static class DateTimeSwitch
{
public static void DateTimeToSqlDateTime(this object obj)
{
Type objType = obj.GetType();
if (typeof(IEnumerable).IsAssignableFrom(objType))
{
IEnumerable enumerable = (IEnumerable)obj;
if (enumerable != null)
{
foreach (object c in enumerable)
{
if (c != null)
c.DateTimeToSqlDateTime();
}
}
}
else
{
PropertyInfo[] properties = objType.GetProperties();
foreach (PropertyInfo property in properties)
{
if (typeof(DateTime).IsAssignableFrom(property.PropertyType))
{
// Get the value, adjust it.
DateTime value = (DateTime)property.GetValue(obj, null);
if (value < (DateTime)SqlDateTime.MinValue)
{
property.SetValue(obj, (DateTime)SqlDateTime.MinValue, null);
}
}
else if (!property.PropertyType.IsPrimitive && typeof(String) != property.PropertyType && typeof(IEnumerable).IsAssignableFrom(property.PropertyType))
{
IEnumerable enumerable = (IEnumerable)property.GetValue(obj, null);
if (enumerable != null)
{
foreach (object c in enumerable)
{
if (c != null)
c.DateTimeToSqlDateTime();
}
}
}
else if (!property.PropertyType.IsPrimitive)
{
if (property.PropertyType.Assembly == objType.Assembly)
{
var value = property.GetValue(obj, null);
if (value != null) value.DateTimeToSqlDateTime();
}
}
}
}
}
}

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;

How to safely check if a dynamic object has a field or not

I'm looping through a property on a dynamic object looking for a field, except I can't figure out how to safely evaluate if it exists or not without throwing an exception.
foreach (dynamic item in routes_list["mychoices"])
{
// these fields may or may not exist
int strProductId = item["selectedProductId"];
string strProductId = item["selectedProductCode"];
}
using reflection is better than try-catch, so this is the function i use :
public static bool doesPropertyExist(dynamic obj, string property)
{
return ((Type)obj.GetType()).GetProperties().Where(p => p.Name.Equals(property)).Any();
}
then..
if (doesPropertyExist(myDynamicObject, "myProperty")){
// ...
}
This is gonna be simple. Set a condition which checks the value is null or empty. If the value is present, then assign the value to the respective datatype.
foreach (dynamic item in routes_list["mychoices"])
{
// these fields may or may not exist
if (item["selectedProductId"] != "")
{
int strProductId = item["selectedProductId"];
}
if (item["selectedProductCode"] != null && item["selectedProductCode"] != "")
{
string strProductId = item["selectedProductCode"];
}
}
You need to surround your dynamic variable with a try catch, nothing else is the better way in makking it safe.
try
{
dynamic testData = ReturnDynamic();
var name = testData.Name;
// do more stuff
}
catch (RuntimeBinderException)
{
// MyProperty doesn't exist
}

Not all code paths return a value

I am having this Linq To SQL query which is taking Customer Category from database.The CustCategory will be defined already.Here is the query.
public IList<string> GetAccountType()
{
using (var db = new DataClasses1DataContext())
{
var acctype = db.mem_types.Select(account=>account.CustCategory).Distinct().ToList();
if (acctype != null)
{
return acctype;
}
}
}
Currently I am getting an error that Not all code paths return a value.If I am always certain that the value is there in the database then do I need to check for null,If I need to check for null then how do I handle this.
Can anyone help me with this.
Any suggestions are welcome.
Since Enumerable.ToList never returns null (see the Return Value section of the documentation), you can safely remove the if.
EDIT: Note that, no matter what your database contains, acctype will never be null:
If no value is found in the database, the return value will be an empty list (which is different than null).
If one record is found and its value is null, the return value will be a valid list with one entry, whose value is null. Still, the list itself is not null.
What happens if:
if (acctype != null)
Is null? What is your method supposed to return?
You need to return something
This is not about LINQ to SQL, the method GetAccountType() must return IList<string>. You should return return acctype; and then check this returned list later using Any(), something like:
if(GetAccountType.Any()){
//not empty
}
How about something like this for a fairly clean and readable solution?:
(Note, updated: removed the check for null, since it would clearly not have any effect).
public IList<string> GetAccountType()
{
var acctype = new List<string>();
using (var db = new DataClasses1DataContext())
{
acctype = db.mem_types.Select(
account=>account.CustCategory).Distinct().ToList();
}
return acctype;
}
You need to return a value from your function:
public IList<string> GetAccountType()
{
using (var db = new DataClasses1DataContext())
{
var acctype = db.mem_types.Select(account=>account.CustCategory).Distinct().ToList();
if (acctype != null)
{
return acctype;
}
}
return acctype;
}

Categories

Resources