Handling Crystal Reports without parameters - c#

I currently run a Windows service that exports Crystal Reports to PDF on schedule. Out of the 66 reports that run, 5 or so have no parameters defined.
Using the Crystal Reports DLL, I understand that the ReportDocument.Export() method requires parameters, as I have been experiencing the "missing parameter values" exception when it hits the ReportDocument.Export() method.
I'm currently doing my set parameters in this method:
private void SetParameters(string rawParameters = null)
{
var crystalParameters = new Dictionary<string, object>();
var parameters = String.IsNullOrEmpty(rawParameters) ? null : HttpUtility.ParseQueryString(rawParameters);
if(parameters != null)
{
foreach (string rawKey in parameters.AllKeys)
{
var value = parameters[rawKey];
// Check for array value (e.g. key[0]=value)
var arrayCheck = Regex.Match(rawKey, #"^(.+)\[[0-9]?\]$");
if (arrayCheck.Success)
{
var key = arrayCheck.Groups[1].Value;
// Existing entry for this key, reconstruct object array with this added
if (crystalParameters.ContainsKey(key))
{
var newParameterArray = new object[((object[])crystalParameters[key]).Count() + 1];
int i = 0;
foreach (object item in (object[])crystalParameters[key])
{
newParameterArray[i++] = item;
}
newParameterArray[i++] = (object)value;
crystalParameters[key] = (object)newParameterArray;
}
// New array value
else
crystalParameters[key] = (object)new object[] { value };
}
// Discrete value
else
crystalParameters[rawKey] = (object)parameters[rawKey];
}
foreach (string parameter in crystalParameters.Keys)
{
try
{
this.reportDocument.SetParameterValue(parameter, crystalParameters[parameter]);
}
catch (Exception ex)
{
// Ignore invalid parameter exceptions, otherwise throw again
if (ex.HResult != -2147352565)
{
throw ex;
}
}
}
}
}

I tested a couple of scenarios, including ReportDocument.SetParameterValue("", ""). However, I resolved the issue when and only when I blanked out this if clause; reserving this method to be called only when the report does have parameters.

Related

Twincat Ads Reactive weird Handle behaviour

I'm working with TwincatAds.Reactive 6.0.190 in .NET 6 WPF Desktop application.
I'm also using MVVM pattern.
My goal is to create a Class that is going to observe for a PLC Variable changes, collect those variables to a dictionary, and later on use those values in the ViewModel.
Here's the method where I'm attaching the notification and action where I'm handling the notification.
public void AttachNotification(IEnumerable<(string key, Type type)> Symbols)
{
_observerValueNotification = Observer.Create<ValueNotification>(val =>
{
// Does handle really start from 2?
var handle = val.Handle;
if (val.UserData is object[] objects)
{
string tag = objects[handle - 2].ToString();
if (!_values.Any(x => x.Key == tag))
_values.Add(new SymbolModel { Key = tag, Value = val.Value });
else
{
var symbol = _values.First(x => x.Key == tag);
symbol.Value = val.Value;
}
}
ValuesChanged?.Invoke(_values);
});
if (_plcWrapper.AdsClient != null)
{
// Get Symbols from SymbolLoader
List<AnySymbolSpecifier> list = new();
List<string> userData = new();
foreach (var (key, type) in Symbols)
{
list.Add(new AnySymbolSpecifier(key, new AnyTypeSpecifier(type)));
userData.Add(key);
}
_subscription2 = _plcWrapper.AdsClient.WhenNotificationEx(list, NotificationSettings.ImmediatelyOnChange, userData.ToArray())
.Subscribe(_observerValueNotification);
}
}
I'm using ValueNotification simply because, I'd like to use this pattern also for complex PLC Variables like Structs.
As You can see, in the WhenNotificationEx method I'm using UserData[] to provide some sort of identification of what Variable has changed when handling the change.
My idea was to use Handle property from ValueNotification as an indexer in UserData[] to identify what variable I'm dealing with, but for some reason Handle starts from 2.
My question is, is it expected behaviour, does the Handle value really always start from 2?
I've decided that relying on the Handle being index in the UserData array is quite unpredictable as Handle is being created by the Twincat Ads server.
Solved the issue by creating own extension method to the WhenNotificationEx. Turned out IDisposableHandleBag has exactly what I was looking for, which is SourceResultHandles property, where AnySymbolSpecifier and ResultHandle are both stored!
Here's created extension method
public static Dictionary<string, uint> Handles { get; private set; } = new();
public static IObservable<ValueNotification> WhenNotificationWithHandle(this IAdsConnection connection, IList<AnySymbolSpecifier> symbols, NotificationSettings settings)
{
IAdsConnection connection2 = connection;
IList<AnySymbolSpecifier> symbols2 = symbols;
NotificationSettings settings2 = settings;
if (connection2 == null)
{
throw new ArgumentNullException("connection");
}
if (symbols2 == null)
{
throw new ArgumentNullException("symbols");
}
if (symbols2.Count == 0)
{
throw new ArgumentOutOfRangeException("symbols", "Symbol list is empty!");
}
IDisposableHandleBag<AnySymbolSpecifier> bag = null;
EventLoopScheduler scheduler = new EventLoopScheduler();
IObservable<int> whenSymbolChangeObserver = connection2.WhenSymbolVersionChanges(scheduler);
IDisposable whenSymbolChanges = null;
Action<EventHandler<AdsNotificationExEventArgs>> addHandler = delegate (EventHandler<AdsNotificationExEventArgs> h)
{
connection2.AdsNotificationEx += h;
bag = ((IAdsHandleCacheProvider)connection2).CreateNotificationExHandleBag(symbols2, relaxSubErrors: false, settings2, null);
bag.CreateHandles();
// Collect Handles
Handles.Clear();
foreach (var item in bag.SourceResultHandles)
Handles.Add(item.source.InstancePath, item.result.Handle);
whenSymbolChanges = whenSymbolChangeObserver.Subscribe((Action<int>)delegate
{
bag.CreateHandles();
Handles.Clear();
foreach (var item in bag.SourceResultHandles)
Handles.Add(item.source.InstancePath, item.result.Handle);
}, (Action<Exception>)delegate
{
TcTraceSource traceAds = AdsModule.TraceAds;
DefaultInterpolatedStringHandler defaultInterpolatedStringHandler = new DefaultInterpolatedStringHandler(101, 1);
defaultInterpolatedStringHandler.AppendLiteral("The AdsServer '");
defaultInterpolatedStringHandler.AppendFormatted(connection2.Address);
defaultInterpolatedStringHandler.AppendLiteral("' doesn't support SymbolVersionChanged Notifications! Handle recreation is not active!");
traceAds.TraceInformation(defaultInterpolatedStringHandler.ToStringAndClear());
});
};
Action<EventHandler<AdsNotificationExEventArgs>> removeHandler = delegate (EventHandler<AdsNotificationExEventArgs> h)
{
if (whenSymbolChanges != null)
{
whenSymbolChanges.Dispose();
}
scheduler.Dispose();
if (bag != null)
{
bag.Dispose();
bag = null;
Handles.Clear();
}
connection2.AdsNotificationEx -= h;
};
return from ev in Observable.FromEventPattern<EventHandler<AdsNotificationExEventArgs>, AdsNotificationExEventArgs>(addHandler, removeHandler)
where bag.Contains(ev.EventArgs.Handle)
select new ValueNotification(ev.EventArgs, ev.EventArgs.Value);
}

SharePoint CSOM not throwing expected exceptions on ExecuteQuery

Essentially what the title says, for whatever reason when I call clientContext.ExecuteQuery(); and for example the list the data is going into has a missing column, I don't get an exception, it actually just runs as expected and I have to go and investigate what might have caused the data to not appear.
The NuGet package i'm using is Microsoft.SharePoint2016.CSOM version 16.0.4690.1000
Any hints, or suggestions to point me in the right direction appreciated. It's entirely possible I'm being a bit dim here.
Here's the full code block I'm using for updating list items:
public override object UpdateEntity(object entity)
{
if (entity == null)
{
// if the definition is null throw argument null exception.
throw new ArgumentNullException(nameof(SharePointDefinition));
}
// check for incorrect type being passed in that we can still handle
if (entity is List<SharePointDefinition> definitions)
{
return UpdateEntities(definitions);
}
ExceptionHandlingScope exceptionScopeFetch = new ExceptionHandlingScope(clientContext);
ExceptionHandlingScope exceptionScopeSubmit = new ExceptionHandlingScope(clientContext);
// run single definition submit to SP.
if (entity is SharePointDefinition definition)
{
// variables.
IntegrationEventLog log = new IntegrationEventLog();
EventInformation ei = new EventInformation();
List list = null;
ListItemCollection listItemCol = null;
using (exceptionScopeFetch.StartScope())
{
using (exceptionScopeFetch.StartTry())
{
// get the required list
list = clientContext.Web.Lists.GetByTitle(definition.ListName);
// create query
listItemCol = list.GetItems(CamlQuery.CreateAllItemsQuery());
// set these items for retrieval.
clientContext.Load(listItemCol);
}
using (exceptionScopeFetch.StartCatch())
{
// Assume that if there's an exception, it can only be
// because there is no list with the specified title, so report this back.
if (exceptionScopeFetch.HasException)
{
ei = new EventInformation()
{
LoggingEventType = LoggingEventType.DataSentFailure,
LoggingSeverity = LoggingSeverity.HighSeverity,
SerialisedMessage = JsonConvert.SerializeObject(definition),
ServiceMessage = $"Hit SharePoint exception handler during list and data pull: Message: {exceptionScopeFetch.ErrorMessage}",
StackTrace = exceptionScopeFetch.ServerStackTrace,
TimeGenerated = DateTime.Now,
TimeWritten = DateTime.Now,
};
log.EventLogEntryType = EventLogEntryType.Error;
log.EventID = LoggingSeverity.HighSeverity;
log.Message = JsonConvert.SerializeObject(ei);
AddToLog(log);
}
}
using (exceptionScopeFetch.StartFinally())
{
//
}
}
// get item instances first
try
{
clientContext.ExecuteQuery();
}
catch (Exception genEx)
{
// return failure log.
ei = new EventInformation()
{
LoggingEventType = LoggingEventType.DataSentFailure,
LoggingSeverity = LoggingSeverity.HighSeverity,
SerialisedMessage = JsonConvert.SerializeObject(definition),
ServiceMessage = "Errored trying to get data from SharePoint list ready for update operation; see stacktrace for more information.",
StackTrace = genEx.StackTrace,
TimeGenerated = DateTime.Now,
TimeWritten = DateTime.Now,
};
log.EventLogEntryType = EventLogEntryType.Error;
log.EventID = LoggingSeverity.HighSeverity;
log.Message = JsonConvert.SerializeObject(ei);
AddToLog(log);
return false;
}
// this is the column we want to overwrite.
var comparisonColumn = definition.UpdateIdentifier ?? "";
//List col to dict
var listItems = listItemCol.Cast<ListItem>().ToList();
// Now we know if we were able to retrieve existing data, perform submit.
using (exceptionScopeSubmit.StartScope())
{
using (exceptionScopeSubmit.StartTry())
{
// loop through our rows
foreach (var row in definition.RowData)
{
int existingItemIndex = -1;
// see if the row exists already
if (!string.IsNullOrEmpty(comparisonColumn) && listItems.Count != 0)
{
existingItemIndex = listItems.FindIndex(x => x[comparisonColumn].ToString() == row[comparisonColumn]);
}
if (existingItemIndex != -1 && listItems.Count != 0)
{
// item exists - loop through our row columns
foreach (var keyValuePair in row)
{
// they key relates to a column, the Value to the rows colum Value.
listItems[existingItemIndex].ParseAndSetFieldValue(keyValuePair.Key, keyValuePair.Value);
}
// update this item
listItems[existingItemIndex].Update();
}
else
{
ListItemCreationInformation itemCreateInfo = new ListItemCreationInformation();
ListItem newItem = list.AddItem(itemCreateInfo);
// loop through our row columns
foreach (var keyValuePair in row)
{
// they key relates to a column, the Value to the rows colum Value.
newItem[keyValuePair.Key] = keyValuePair.Value;
}
newItem.Update();
}
}
}
using (exceptionScopeSubmit.StartCatch())
{
// Assume that if there's an exception, it can only be
// because there is no list with the specified title, so report this back.
if (exceptionScopeFetch.HasException)
{
ei = new EventInformation()
{
LoggingEventType = LoggingEventType.DataSentFailure,
LoggingSeverity = LoggingSeverity.HighSeverity,
SerialisedMessage = JsonConvert.SerializeObject(definition),
ServiceMessage = $"Error at SharePoint exception handler during submit to list: Message: {exceptionScopeFetch.ErrorMessage}",
StackTrace = exceptionScopeFetch.ServerStackTrace,
TimeGenerated = DateTime.Now,
TimeWritten = DateTime.Now,
};
log.EventLogEntryType = EventLogEntryType.Error;
log.EventID = LoggingSeverity.HighSeverity;
log.Message = JsonConvert.SerializeObject(ei);
AddToLog(log);
}
}
using (exceptionScopeSubmit.StartFinally())
{
//
}
}
// try to execute submit.
try
{
clientContext.ExecuteQuery();
ei = new EventInformation()
{
LoggingEventType = LoggingEventType.DataSentSuccess,
LoggingSeverity = LoggingSeverity.Information,
SerialisedMessage = JsonConvert.SerializeObject(definition),
ServiceMessage = "No exceptions were thrown from the Execution process.",
StackTrace = "",
TimeGenerated = DateTime.Now,
TimeWritten = DateTime.Now,
};
log.EventLogEntryType = EventLogEntryType.Information;
log.EventID = LoggingSeverity.Information;
log.Message = JsonConvert.SerializeObject(ei);
}
catch (Exception genEx)
{
ei = new EventInformation()
{
LoggingEventType = LoggingEventType.DataSentFailure,
LoggingSeverity = LoggingSeverity.HighSeverity,
SerialisedMessage = JsonConvert.SerializeObject(definition),
ServiceMessage = $"Data failed to be updated within SharePoint - {genEx.Message} - see stacktrace for more information.",
StackTrace = genEx.StackTrace,
TimeGenerated = DateTime.Now,
TimeWritten = DateTime.Now,
};
log.EventLogEntryType = EventLogEntryType.Error;
log.EventID = LoggingSeverity.HighSeverity;
log.Message = JsonConvert.SerializeObject(ei);
AddToLog(log);
return false;
}
AddToLog(log);
return true;
}
else
{
// If a different definition type is passed in throw an appropriate exception.
// This should be caught at runtime only.
throw new TypeLoadException(nameof(SharePointDefinition));
}
}
I checked the code you posted and I see that you are updating the list of objects, in your case listItems, instead of a ListItem in ListItemCollection, in your case listItemCol.
To be more clear, I believe you can try to replace listItems with listItemCol.
For instance, instead of:
listItems[existingItemIndex].ParseAndSetFieldValue(keyValuePair.Key, keyValuePair.Value);
use
listItemCol[existingItemIndex][keyValuePair.Key] = keyValuePair.Value;
So I went away and re-read a lot of documentation and looked over some examples, and I wondered why the exception handler never returned errors even though it's obvious there were issues with the query being executed. If you look at the Microsoft documentation and their example here you'll see that they don't show you how to use exceptionScopeSubmit.HasException component of the exception scope object. Which lead to a really stupid assumption on my part. It turns out that the exception block runs on the SharePoint server and should be used to fix issues you might have caused during an expected exception in your query.
Not only that but wrapping the ExecuteQuery in the try catch is redundant when using an exception scope as it means that method will no longer throw an exception. You actually have to assess exceptionScopeSubmit.HasException after the execution to pull back some more detailing information on errors reported by the SharePoint server side execution of your query.
So now I'm using it as below, and I can get detailed error information without having to do some stupid manual debugging which would easily take me hours to track down silly issues. So in case anyone stumbles across this having the same issues, I hope it helps.
ExceptionHandlingScope exceptionScopeFetch = new ExceptionHandlingScope(clientContext);
// variables.
IntegrationEventLog log = new IntegrationEventLog();
EventInformation ei = new EventInformation();
List list = null;
ListItemCollection listItemCol = null;
using (exceptionScopeFetch.StartScope())
{
using (exceptionScopeFetch.StartTry())
{
// get the required list
list = clientContext.Web.Lists.GetByTitle(definition.ListName);
// create query
listItemCol = list.GetItems(CamlQuery.CreateAllItemsQuery());
// set these items for retrieval.
clientContext.Load(listItemCol);
}
using (exceptionScopeFetch.StartCatch())
{
//
}
using (exceptionScopeFetch.StartFinally())
{
//
}
}
// get item instances first
clientContext.ExecuteQuery();
if (exceptionScopeSubmit.HasException)
{
ei = new EventInformation()
{
LoggingEventType = LoggingEventType.DataSentFailure,
LoggingSeverity = LoggingSeverity.HighSeverity,
SerialisedMessage = JsonConvert.SerializeObject(definition),
ServiceMessage = $"Error at SharePoint exception handler during submit to list: Message: {exceptionScopeFetch.ErrorMessage}",
StackTrace = exceptionScopeFetch.ServerStackTrace,
TimeGenerated = DateTime.Now,
TimeWritten = DateTime.Now,
};
log.EventLogEntryType = EventLogEntryType.Error;
log.EventID = LoggingSeverity.HighSeverity;
log.Message = JsonConvert.SerializeObject(ei);
AddToLog(log);
}

Value cannot be null. Parameter name: first

When I run the code below, I get the following error:
Value cannot be null. Parameter name: first
Here is my code:
private async void CallWebApiDetails()
{
WebApiService oWS = new WebApiService();
lstLocalDBAlertsID = new List<string>();
try
{
ErrorHandle err = await oWS.GetAllAlerts();
var lstdistinct = err.lstServerAlertsIDs.Except(lstLocalDBAlertsID).ToList();
if (lstdistinct != null)
{
var lstOrderedLst = lstdistinct.OrderBy(i => i).ToList();
if (lstOrderedLst.Count > 0)
{
for (int i = 0; i < lstOrderedLst.Count; i++)
{
AlertData oAlertData = new AlertData();
oAlertData.Where.AlertId.Value = lstOrderedLst[i];
if (!oAlertData.Query.Load())
{
ErrorHandle oErr = new ErrorHandle();
oErr = await oWS.getSpecificAlert(lstOrderedLst[i]);
await SaveAlertData(Convert.ToInt32(lstOrderedLst[i]), oErr);
}
}
}
}
}
catch (Exception ex)
{
LogError oLE = new LogError();
oLE.logEx(ex, "CallWebApiDetails");
}
}
Can somebody tell me what's wrong with my code?
The Except extension method has first as the this parameter; it is defined as
public static IEnumerable<TSource> Except<TSource>(
this IEnumerable<TSource> first, IEnumerable<TSource> second)
{
if (first == null)
{
throw Error.ArgumentNull("first");
}
// ...
}
so presumably in this line:
var lstdistinct = err.lstServerAlertsIDs.Except(lstLocalDBAlertsID).ToList()
the value of err.lstServerAlertsIDs is null. So: fix that.
Note: it is a good idea to get familiar with using the debugger with breakpoints or at least the stack trace to identify which line is failing. You can't always (or even often) infer the context like this.

CodeFluent vs Interop.MSScriptControl.dll

We had a 32 bits service that we are trying to migrate to 64 bits.
We were using Interop.MSScriptControl.dll to evaluate vb script written by users.
Since there is no 64 bits version of the MSScriptControl. I created a process that was called inside the service. Each time that we need to evaluate users scripts, we call the process. After trying this solution, I found it really slow.
I just discovered the CodeFluentRuntimeClient library that can evaluate vb script as well as JavaScript. However, the way it evaluates the script is complety different from MSScriptControl library.
I created a simple test program to evaluate the old vb script wrote by users.
public class VBScriptEvaluator
{
public static dynamic Evaluate(string key, string script, IDictionary<string, object> parameterValuePair)
{
try
{
using (ScriptEngine engine = new ScriptEngine(ScriptEngine.VBScriptLanguage))
{
ParsedScript parsed = engine.Parse(string.Format(#"Function {0}()
{1}
End Function", key, script));
if (script.Contains("NecUserProfile"))
engine.SetNamedItem("NecUserProfile", #"" + "ADMIN" + #""); //Hardcoded For now
if (parameterValuePair != null && parameterValuePair.Count > 0)
{
foreach (var para in parameterValuePair)
engine.SetNamedItem(para.Key, para.Value);
}
dynamic value = parsed.CallMethod(key);
return (value != null) ? value.ToString() : string.Empty;
}
}
catch (Exception ex)
{
throw;
}
}
}
If I use like this, it's working fine:
static void Main(string[] args)
{
string key = "necGlobalValue";
string script = #"necGlobalValue = ""ADMIN""";
var result = VBScriptEvaluator.Evaluate(key, script, null); //ADMIN
}
Like this it works well too:
static void Main(string[] args)
{
Dictionary<string, object> parameterValuePair = new Dictionary<string, object>();
parameterValuePair.Add("ZINVOICE_MARGIN_0", 141615427.8);
parameterValuePair.Add("ZINVOICE_AMTNOTLIN_0", 187260276.84);
var script = #"If (ZINVOICE_AMTNOTLIN_0) <> 0 Then
SERVER_FLD0000001 = Abs(ZINVOICE_MARGIN_0) / ZINVOICE_AMTNOTLIN_0
else
SERVER_FLD0000001 = 0
End If";
var key = "SERVER_FLD0000001";
var result = VBScriptEvaluator.Evaluate(key, script, parameterValuePair);
}
In the previous library it was detecting automatically the type of the variables that will be evaluated. I can pass integers as string and it will work just fine.
If I replace the value of the dictionary like by using the ScripEngine, it will fail:
Dictionary<string, object> parameterValuePair = new Dictionary<string, object>();
parameterValuePair.Add("ZINVOICE_MARGIN_0", "141615427.8");
parameterValuePair.Add("ZINVOICE_AMTNOTLIN_0", "187260276.84");
Also, If I do this I'm not getting the user ADMIN.
string key = "necGlobalValue";
string script = #"necGlobalValue = ""NecUserProfile""";
var result = VBScriptEvaluator.Evaluate(key, script, null); // output NecUserProfile instead of ADMIN
And BTW I tried to give as much details, that's why the question is that long.
I made it work by passing the parameters to the function instead of using the SetNamedItem function.
public class VBScriptEvaluator
{
public static dynamic Evaluate(string key, string script, IDictionary<string, object> parameterValuePair = null)
{
try
{
using (ScriptEngine engine = new ScriptEngine(ScriptEngine.VBScriptLanguage))
{
List<object> parameters = new List<object>() { "ADMIN" };
string extraParameters = string.Empty;
if (parameterValuePair != null && parameterValuePair.Count > 0)
{
extraParameters = "," + string.Join(",", parameterValuePair.Select(e => e.Key));
foreach (var para in parameterValuePair)
parameters.Add(para.Value);
}
string parsedScript = string.Format(#"Function {0}(NecUserProfile {2})
{1}
End Function", key, script, extraParameters);
ParsedScript parsed = engine.Parse(parsedScript);
dynamic value = parsed.CallMethod(key, parameters.ToArray());
return (value != null) ? value.ToString() : string.Empty;
}
}
catch (Exception ex)
{
throw;
}
}
}
And here's how to use it:
Dictionary<string, object> parameterValuePair = new Dictionary<string, object>()
{
{"Param1", 100.0 },
{"Param2", 10.0}
};
var script = #"If (Param2) <> 0 Then
result = Param1 + Param2
else
result = 1 + 2
End If";
var key = "result";
var result = VBScriptEvaluator.Evaluate(key, script, parameterValuePair); // output 110

C# and Reflection don't work twice in a row

I've a problem and I can't figured it out how to solve it.
I've a class for fetching data from a Database, in this class I've a method for a simple select * this method is called
List<T> All<T>(string tableName)
and you have to specify which resource you want to fetch, for example
All<User>("users")
And, aside from the classic SQL Reader and SQL Command, the core of the method is this
public override List<T> All<T>(string resource)
{
List<T> result = new List<T>();
using (MySqlConnection sqlConnection = new MySqlConnection(connectionString))
{
sqlConnection.Open();
try
{
string query = "SELECT * FROM " + resource + " WHERE 1=1";
using (MySqlCommand sqlCommand = new MySqlCommand(query, sqlConnection))
{
lock (locker)
{
MySqlDataReader reader = sqlCommand.ExecuteReader();
if (reader.HasRows)
{
while (reader.Read())
{
T model = Activator.CreateInstance<T>();
Dictionary<string, object> _properties = new Dictionary<string, object>();
for (int i = 0; i < reader.FieldCount; i++)
{
string property = reader.GetName(i);
object value = reader.GetValue(i);
_properties.Add(property, value);
}
var type = model.GetType();
var method = type.GetMethod("SetProperties");
var invoked = method.Invoke(model, new object[] { _properties });
result.Add(model);
}
}
reader.Close();
}
}
}
catch (Exception ex)
{
Program.eventLogger.Add(new Event(EventType.Error, "SQL Data Providers", "Exception catched on All", ex));
}
finally
{
sqlConnection.Close();
}
}
return result;
}
Basically, based on the Type from the method header, the method will try to create an new instance of the specific type, later for each field from the query, it will fills all the attributes of the class on a temporaneous list. Once it's done it will try to call the method "SetProperties" which basically set every attributes of the class using reflection.
This is the core of SetProperties, equal for each entity:
public virtual bool SetProperties(Dictionary<string,object> properties)
{
if(this.ValidateData(properties))
{
FillNullableAttributes(properties);
// Iterate trough every key : value pairs in properties
foreach (KeyValuePair<string, object> kvp in properties)
{
if (this.data.Contains(kvp.Key))
{
var property = this.GetType().GetProperty(kvp.Key);
PropertyInfo propertyInfo = this.GetType().GetProperty(kvp.Key);
// Set the current fetched key with the given value if !null
if (kvp.Value != null)
{
Type fetchedType = Nullable.GetUnderlyingType(property.PropertyType) ?? property.PropertyType;
object safeConversion = (kvp.Value == null || kvp.Value == DBNull.Value) ? null : Convert.ChangeType(kvp.Value, fetchedType);
if (propertyInfo.CanWrite)
{
propertyInfo.SetValue(this, safeConversion, null);
}
}
}
}
return true;
}
return false;
}
In conclusion the result, which is a list, will be returned and the specific Entity will have its own BindingList filled. The binding list, for each entity is described as follow:
public static BindingList<Seller> items = new BindingList<Seller>();
This code works fine, even if there's a lot of space for improvements I know, but if I called it twice like this:
User.items = new BindingList<User>(provider.All<User>("users"));
User.items = new BindingList<User>(provider.All<User>("users"));
The second list will be filled by empty entities, the counting of the will be correct but they will be empties... and that shouldn't occurs.
The only thing that I figured it out, from the debugging, is that on the second call
var invoked = method.Invoke(model, new object[] { _properties });
invoked is set to false.
The result of:
var invoked = method.Invoke(model, new object[] { _properties });
Is the return value from your SetProperties method, not whether the method was invoked as your question indicates. Your SetProperties method is telling you that it was unable to do its work, debug that and you will find your answer.

Categories

Resources