I have this class:
public class PlaylistMessageBindingModel
{
//Other non-important fields
public Decimal Duration { get; set; }
}
I am instantiating an object of this class and read values out of a database with a data reader:
while (innerReader.Read())
{
var playlistMsg = new PlaylistMessageBindingModel();
playlistMsg.Duration = (Decimal)reader["size_time"];
}
But when it hits the .Duration line of code, it throws an Exception:
An exception of type 'System.Exception' occurred in MyDll.dll but was not handled in user code
Additional information: size_time
The value column in the database is decimal(6, 3). I'm guessing it might have to do with the fact that the value coming out of the database is 0.000, but if that is the case, I'm not sure how to deal with that. I'd appreciate any observations.
Have you tried this?
while (innerReader.Read())
{
var playlistMsg = new PlaylistMessageBindingModel();
playlistMsg.Duration = reader.GetDecimal["size_time"];
}
Also very useful SQL Server Data Type Mappings
This is what happens when you stare at your own code for too long...
I failed to notice a subtle difference in my reader objects. Above this section of code, I have instantiated a reader called reader. Further down the code, I instantiated innerReader. So when I was trying to set the duration, I have while (innerReader.Read()) and inside that code block I'm trying playlistMsg.Duration = (Decimal)reader["size_time"]; but it should be (Decimal)innerReader["size_time"]. After changing the code, I got my intended result.
Related
I have created a SSIS custom data flow component that performs the simple task of converting dates from a COBOL mainframe date types, that are in the format of CYYMMDD, to a SQL Server supported format of YYYYMMDD. There are error rows because the incoming COBOL formatted dates can be invalid dates (i.e., 2-29-2015, 4-31-2016, 9-31-2010, etc). These bad dates are from user derived input fields that do not have a date mask on them and I cannot add a date mask because the application belongs to a 3rd party data vendor.
My problem:
The custom component will not re-direct error rows. I found a posting on MSDN that explains how to re-direct error rows:
https://msdn.microsoft.com/en-us/library/ms136009.aspx
I used this code as a guide and modified it to suit my needs of being able to execute over multiple selected input columns (MS's example only contemplates one input column). When I execute the process, I get the following two errors:
[ConvertCobolDates [2]] Error: System.ArgumentException: Value does not fall within the expected range.
at Microsoft.SqlServer.Dts.Pipeline.Wrapper.IDTSBuffer100.DirectErrorRow(Int32 hRow, Int32 lOutputID, Int32 lErrorCode, Int32 lErrorColumn)
at Microsoft.SqlServer.Dts.Pipeline.PipelineBuffer.DirectErrorRow(Int32 outputID, Int32 errorCode, Int32 errorColumn)
at SSIS.Convert.CobolDate.DataFlow.ConvertCobolDateDataFlow.ProcessInput(Int32 inputID, PipelineBuffer buffer)
at Microsoft.SqlServer.Dts.Pipeline.ManagedComponentHost.HostProcessInput(IDTSManagedComponentWrapper100 wrapper, Int32 inputID, IDTSBuffer100 pDTSBuffer, IntPtr bufferWirePacket)
[SSIS.Pipeline] Error: SSIS Error Code DTS_E_PROCESSINPUTFAILED. The ProcessInput method on component "ConvertCobolDates" (2) failed with error code 0x80070057 while processing input "Input" (4). The identified component returned an error from the ProcessInput method. The error is specific to the component, but the error is fatal and will cause the Data Flow task to stop running. There may be error messages posted before this with more information about the failure.
Also, I don't know if I am missing code to specifically redirect the output or if the error handling is being done incorrectly - it does not show up in the Configure Error Output screen. Any assistance is greatly appreciated!
enter image description here
Note: I suspect the error is in one of the following places: ProvideComponentProperties or ProcessInput.
public override void ProvideComponentProperties()
{
try
{
// Perform the base class' method
base.ProvideComponentProperties();
// Start out clean, remove anything put on by the base class
base.RemoveAllInputsOutputsAndCustomProperties();
// Set component information
ComponentMetaData.Name = "ConvertCobolDates";
ComponentMetaData.Description = "Data Flow task that converts COBOL date types into SQL Server Date types for each row flowing through the component.";
ComponentMetaData.ContactInfo = "Contact Info.";
ComponentMetaData.UsesDispositions = true; // As a rule, components should support error dispositions - they make it easier to troubleshoot problems with the data
// Create input objects. This allows the custom component to have a 'Success' input data flow line
IDTSInput100 input = ComponentMetaData.InputCollection.New();
input.Name = "Input";
input.ErrorRowDisposition = DTSRowDisposition.RD_RedirectRow; // Use RD_RedirectRow is ComponentMetaData.UsesDispositions = true. Otherwise, use RD_NotUsed
input.ErrorOrTruncationOperation = "Either a bad date has been detected or an input column(s) has been selected that does not contain dates.";
// Create output objects. This allows the custom component to have a 'Success' output data flow line
IDTSOutput100 output = ComponentMetaData.OutputCollection.New();
output.Name = "Output";
output.SynchronousInputID = input.ID; //Synchronous transformation
output.ExclusionGroup = 1;
// Create output objects. This allows the custom component to have a 'Error' output data flow line
IDTSOutput100 errorOutput = ComponentMetaData.OutputCollection.New();
errorOutput.IsErrorOut = true;
errorOutput.Name = "ErrorOutput";
errorOutput.SynchronousInputID = input.ID;
errorOutput.ExclusionGroup = 1;
}
catch (Exception ex)
{
bool bolCancel = false;
ComponentMetaData.FireError(0, ComponentMetaData.Name, ex.Message, "", 0, out bolCancel);
throw;
}
}
public override void ProcessInput(int inputID, PipelineBuffer buffer)
{
IDTSInput100 input = ComponentMetaData.InputCollection.GetObjectByID(inputID);
// This code assumes the component has two outputs, one the default,
// the other the error output. If the intErrorOutputIndex returned from GetErrorOutputInfo
// is 0, then the default output is the second output in the collection.
int intDefaultOutputID = -1;
int intErrorOutputID = -1;
int intErrorOutputIndex = -1;
int intErrorColumnIndex = -1;
bool bolValidDate = false;
GetErrorOutputInfo(ref intErrorOutputID, ref intErrorOutputIndex);
if (intErrorOutputIndex == 0)
intDefaultOutputID = ComponentMetaData.OutputCollection[1].ID;
else
intDefaultOutputID = ComponentMetaData.OutputCollection[0].ID;
// Process each incoming row
while (buffer.NextRow())
{
try
{
for (int i = 0; i < inputBufferColumnIndex.Length; i++)
{
if (!buffer.IsNull(inputBufferColumnIndex[i]))
{
// Get the name of the current column that is being processed
string strColName = this.ComponentMetaData.InputCollection[0].InputColumnCollection[i].Name;
// Get the current row number that is being processed
int intCurRow = buffer.CurrentRow + 2; // Buffer.CurrentRow is zero bounded and the first row is a header row, which is skipped. Adjust by two to account for this
// Ideally, your code should detect potential exceptions before they occur, rather
// than having a generic try/catch block such as this. However, because the error or truncation implementation is specific to each component,
// this sample focuses on actually directing the row, and not a single error or truncation.
// Get the ID of the PipelineBuffer column that may cause an error. This is required for redirecting error rows
intErrorColumnIndex = this.ComponentMetaData.InputCollection[0].InputColumnCollection[i].ID;
string strCobolDate = buffer.GetString(inputBufferColumnIndex[i]);
string strConvertedCobolDate = ConvertCobolDate(strCobolDate, strColName, intCurRow);
DateTime dtConvertedSQLDate;
// Validate that the date is correct. This detects bad dates (e.g., 2-30-2016, 4-31-2015, etc.) that are inputted from the user
// Throw an error if the date is bad
bolValidDate = DateTime.TryParse(strConvertedCobolDate, out dtConvertedSQLDate);
if (!bolValidDate)
{
// Validation failed, throw an exception and redirect the error row
throw new Exception();
}
else if (bolValidDate)
{
// validation passed. Direct the column back to its corresponding row within the pipeline buffer
buffer[inputBufferColumnIndex[i]] = dtConvertedSQLDate.ToShortDateString();
}
}
}
// Unless an exception occurs, direct the row to the default
buffer.DirectRow(intDefaultOutputID);
}
catch(Exception)
{
// Has the user specified to redirect the row?
if (input.ErrorRowDisposition == DTSRowDisposition.RD_RedirectRow)
{
// Yes, direct the row to the error output.
buffer.DirectErrorRow(intErrorOutputID, 0, intErrorColumnIndex);
}
else if (input.ErrorRowDisposition == DTSRowDisposition.RD_FailComponent || input.ErrorRowDisposition == DTSRowDisposition.RD_NotUsed)
{
// No, the user specified to fail the component, or the error row disposition was not set.
throw new Exception("An error occurred, and the DTSRowDisposition is either not set, or is set to fail component.");
}
else
{
// No, the user specified to ignore the failure so direct the row to the default output.
buffer.DirectRow(intDefaultOutputID);
}
}
}
}
After some pain staking research, and help from a friend, the problem has been identified - the errorCode of 0 (specified on the MSDN article I previosuly posted) that is being passed into the DirectErrorRow function is incorrect [it is actually a negative number (in this case: -1071628258)]. This was a difficult bug to fix because the compiler was outputting a generic out of bounds error without specifying both the argument and value that was out of bounds (see below).
'System.ArgumentException: Value does not fall within the expected range.' ... message truncated to reduce post length
I thought that the compiler error was referring to the actual bad date that it was unable to convert and so I spent all of my time focusing on the intErrorColumnIndex, which the MSDN article lists as:
// TODO: Add code to include the intErrorColumnIndex.
I assumed that the errorCode of 0 that was provided by Microsoft was correct. On a hunch, my friend said to try retrieving the actual error code and that worked! Thus, the errorCode is probably bounded somewhere between negative infinity to -1. Microsoft's MSDN article on directing error rows needs to be corrected.
Me: 1
Microsoft: 0
The solution is as follows in the catch block:
catch(Exception)
{
// Has the user specified to redirect the row?
if (input.ErrorRowDisposition == DTSRowDisposition.RD_RedirectRow)
{
// Yes, get the error code
int DTS_E_ERRORTRIGGEREDREDIRECTION = -1;
unchecked
{
DTS_E_ERRORTRIGGEREDREDIRECTION = (int)0xC020401E;
}
// Direct the row to the error output
buffer.DirectErrorRow(intErrorOutputID, DTS_E_ERRORTRIGGEREDREDIRECTION, intErrorColumnIndex);
}
else if (input.ErrorRowDisposition == DTSRowDisposition.RD_FailComponent || input.ErrorRowDisposition == DTSRowDisposition.RD_NotUsed)
{
// No, the user specified to fail the component, or the error row disposition was not set
throw new Exception("An error occurred, and the DTSRowDisposition is either not set or is set to fail component.");
}
else
{
// No, the user specified to ignore the failure so direct the row to the default output
buffer.DirectRow(intDefaultOutputID);
}
}
I have been searching for an answer to this for a couple of days now and I'm completely stuck. I am assuming its just that I am not looking for the right thing as I'm sure the solution is simple and it is just something I have done wrong.
For some reason when I try to get data from the database using Entity Framework like this code below, nothing happens. When I stepped through the code I found that it was getting to the line that gets the data from the database and then just jumped out of the function, no errors, nothing, the application just carries on running, but I don't get my data.
private void CreateKitWindow_Loaded(object sender, RoutedEventArgs e)
{
string kitID = "";
using (MyContext foo = new MyContext())
{
foo.Database.Log = s => System.Diagnostics.Debug.WriteLine(s);
LaminationKit theKit = foo.Lamination_Kit.OrderByDescending(x => x.KitNumber).First();
kitID = theKit.KitNumber;
}
kitNumber.Content = kitID;
}
I found out about the database log line here on Stack Overflow and added it and inspected the results.
Opened connection at 2/12/2015 2:26:22 p.m. +13:00
SELECT TOP (1)
[Extent1].[UID] AS [UID],
[Extent1].[KitNumber] AS [KitNumber],
[Extent1].[dateCreated] AS [dateCreated],
[Extent1].[lastTransaction] AS [lastTransaction],
[Extent1].[daysLeft] AS [daysLeft],
[Extent1].[hoursLeft] AS [hoursLeft],
[Extent1].[minsLeft] AS [minsLeft],
[Extent1].[jobNo] AS [jobNo],
[Extent1].[assemblyNo] AS [assemblyNo],
[Extent1].[storageLocation] AS [storageLocation],
[Extent1].[jobName] AS [jobName],
[Extent1].[jobDescription] AS [jobDescription],
[Extent1].[mouldNumber] AS [mouldNumber],
[Extent1].[targetJob] AS [targetJob],
[Extent1].[targetAssembly] AS [targetAssembly],
[Extent1].[targetLine] AS [targetLine],
[Extent1].[dateFrozen] AS [dateFrozen],
[Extent1].[dateThawed] AS [dateThawed],
[Extent1].[cookDate] AS [cookDate]
FROM [dbo].[LaminationKit] AS [Extent1]
ORDER BY [Extent1].[KitNumber] DESC
-- Executing at 2/12/2015 2:26:22 p.m. +13:00
-- Completed in 5 ms with result: SqlDataReader
Exception thrown: 'System.InvalidOperationException' in EntityFramework.dll
Closed connection at 2/12/2015 2:26:22 p.m. +13:00
Can anyone tell me what is happening here? The query is valid and according to this, completed successfully, but then throws an exception.
I pasted that exact query into SQL server management studio and it runs fine and does exactly what I want.
Here is the context if that helps (extra stuff removed)
class MyContext : DbContext
{
public MyContext() : base("Password=xxxxxxx;Persist Security Info=True;User ID=xxxxxxxx;Data Source=ssnzsql02;Packet Size=4096;Initial Catalog=SS_Outlife_E10")
{
}
public DbSet<LaminationKit> Lamination_Kit { get; set; }
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
modelBuilder.Conventions.Remove<PluralizingTableNameConvention>();
}
}
The other thing is that I can get data using the longer
var foo = from item in context.table
where item.this == something
Orderby item.that
select item
foreach(var t in foo)
{
//loop through and do stuff
}
Method just fine, but I don't want to do this in every case.
The issue is not just on this table, its happened on lots of different tables in the database.
Thank you cFrozenDeath and FizzBuzz, i don't know why i didn't think to try this in the first place. (maybe because no actual crash or break was happening during run time)
I threw a try / catch round it to see if it would catch it and i get this message
The 'dateFrozen' property on 'LaminationKit' could not be set to a 'null' value. You must set this property to a non-null value of type 'System.DateTime'.
So turns out it was super simple and i just derped, i need to make sure that i set my data types to nullable for table fields that allow null values #EpicFacepalm
changing
public DateTime dateFrozen { get; set; }
to
public Nullable<DateTime> dateFrozen { get; set; }
solved it.
Now to go through everything and fix all the others.
I have a MVC Web Application using the following approach:
public class MyController : Controller
{
public FooRepository fooRepository = new FooRepository();
public BarRepository barRepository = new BarRepository();
public ActionResult UpdateItems(int id, int range1, int range2)
{
Foo foo = fooRepository.GetItem(id);
List<Bar> bars = barRepository.GetItemsByRange(range1, range2);
// Some validation rules here...
DoSomeWork(foo, bars);
// Show confirmation / error message
}
private void DoSomeWork(Foo foo, List<Bar> bars)
{
foreach(int i = 0; i < bars.Count; i++)
{
bars[i].Prop1 = foo.Prop1; // This field is updated
bars[i].Owner = "someuser"; // This one too
bars[i].Status = BarStatus.SomeStatus; // This isn't...
}
foo.Status = FooStatus.SomeStatus; // Ok
// Calls DataContext.SubmitChanges()
fooRepository.SubmitChanges();
barRepository.SubmitChanges();
}
}
However, in some "random" cases (I see no pattern), one of the fields doesn't get updated, as noted in the comments.
It seems like LINQ isn't recognizing the field's update, so it gets excluded from the generated query.
Can anyone tell me if I'm missing something here, what could be causing it and/or how can I solve it?
Note: I don't get any Exception and can't verify this case in a development scenario.
From my experience if the error is random and you can't reproduce in development than the problem is user error.
Programming would be really hard if the .net framework or the CLR just randomly decided to do things differently.
You probably have an implicit/explicit bind exclusion floating around somewhere
[Bind(Exclude="...,Status,...")]
Just guessing of course
If Linq thinks that the Status is already BarStatus.SomeStatus, then it won't update it.
What can happen is that you find a record with the status set to this value, and then some other routine changes it, and then, if you are using your same DataContext, you will get the old value from the cached copy and hence Linq thinks that it does not need to update it.
I have a DAL class library that is included in my program as a DLL. The below line is from the DAL to initialize the connection.
DataSet ds = new DataSet("table");
SqlConnection cnn = new SqlConnection(Settings.CMOSQLConn);
When I run this I get the below error:
An unhandled exception of type 'System.StackOverflowException' occurred in CMO.DAL.dll
The below is in the Settings.Designer.cs file and it is where it shows the error on the get call:
[global::System.Configuration.ApplicationScopedSettingAttribute()]
[global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
[global::System.Configuration.SpecialSettingAttribute(global::System.Configuration.SpecialSetting.ConnectionString)]
[global::System.Configuration.DefaultSettingValueAttribute("Data Source=WWCSTAGE;Initial Catalog=CMO;Persist Security Info=True;User ID=CMOWe" +
"bService;Password=ecivreSbeWOMC")]
public static string CMOSQLConn {
get {
return (CMOSQLConn);
}
}
Anyone have any ideas of what to look for? Is it because the connection string is stored in the dll instead of my Main App? I am really stuck on this and will greatly appreciate any help!
EDIT 1
I tried Greg's suggestion below:
public static string CMOSQLConn {
get {
return (Settings.CMOSQLConn);
}
}
And I still get the same error... Any more thoughts? Thanks so far!
EDIT 2
So I followed the suggestion of regenerating the settings file below and now my setting file looks like this -->
public string CMOSQLConn {
get {
return ((string)(this["CMOSQLConn"]));
}
}
Unfortunately this won't compile now as wherever I have this statement -->
SqlConnection cnn = new SqlConnection(Settings.CMOSQLConn);
I now get this error -->
Error 1 An object reference is required for the non-static field, method, or property 'CMO.DAL.Properties.Settings.CMOSQLConn.get' B:\MyDocs\tmpPATRIOT\Projects\VS2008\DisConnectDAL\CMO.DAL\SupportWorker.cs 13 51 CMO.DAL
Is this what I should expect?
Thanks!
This is a classic c# properties mistake. Double check what you're returning in your property-- you're returning the property itself! Name resolution will prefer the local name over an external name. You're getting a stack overflow because you hit infinite recursion when CMOSQLConn.get calls CMOSQLConn.get.
Consider returning Settings.CMOSQLConn. The extra specification should clearly indicate the correct location of your connection string.
EDIT:
Whoops! I didn't notice that you pasted that from your Settings designer file. The infinite recursion is clearly happening, but I'm afraid you'll have to do some more investigation to track down why it's happening in this case.
It appears that your designer file was generated incorrectly (!!!). On VS2008, my settings designer getters look something like:
public bool Foo{
get {
return ((bool)(this["Foo"]));
}
// ...
}
You may need to do something similar. IE:
public string CMOSQLConn
get {
return ((string)(this["CMOSQLConn"]));
}
// ...
}
Try changing your code to this:
public static string CMOSQLConn {
get {
return ((string)(this["CMOSQLConn"]));
}
}
Hmm.. Good point in the comments. I just looked in my VS settings file and copied and pasted without thinking. Something isn't right with your settings file... It shouldn't be creating a static property for the settings.
When dealing with custom exceptions, I usually inherit from Exception and then add some fields/properties to my exception class to store some additional info:
public class MyException : Exception
{
public int ErrorCode{get;set;}
public MyException()
{}
}
In the above example, the ErrorCode value is stored in the exception, meaning that I have to add it to and retireve if from the SerializationInfo object in the protected constructor and the overridden GetObjectData method.
The Exception class has a Data property, which according to MSDN:
Gets a collection of key/value pairs that provide additional user-defined information about the exception.
If I store the error code inside the Data, it will get serialised for me by the Exception class (according to Reflector), meaning that my exception class now looks like:
public class MyException : Exception
{
public int ErrorCode
{
get {return (int) Data["ErrorCode"];}
set {Data["ErrorCode"] = value;}
}
public MyException()
{}
}
This means that whilst there is a bit more work to do in dealing with the get/set of the error code (like dealing with casting errors and situations where the error code might not be in the dictionary), I don't have to worry about serialising/deserialising it.
Is this just two different ways of achieving the same thing, or does one way have any clear advantage(s) over the other (apart from those I've already mentioned)?
If you are bothering to create your own exception, you don't need the Data property. Data comes in useful when you want to store a bit of extra information in an existing exception class, but don't want to create your own custom exception class.
I would avoid using Data as it is not under your control e.g. some code somewhere might decide to overwrite the "ErrorCode" value. Instead use the propery and implement serialization. I use the following code to test all my custom exceptions to make sure I've implemented them properly.
public static void TestCustomException<T>() where T : Exception
{
var t = typeof(T);
//Custom exceptions should have the following 3 constructors
var e1 = (T)Activator.CreateInstance(t, null);
const string message = "message";
var e2 = (T)Activator.CreateInstance(t, message);
Assert.AreEqual(message, e2.Message);
var innerEx = new Exception("inner Exception");
var e3 = (T)Activator.CreateInstance(t, message, innerEx);
Assert.AreEqual(message, e3.Message);
Assert.AreEqual(innerEx, e3.InnerException);
//They should also be serializable
var stream = new MemoryStream();
var formatter = new BinaryFormatter();
formatter.Serialize(stream, e3);
stream.Flush();
stream.Position = 0;
var e4 = (T)formatter.Deserialize(stream);
Assert.AreEqual(message, e4.Message);
Assert.AreEqual(innerEx.ToString(), e4.InnerException.ToString());
}
Microsoft's own guidelines:
Do make exceptions serializable. An exception must be serializable to work correctly across application domain and remoting boundaries.
I would store it in the Data-property which sadly would let outside code modify the value without consent, or use solution 1 (in your example) but make it serializeable. In the end I would probably go for solution 1 so I can be sure that the value never changes.
You should go with the first solution. I can't see much value in the Data property, unless you plan to raise row Exception instances with attached infos.
If you have your own Exception type, then use properties instead: it's more clean and safe.