|
Ya I guess this is all for nothing
If it's not broken, fix it until it is.
Everything makes sense in someone's mind.
Ya can't fix stupid.
|
|
|
|
|
I'm curious how you guys do exception handling in your DAL.
My Apps are usually structured like this... The Client (this case a WPF app) calls the BL, which then calls the DAL:
WPF App
private async void AddUser()
{
UserEntity user = new UserEntity
{
FirstName = "Jack",
LastName = "Stone",
UserName = "jstone",
Password = ""
};
await Task.Factory.StartNew(() =>
{
IBizObj bo = new BizObj(connString);
bo.AddUser(user);
}).ContinueWith(task =>
{
UserList.Add(user);
});
}
Bis Layer
public class BizObj : IBizObj
{
IRepository _repoisitory;
<pre>
public BizObj(string connectionString)
{
_repoisitory = new FalconRepository(connectionString);
}
public int AddUser(UserEntity entity)
{
return _repoisitory.AddUser(entity);
}
}
DAL
public int AddUser(UserEntity entity)
{
using (var dc = GetDataContext())
{
int results = 0;
try
{
User user = new User
{
FirstName = entity.FirstName,
LastName = entity.LastName,
UserName = entity.UserName,
Password = entity.Password,
CanLogIn = entity.CanLogIn,
PasswordChangeDate = entity.PasswordChangeDate,
CreatedByUserId = 1,
CreatedDT = DateTime.Now,
};
dc.Users.InsertOnSubmit(user);
dc.SubmitChanges();
results = user.Id;
}
catch (Exception e)
{
}
return results;
}
}
So, the question is wwhat is the correct way to handle the exception on the DAL? Would you handle the exception in the UI, the DAL, both?
If it's not broken, fix it until it is.
Everything makes sense in someone's mind.
Ya can't fix stupid.
|
|
|
|
|
The general rule of thumb is that you should only handle exceptions at the point that you can actually do anything with them. In your example, for instance, what good does it do handling the exception in the DAL? What are you going to do with it? Are you going to log that there was an exception and then return a value of 0 to indicate that the request failed for some reason? In that case, you're going to have to have something in your business layer that knows that a result of 0 means that there was an exception internally. In which case, why did you bother handling it in the DAL at all?
|
|
|
|
|
I'd handle it in the DAL, log it in the DAL log with as much detail as possible, and then throw a new exception with the current exception as the inner exception. The BL can handle it or not as it choses, but since there may be multiple DAL layers, it's important (to me at least) to log it correctly for later analysis - and that means as early as possible before you lose potentially useful info, or debug opportunities.
"I have no idea what I did, but I'm taking full credit for it." - ThisOldTony
AntiTwitter: @DalekDave is now a follower!
|
|
|
|
|
If my Try/Catch caught a "File Not Found" exception, the user could probably fix that - ensure the file is there.
But for something like this, an exception when adding a user, what can the user do?? Nothing really. Most likely, the exception is something beyond the ability of the user to fix.
So, again, aside from logging, what "handling" should take place? And where?
If it's not broken, fix it until it is.
Everything makes sense in someone's mind.
Ya can't fix stupid.
|
|
|
|
|
That's up to the BL and / or PL - the DAL can't do anything about telling the user, so logging it is all it can and should do. Passing the exception up gives the higher levels a chance to report / recover / ignore the error.
"I have no idea what I did, but I'm taking full credit for it." - ThisOldTony
AntiTwitter: @DalekDave is now a follower!
|
|
|
|
|
Sorry, I'd have to disagree with you on this one. If you catch and log the exception in the DAL, then let this bubble up to be caught and logged again (this time as an inner exception), you're polluting your exception logs. Effectively you are double logging this exception. If the logging engine uses structured logging (and most do nowadays), then navigating to the DAL log and extracting useful information from it is straightforward. More importantly, you avoid a double exception penalty which could be costly in terms of performance.
|
|
|
|
|
What's the "user" doing while his "user record" is being added "asynchronously" and there is an error in the DAL?
It was only in wine that he laid down no limit for himself, but he did not allow himself to be confused by it.
― Confucian Analects: Rules of Confucius about his food
|
|
|
|
|
That's the point of my question. I don't want to block the UI, but I also want to handle the exception.
If it's not broken, fix it until it is.
Everything makes sense in someone's mind.
Ya can't fix stupid.
|
|
|
|
|
Don't make it async then.
Every "transaction" should return a "return code" to the "caller"; the ui in this case.
Make a "synchronous" call with say a 5 sec. timeout. A timeout is a "no server response".
Don't leave the user hanging; you need to tell them success or failure.
In any event, if it's not a duplicate, etc. it's a "system error" and needs to be escalated.
"Graceful degeneration". If the user CANNOT proceed, these is nothing "graceful" you can do except inform the user.
It was only in wine that he laid down no limit for himself, but he did not allow himself to be confused by it.
― Confucian Analects: Rules of Confucius about his food
|
|
|
|
|
ContinueWith doesn't really belong in an async method.
private async void AddUser()
{
UserEntity user = new UserEntity
{
FirstName = "Jack",
LastName = "Stone",
UserName = "jstone",
Password = ""
};
await Task.Run(() =>
{
IBizObj bo = new BizObj(connString);
bo.AddUser(user);
});
UserList.Add(user);
} Also, you should try to avoid async void methods wherever possible.
Avoid async void methods | You’ve Been Haacked[^]
"These people looked deep within my soul and assigned me a number based on the order in which I joined."
- Homer
|
|
|
|
|
So the method doesn't need to return anything, and I'd like the UI to remain resonsive until the server trip is finished. What's the right approach here?
If it's not broken, fix it until it is.
Everything makes sense in someone's mind.
Ya can't fix stupid.
|
|
|
|
|
The code I posted will push the IBizObj onto a background thread. When it completes, the method will result on the UI thread to run the UserList.Add code. There's no need to use ContinueWith here.
Ideally, your method should return a Task . If you want it to be a "fire and forget" call, assign the returned task to a discard variable:
_ = AddUser(); That way, any unhandled exceptions will raise the TaskScheduler.UnobservedTaskException[^] event.
With an async void method, any unhandled exceptions could bring down the entire process.
"These people looked deep within my soul and assigned me a number based on the order in which I joined."
- Homer
|
|
|
|
|
I have this setup of a list containing dates and strings:
var d0 = new DateTime(2019, 10, 20);
var d1 = new DateTime(2018, 10, 20);
var d2 = new DateTime(2019, 10, 21);
var d3 = new DateTime(2019, 10, 20);
var d4 = new DateTime(2017, 10, 21);
var d5 = new DateTime(2013, 10, 22);
var d6 = new DateTime(2015, 10, 21);
var d7 = new DateTime(2010, 10, 23);
var d8 = new DateTime(1995, 10, 20);
var d9 = new DateTime(2019, 10, 22);
var list = new List<(DateTime Date, string Name)>();
list.Add((d0, "D0"));
list.Add((d1, "D1"));
list.Add((d2, "D2"));
list.Add((d3, "D3"));
list.Add((d4, "D4"));
list.Add((d5, "D5"));
list.Add((d6, "D6"));
list.Add((d7, "D7"));
list.Add((d8, "D8"));
list.Add((d9, "D9")); I want to use LINQ to transform the list in the following order:
(2019-10-20, "D0")
(2018-10-20, "D1")
(2019-10-20, "D3")
(1995-10-20, "D8")
(2019-10-21, "D2")
(2017-10-21, "D4")
(2015-10-21, "D6")
(2013-10-22, "D5")
(2019-10-22, "D9")
(2010-10-23, "D7") Notice the month and day decides the order, not the year.
I have tried to mess around with LINQ's GroupBy() method, but cannot understand how to group dates where months and days are equal.
Also there is a sorting rule:
Notice the "D0"-item comes before the "D1"-item, even if the "D1"-item has year set to 2018 and the "D0"-item has year set to 2019.
|
|
|
|
|
You're not grouping the list; you're sorting it.
With LINQ:
var sortedSequence = list
.OrderBy(p => p.Date.Month)
.ThenBy(p => p.Date.Day)
.ThenBy(p => p.Name); With a custom comparer:
public class MonthDayNameComparer : IComparer<(DateTime date, string name)>
{
public int Compare((DateTime date, string name) left, (DateTime date, string name) right)
{
int result = left.date.Month.CompareTo(right.date.Month);
if (result == 0) result = left.date.Day.CompareTo(right.date.Day);
if (result == 0) result = StringComparer.Ordinal.Compare(left.name, right.name);
return result;
}
}
...
list.Sort(new MonthDayNameComparer());
"These people looked deep within my soul and assigned me a number based on the order in which I joined."
- Homer
|
|
|
|
|
Thank you!
The LINQ example worked just fine!
|
|
|
|
|
Hello @all,
if have a question regarding a ToolTip i created:
private void showTooltip(Control position, string Title, string Message)
{
ToolTip t = new ToolTip();
try
{
t.UseFading = true;
t.UseAnimation = true;
t.IsBalloon = true;
t.ShowAlways = true;
t.AutoPopDelay = 5000;
t.InitialDelay = 500;
t.ReshowDelay = 500;
t.ToolTipTitle = Title;
t.RemoveAll();
t.SetToolTip(position, Message);
}
catch (Exception ex)
{
MessageBox.Show(ex.Message.ToString());
}
}
The Issue is that i have to dispose Tooltip. But if i do the Tooltip will not be shown.
May i ask you for help?
Thank you very much.
|
|
|
|
|
You cannot use, display, or even access a Disposed control - Dispose is there to tell the system that the object is done with, finished, caput: once you call it on an object you will get an error (if you are lucky) when you try to use it again.
Once you call Dispose, you will need to recreate a new instance of the ToolTip if you want to show the same information again.
"I have no idea what I did, but I'm taking full credit for it." - ThisOldTony
AntiTwitter: @DalekDave is now a follower!
|
|
|
|
|
It sort of depends on how you're calling "showTooltip".
You can return a reference to "ToolTip" from your "show" routine and dispose of that (at the right time).
It was only in wine that he laid down no limit for himself, but he did not allow himself to be confused by it.
― Confucian Analects: Rules of Confucius about his food
|
|
|
|
|
Hi,
you seem to be creating several tooltips, one for each control that needs a tip.
That is not what WinForms is suggesting.
The more natural way to use ToolTip class is by creating one ToolTip for a Form, then use that ToolTip for each and every Control on that Form that needs a tip. When you are done with the Form, that is the right moment to dispose of the ToolTip. Look at the example code in ToolTip.SetToolTip(Control, String) Method (System.Windows.Forms) | Microsoft Docs[^].
|
|
|
|
|
I have an interface and a type implementing it:
public interface IValueWithUnit {...}
[Serializable]
public struct DoubleValueWithUnit : IValueWithUnit { ... } At a different place, I have a string with the name of the type, and must find out if the type derives from above interface:
string name = "DoubleValueWithUnit";
Type t;
if (TypeHelper.TryFindType(name, out t))
{
m_IsValueWithUnit = t.IsSubclassOf(typeof(IValueWithUnit));
m_IsValueWithUnit = t.IsSubclassOf(typeof(DoubleValueWithUnit));
m_IsValueWithUnit = t.IsInstanceOfType(typeof(IValueWithUnit));
m_IsValueWithUnit = t.IsInstanceOfType(typeof(DoubleValueWithUnit));
m_IsValueWithUnit = t.IsAssignableFrom(typeof(IValueWithUnit));
m_IsValueWithUnit = t.IsAssignableFrom(typeof(DoubleValueWithUnit));
m_IsValueWithUnit = t.GetInterface(nameof(IValueWithUnit)) != null; The TryFindType function is from c# - Type.GetType("namespace.a.b.ClassName") returns null - Stack Overflow[^].
You see the many "false" comments? DoubleValueWithUnit is neither a subclass nor an instance of DoubleValueWithUnit or IValueWithUnit , and is also not assignable from IValueWithUnit , but at least it is assignable from DoubleValueWithUnit .
I am confused now: my brain threw a TypeOverflowException .
Oh sanctissimi Wilhelmus, Theodorus, et Fredericus!
modified 15-Nov-19 6:47am.
|
|
|
|
|
What is the relation between the name variable, and the m_Setting.Type property?
Here's what I can understand from the results you get:
DoubleValueWithUnit is not a subclass of IValueWithUnit . An interface implementation is not considered as inheritance.DoubleValueWithUnit is not a subclass of DoubleValueWithUnit . It is the very same class.DoubleValueWithUnit is not an instance of IValueWithUnit . It is the type itself, not an instance of the type.DoubleValueWithUnit is not an instance of DoubleValueWithUnit . Same comment as above.DoubleValueWithUnit is not assignable from IValueWithUnit . It implements the interface, but that does not mean that every implementation of the interface should allow to cast to actual type. There could be some properties/methods of the class which are not part of the interface.DoubleValueWithUnit is assignable from DoubleValueWithUnit . This one seems logical.DoubleValueWithUnit implements IValueWithUnit . Thus IValueWithUnit is an element of the list of interfaces implemented by DoubleValueWithUnit .
"Five fruits and vegetables a day? What a joke!
Personally, after the third watermelon, I'm full."
|
|
|
|
|
phil.o wrote: What is the relation between the name variable, and the m_Setting.Type property? Oh, I forgot to replace that; question updated.
Thanks for your explanation!
Oh sanctissimi Wilhelmus, Theodorus, et Fredericus!
|
|
|
|
|
|
Thanks. Good to know that.
Oh sanctissimi Wilhelmus, Theodorus, et Fredericus!
|
|
|
|
|