Register a SA Forums Account here!
JOINING THE SA FORUMS WILL REMOVE THIS BIG AD, THE ANNOYING UNDERLINED ADS, AND STUPID INTERSTITIAL ADS!!!

You can: log in, read the tech support FAQ, or request your lost password. This dumb message (and those ads) will appear on every screen until you register! Get rid of this crap by registering your own SA Forums Account and joining roughly 150,000 Goons, for the one-time price of $9.95! We charge money because it costs us money per month for bills, and since we don't believe in showing ads to our users, we try to make the money back through forum registrations.
 
  • Post
  • Reply
LongSack
Jan 17, 2003

Kinda a weird question, I think

In my ticket-tracking app, a “Ticket” is a unit of work. The Ticket class has a bunch of fields like task, work order, security exception, external vendor ticket, etc. It also has 2 status fields, one for each approval I need (let’s call them the overlords and the underlords).

I want to implement a basic kind of work flow, where the user can enter scripts something like this:
code:
when (OverLordRequest => nonempty) 
{
    OLStatus => WaitingApproval;
    TicketStatus => WaitingOLApproval;
}
And
code:
when (UnderlordRequest added)
{
    TicketStatus => Scheduled;
}
The problem is that statuses are not hard coded, but are entries in a database and can vary from user to user, so that my status “Waiting Approval” might be “Pending” to some other Engineer.

One plus is that only the Ticket object needs to be involved in this scripting, so that simplifies things a bit.

Anything out there that I can use / adapt for this, or should I roll my own?

Adbot
ADBOT LOVES YOU

putin is a cunt
Apr 5, 2007

BOY DO I SURE ENJOY TRASH. THERE'S NOTHING MORE I LOVE THAN TO SIT DOWN IN FRONT OF THE BIG SCREEN AND EAT A BIIIIG STEAMY BOWL OF SHIT. WARNER BROS CAN COME OVER TO MY HOUSE AND ASSFUCK MY MOM WHILE I WATCH AND I WOULD CERTIFY IT FRESH, NO QUESTION
My recommendation would be to come up with a JSON schema to represent these triggers and actions. But this depends how complex these things need to be?

New Yorp New Yorp
Jul 18, 2003

Only in Kenya.
Pillbug

LongSack posted:

Kinda a weird question, I think

In my ticket-tracking app, a “Ticket” is a unit of work. The Ticket class has a bunch of fields like task, work order, security exception, external vendor ticket, etc. It also has 2 status fields, one for each approval I need (let’s call them the overlords and the underlords).

I want to implement a basic kind of work flow, where the user can enter scripts something like this:
code:
when (OverLordRequest => nonempty) 
{
    OLStatus => WaitingApproval;
    TicketStatus => WaitingOLApproval;
}
And
code:
when (UnderlordRequest added)
{
    TicketStatus => Scheduled;
}
The problem is that statuses are not hard coded, but are entries in a database and can vary from user to user, so that my status “Waiting Approval” might be “Pending” to some other Engineer.

One plus is that only the Ticket object needs to be involved in this scripting, so that simplifies things a bit.

Anything out there that I can use / adapt for this, or should I roll my own?

Windows Workflow might be a good fit for this, although I've only used it as an end-user and never as a developer.

mystes
May 31, 2006

If you don't need to handle more complicated situations, it might be easiest to just have drop-down lists to select conditions and actions so you don't need to do parsing.

Rocko Bonaparte
Mar 12, 2002

Every day is Friday!
Let's say I have an event, and I attach a callback to it. That callback attaches another callback to the event. When I fire the event the first time, the second callback doesn't run. Any idea why? Is this an absolute?

I'm actually happy it doesn't, but I was under the impressions events were basically just lists of callbacks, so I had pondered the possibility that adding new callbacks on while previous ones were being run could cause the new ones to get caught up in everything.

New Yorp New Yorp
Jul 18, 2003

Only in Kenya.
Pillbug

Rocko Bonaparte posted:

Let's say I have an event, and I attach a callback to it. That callback attaches another callback to the event. When I fire the event the first time, the second callback doesn't run. Any idea why? Is this an absolute?

I'm actually happy it doesn't, but I was under the impressions events were basically just lists of callbacks, so I had pondered the possibility that adding new callbacks on while previous ones were being run could cause the new ones to get caught up in everything.

Makes sense to me.

https://stackoverflow.com/questions/2582052/event-handlers-not-thread-safe

"...The delegates in play here are immutable. The only problem is the split second before checking if anyone has subscribed and actually calling the event handlers. Once you've started executing those, no background changes will affect the current invocation."

LongSack
Jan 17, 2003

Thanks for the suggestions. Still messing around with it, and I like the idea of selecting items rather than scripting.

Haven't messed around much with reflection, and i'm finding it pretty cool. First stab at a method to detect changes between 2 objects ("before" and "after") (in more lines than needed for debugging purposes):
C# code:
public static bool ObjectChanged(object before, object after)
{
    bool ret = false;
    if (before == null && after == null)
        throw new ArgumentNullException("before and after");
    if (before == null)
        return ObjectAdded(after);
    else if (after == null)
        return ObjectDeleted(before);
    Type t = before.GetType();
    if (t != after.GetType())
        throw new ApplicationException("Before and After are different types");
    var props = t.GetProperties(BindingFlags.Public | BindingFlags.Instance);
    foreach (PropertyInfo prop in props)
    {
        var bv = prop.GetValue(before);
        var av = prop.GetValue(after);
        var cmp = bv.GetType().GetMethod("Equals", new Type[] { av.GetType() });
        if (cmp == null)
            continue;
        bool result = (bool)cmp.Invoke(bv, new object[] { av });
        if (result)
            continue;
        ret = true;
        ObjectUpdated(t, before, after, bv, av, prop.Name);
    }
    return ret;
}

EssOEss
Oct 23, 2006
128-bit approved

Rocko Bonaparte posted:

Any idea why? Is this an absolute?

To put what NYNY said in more basic words: the set of callbacks called is determined the instant the event is raised. Nothing you do to the event after that moment affects what callbacks are called (in non-exceptional cases).

raminasi
Jan 25, 2005

a last drink with no ice
Man, this new private protected thing looks pretty neat! Too bad the /langversion flag appears to not actually affect the language features that are supported!

ljw1004
Jan 18, 2005

rum

LongSack posted:

Haven't messed around much with reflection, and i'm finding it pretty cool.

I bet that was what our first ancestors to discover fire said as well! ...

How about
code:
before.Equals(after)
, and in your custom objects that you want to be compared, you override the Equals method?

Mr. Crow
May 22, 2008

Snap City mayor for life
What's the recommend gut check online test website for c# these days? (From a managers perspective)? Is interview mocha any good?

putin is a cunt
Apr 5, 2007

BOY DO I SURE ENJOY TRASH. THERE'S NOTHING MORE I LOVE THAN TO SIT DOWN IN FRONT OF THE BIG SCREEN AND EAT A BIIIIG STEAMY BOWL OF SHIT. WARNER BROS CAN COME OVER TO MY HOUSE AND ASSFUCK MY MOM WHILE I WATCH AND I WOULD CERTIFY IT FRESH, NO QUESTION

LongSack posted:

(in more lines than needed for debugging purposes)

Thank you for coding in this manner.

LongSack posted:

Thanks for the suggestions. Still messing around with it, and I like the idea of selecting items rather than scripting.

Haven't messed around much with reflection, and i'm finding it pretty cool. First stab at a method to detect changes between 2 objects ("before" and "after") (in more lines than needed for debugging purposes):
C# code:
public static bool ObjectChanged(object before, object after)
{
    bool ret = false;
    if (before == null && after == null)
        throw new ArgumentNullException("before and after");
    if (before == null)
        return ObjectAdded(after);
    else if (after == null)
        return ObjectDeleted(before);
    Type t = before.GetType();
    if (t != after.GetType())
        throw new ApplicationException("Before and After are different types");
    var props = t.GetProperties(BindingFlags.Public | BindingFlags.Instance);
    foreach (PropertyInfo prop in props)
    {
        var bv = prop.GetValue(before);
        var av = prop.GetValue(after);
        var cmp = bv.GetType().GetMethod("Equals", new Type[] { av.GetType() });
        if (cmp == null)
            continue;
        bool result = (bool)cmp.Invoke(bv, new object[] { av });
        if (result)
            continue;
        ret = true;
        ObjectUpdated(t, before, after, bv, av, prop.Name);
    }
    return ret;
}

You could simplify some of this by making the method generic - for example you could eliminate the need to check that the types match by using the following signature:

C# code:
public static bool ObjectChanged<T>(T before, T after)

putin is a cunt fucked around with this message at 23:40 on Jan 31, 2018

LongSack
Jan 17, 2003

ljw1004 posted:

I bet that was what our first ancestors to discover fire said as well! ...

How about
code:
before.Equals(after)
, and in your custom objects that you want to be compared, you override the Equals method?

Yes, they are all observable (derived from NotifyBase) and IEquatable<T>

a hot gujju bhabhi posted:

Thank you for coding in this manner.


You could simplify some of this by making the method generic - for example you could eliminate the need to check that the types match by using the following signature:

C# code:
public static bool ObjectChanged<T>(T before, T after)

It was a neat bit of code, but I’m not going to use it. I realized that I only care about changes to Tickets after creation. So I made a static class with TicketChanged methods (with overloads for string, int, and DateTime fields). These methods determine what event just happened (FieldSet, FieldChanged, FieldCleared, etc.) and fire off an Event with args containing the ticket, event, property name, before and after values.

So I’m successfully capturing all the events I’m interested in. Now how to handle the workflow?

First pass I’m building a class that encapsulates “when event a happens to source property b, perform action c on destination property d with optional data e.

I think this will handle all the stuff I need it to without being unnecessarily complex.

And it fits very well into the multiple combo-box UI scenario.

Munkeymon
Aug 14, 2003

Motherfucker's got an
armor-piercing crowbar! Rigoddamndicu𝜆ous.



Anybody know what the heck to look for when IIS just won't or can't start worker processes? I can't find any logs about it and Google results are all about recycling settings. I just restarted this server and that didn't help, so I'm out of ideas.

EssOEss
Oct 23, 2006
128-bit approved
That's a shamefully bad issue report for a programmer forum!

Can you share some info about the problem? What do you do? What do you expect to happen? What actually happens? What is special about the server? What is running on it? When did it break? What changed?

30 TO 50 FERAL HOG
Mar 2, 2005



So here's something that I'm not sure if can be done or if I should just redesign my UI:



So what I'd like to do is bind a property to either the label or to the ComboBox SelectedItem depending on if the top or bottom RadioButton is checked. I could do this pretty easy with code behind and CheckChanged events on the RadioButton. But I'm trying to go full XAML here as something of a learning exercise.

Here's the XAML

code:
<WPF:BaseWPFWindow x:Class="MELAdd.WPF.AddCategoryWPF"
	xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
	xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
	xmlns:WPF="clr-namespace:MELAdd.WPF"
	xmlns:validators="clr-namespace:MELAdd.WPF.ValidationRules"
	xmlns:ViewModels="clr-namespace:MELAdd.WPF.ViewModels"
	xmlns:data="clr-namespace:System.Data;assembly=System.Data"
	Title="Add Category" Height="Auto" Width="Auto" ResizeMode="NoResize" SizeToContent="WidthAndHeight" WindowStartupLocation="CenterOwner" MaxWidth="350">
	<Window.DataContext>
		<ViewModels:AddCategoryViewModel />
	</Window.DataContext>
	<Window.Resources>
		<ResourceDictionary>
			<ResourceDictionary.MergedDictionaries>
				<ResourceDictionary Source="Resources/Styles.xaml" />
			</ResourceDictionary.MergedDictionaries>
		</ResourceDictionary>
	</Window.Resources>
	<!-- The UI is split into three StackPanel elements -->
	<StackPanel Orientation="Vertical">
		<!-- First StackPanel element is the Category PIN GroupBox -->
		<GroupBox VerticalAlignment="Stretch" HorizontalAlignment="Stretch" Margin="5,0">
			<GroupBox.Header>
				<TextBlock Margin="0,0,0,1" Text="Category PIN" />
			</GroupBox.Header>
			<Grid>
				<Grid.RowDefinitions>
					<RowDefinition Height="Auto" />
					<RowDefinition Height="Auto" />
				</Grid.RowDefinitions>
				<Grid.ColumnDefinitions>
					<ColumnDefinition Width="6*" />
					<ColumnDefinition Width="1*" />
				</Grid.ColumnDefinitions>
				<RadioButton Margin="0,0,5,0" Grid.Row="0" Grid.Column="0" Name="m_UseNextAvailableRadioButton" Content="Use next available" VerticalAlignment="Center" IsChecked="True" />
				<Label Grid.Row="0" Grid.Column="1" Height="22" HorizontalContentAlignment="Right" VerticalContentAlignment="Center" Padding="0" Content="{Binding AvailablePINPrefixes[0]}"/>
				<RadioButton Margin="0,0,5,0" Grid.Row="1" Grid.Column="0" Name="m_ManuallySpecifyRadioButton" Content="Manually specify" VerticalAlignment="Center" />
				<ComboBox Grid.Row="1" Grid.Column="1" Name ="m_CategoryNumberDropDown" ItemsSource="{Binding AvailablePINPrefixes}" Style="{StaticResource textStyleComboBox}" IsEditable="True" IsEnabled="{Binding ElementName=m_ManuallySpecifyRadioButton, Path=IsChecked}" SelectedIndex="0">
					<ComboBox.SelectedValue>
						<Binding Path="Category[CategoryID]" Mode="OneWayToSource">
							<Binding.ValidationRules>
								<validators:SelectedItemExistsValidationRule ValidatesOnTargetUpdated="True" />
							</Binding.ValidationRules>
						</Binding>
					</ComboBox.SelectedValue>
				</ComboBox>
			</Grid>
		</GroupBox>
		<!-- Second StackPanel element is the Grid that holds the Description Label & TextBox -->
		<Grid>
			<Grid.RowDefinitions>
				<RowDefinition Height="Auto" />
			</Grid.RowDefinitions>
			<Grid.ColumnDefinitions>
				<ColumnDefinition Width="Auto" />
				<ColumnDefinition Width="*" />
			</Grid.ColumnDefinitions>
			<Label Grid.Column="0" Grid.Row="2" Padding="0,0,5,0" Margin="5,5,5,3" VerticalContentAlignment="Center">Description:</Label>
			<TextBox Grid.Column="1" Grid.Row="2" x:Name="m_DescriptionTextBox" TextWrapping="Wrap" Margin="5,5,5,3" Width="220" Height="22" VerticalContentAlignment="Center" Style="{StaticResource textStyleTextBox}">
				<TextBox.Text>
					<Binding Path="Category[Description]" UpdateSourceTrigger="PropertyChanged">
						<Binding.ValidationRules>
							<validators:NotEmptyValidationRule ValidatesOnTargetUpdated="True" />
						</Binding.ValidationRules>
					</Binding>
				</TextBox.Text>
			</TextBox>
		</Grid>
		<!-- Third panel, contains the OK and Cancel buttons -->
		<StackPanel HorizontalAlignment="Right" Orientation="Horizontal">
			<Button Name="m_OKButton" Width="75" Command="{Binding SaveCommand}" HorizontalAlignment="Right" IsDefault="True" Margin="5">
				<Button.Style>
					<Style TargetType="Button">
						<!-- Sets this button to be enabled or disabled based on the trigger conditions -->
						<Setter Property="IsEnabled" Value="False"/>
						<Style.Triggers>
							<MultiDataTrigger>
								<MultiDataTrigger.Conditions>
									<!-- The trigger conditions are validation errors on the following controls -->
									<Condition Binding="{Binding Path=(Validation.HasError), ElementName=m_CategoryNumberDropDown}" Value="False"/>
									<Condition Binding="{Binding Path=(Validation.HasError), ElementName=m_DescriptionTextBox}" Value="False"/>
								</MultiDataTrigger.Conditions>
								<Setter Property="IsEnabled" Value="True"/>
							</MultiDataTrigger>
						</Style.Triggers>
					</Style>
				</Button.Style>
				OK
			</Button>
			<Button Name="m_CancelButton" Width="75" HorizontalAlignment="Right" IsCancel="True" Margin="5" Height="25">Cancel</Button>
		</StackPanel>
	</StackPanel>
</WPF:BaseWPFWindow>

Munkeymon
Aug 14, 2003

Motherfucker's got an
armor-piercing crowbar! Rigoddamndicu𝜆ous.



EssOEss posted:

That's a shamefully bad issue report for a programmer forum!

Can you share some info about the problem? What do you do? What do you expect to happen? What actually happens? What is special about the server? What is running on it? When did it break? What changed?

Probably because I'm a data janitor not a server janitor :v:

It's Server '16 running in AWS with up to date patches since I patched it during the reboot.

I'm not sure what changed because I can't find anything interesting in any of the logs (system logs for IIS were off when I went to look lol). I only have a very fuzzy idea of when it changed because of when the request logs stopped getting new writes, but it's an internal test server and we just deployed a new version so QA isn't using it heavily.

Team lead thinks it's a DNS issue but Chrome says 'took too long to respond' which would be odd - I would expect a DNS issue to either be a lookup failed or connection refused.

IIS management doesn't list any worker processes, which supports the DNS hypothesis. Process monitor doesn't see any activity from w3wp, so it's not starting them only to have them crash on a bad DLL or something.

downout
Jul 6, 2009

Munkeymon posted:

Probably because I'm a data janitor not a server janitor :v:

It's Server '16 running in AWS with up to date patches since I patched it during the reboot.

I'm not sure what changed because I can't find anything interesting in any of the logs (system logs for IIS were off when I went to look lol). I only have a very fuzzy idea of when it changed because of when the request logs stopped getting new writes, but it's an internal test server and we just deployed a new version so QA isn't using it heavily.

Team lead thinks it's a DNS issue but Chrome says 'took too long to respond' which would be odd - I would expect a DNS issue to either be a lookup failed or connection refused.

IIS management doesn't list any worker processes, which supports the DNS hypothesis. Process monitor doesn't see any activity from w3wp, so it's not starting them only to have them crash on a bad DLL or something.

I usually try to remove as many variables as possible. Can the site be accessed from the machine through the URL or localhost/<port no>? If not, then usually that suggests that when the request hits the server IIS does not know what to do with it. So a bindings issue perhaps? You need to determine if the site gets the requests, IIS logging could help narrow that down. Also, it might be worth checking the event logs to see if anything is getting dumped into the application log or if IIS has a log in the windows event logs.

Another question I thought of - what made you think IIS can't or won't start worker processes?

Scikar
Nov 20, 2005

5? Seriously?

Munkeymon posted:

Anybody know what the heck to look for when IIS just won't or can't start worker processes? I can't find any logs about it and Google results are all about recycling settings. I just restarted this server and that didn't help, so I'm out of ideas.

Something else bound to the port(s) IIS wants from the bindings?

roflsaurus
Jun 5, 2004

GAOooooooh!! RAOR!!!
Anyone have experience with Azure Cosmos DB / Document DB?

I'm looking at building a new email distribution engine, and wondering if it would be suitable. It would certainly make things nice being able to use Azure functions with a Cosmos DB trigger.

I'm new to NoSQL, so not sure how this would handle aggregate functions.

e.g. We send 1000 emails in a campaign. We will have a reporting dashboard that will show total unsubscribes, opens, bounces etc. In SQL this is straightforward we just put these events into an events table, and then perform some Sum/Group By JobID to present the stats

My understanding with Cosmos is I would need to update the original document record and add additional data to the document? Or would I create a separate document record?

[Edit] thought I should clarify. it's not 1000 documents - it's 1 million per year. 2-3K per day, split over 10 or so batches/jobs. hence the reason for scale

roflsaurus fucked around with this message at 03:00 on Feb 2, 2018

Gallatin
Sep 20, 2004

Mr. Crow posted:

What's the recommend gut check online test website for c# these days? (From a managers perspective)? Is interview mocha any good?

Thanks for bringing this up - this is relevant for me as well. Does anyone have any recommendations?

leper khan
Dec 28, 2010
Honest to god thinks Half Life 2 is a bad game. But at least he likes Monster Hunter.

roflsaurus posted:

Anyone have experience with Azure Cosmos DB / Document DB?

I'm looking at building a new email distribution engine, and wondering if it would be suitable. It would certainly make things nice being able to use Azure functions with a Cosmos DB trigger.

I'm new to NoSQL, so not sure how this would handle aggregate functions.

e.g. We send 1000 emails in a campaign. We will have a reporting dashboard that will show total unsubscribes, opens, bounces etc. In SQL this is straightforward we just put these events into an events table, and then perform some Sum/Group By JobID to present the stats

My understanding with Cosmos is I would need to update the original document record and add additional data to the document? Or would I create a separate document record?

[Edit] thought I should clarify. it's not 1000 documents - it's 1 million per year. 2-3K per day, split over 10 or so batches/jobs. hence the reason for scale

Just use Postgres

B-Nasty
May 25, 2005

roflsaurus posted:

[Edit] thought I should clarify. it's not 1000 documents - it's 1 million per year. 2-3K per day, split over 10 or so batches/jobs. hence the reason for scale

That's not scale. Any real RDBMS will not even break a sweat handling that type of data. Use MSSQL if you have a license or if you don't want to pay, Postgres as leper khan said.

SQL Azure is pretty cheap as well.

raminasi
Jan 25, 2005

a last drink with no ice

NEED MORE MILK posted:

So here's something that I'm not sure if can be done or if I should just redesign my UI:



So what I'd like to do is bind a property to either the label or to the ComboBox SelectedItem depending on if the top or bottom RadioButton is checked. I could do this pretty easy with code behind and CheckChanged events on the RadioButton. But I'm trying to go full XAML here as something of a learning exercise.

Here's the XAML

code:
<WPF:BaseWPFWindow x:Class="MELAdd.WPF.AddCategoryWPF"
	xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
	xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
	xmlns:WPF="clr-namespace:MELAdd.WPF"
	xmlns:validators="clr-namespace:MELAdd.WPF.ValidationRules"
	xmlns:ViewModels="clr-namespace:MELAdd.WPF.ViewModels"
	xmlns:data="clr-namespace:System.Data;assembly=System.Data"
	Title="Add Category" Height="Auto" Width="Auto" ResizeMode="NoResize" SizeToContent="WidthAndHeight" WindowStartupLocation="CenterOwner" MaxWidth="350">
	<Window.DataContext>
		<ViewModels:AddCategoryViewModel />
	</Window.DataContext>
	<Window.Resources>
		<ResourceDictionary>
			<ResourceDictionary.MergedDictionaries>
				<ResourceDictionary Source="Resources/Styles.xaml" />
			</ResourceDictionary.MergedDictionaries>
		</ResourceDictionary>
	</Window.Resources>
	<!-- The UI is split into three StackPanel elements -->
	<StackPanel Orientation="Vertical">
		<!-- First StackPanel element is the Category PIN GroupBox -->
		<GroupBox VerticalAlignment="Stretch" HorizontalAlignment="Stretch" Margin="5,0">
			<GroupBox.Header>
				<TextBlock Margin="0,0,0,1" Text="Category PIN" />
			</GroupBox.Header>
			<Grid>
				<Grid.RowDefinitions>
					<RowDefinition Height="Auto" />
					<RowDefinition Height="Auto" />
				</Grid.RowDefinitions>
				<Grid.ColumnDefinitions>
					<ColumnDefinition Width="6*" />
					<ColumnDefinition Width="1*" />
				</Grid.ColumnDefinitions>
				<RadioButton Margin="0,0,5,0" Grid.Row="0" Grid.Column="0" Name="m_UseNextAvailableRadioButton" Content="Use next available" VerticalAlignment="Center" IsChecked="True" />
				<Label Grid.Row="0" Grid.Column="1" Height="22" HorizontalContentAlignment="Right" VerticalContentAlignment="Center" Padding="0" Content="{Binding AvailablePINPrefixes[0]}"/>
				<RadioButton Margin="0,0,5,0" Grid.Row="1" Grid.Column="0" Name="m_ManuallySpecifyRadioButton" Content="Manually specify" VerticalAlignment="Center" />
				<ComboBox Grid.Row="1" Grid.Column="1" Name ="m_CategoryNumberDropDown" ItemsSource="{Binding AvailablePINPrefixes}" Style="{StaticResource textStyleComboBox}" IsEditable="True" IsEnabled="{Binding ElementName=m_ManuallySpecifyRadioButton, Path=IsChecked}" SelectedIndex="0">
					<ComboBox.SelectedValue>
						<Binding Path="Category[CategoryID]" Mode="OneWayToSource">
							<Binding.ValidationRules>
								<validators:SelectedItemExistsValidationRule ValidatesOnTargetUpdated="True" />
							</Binding.ValidationRules>
						</Binding>
					</ComboBox.SelectedValue>
				</ComboBox>
			</Grid>
		</GroupBox>
		<!-- Second StackPanel element is the Grid that holds the Description Label & TextBox -->
		<Grid>
			<Grid.RowDefinitions>
				<RowDefinition Height="Auto" />
			</Grid.RowDefinitions>
			<Grid.ColumnDefinitions>
				<ColumnDefinition Width="Auto" />
				<ColumnDefinition Width="*" />
			</Grid.ColumnDefinitions>
			<Label Grid.Column="0" Grid.Row="2" Padding="0,0,5,0" Margin="5,5,5,3" VerticalContentAlignment="Center">Description:</Label>
			<TextBox Grid.Column="1" Grid.Row="2" x:Name="m_DescriptionTextBox" TextWrapping="Wrap" Margin="5,5,5,3" Width="220" Height="22" VerticalContentAlignment="Center" Style="{StaticResource textStyleTextBox}">
				<TextBox.Text>
					<Binding Path="Category[Description]" UpdateSourceTrigger="PropertyChanged">
						<Binding.ValidationRules>
							<validators:NotEmptyValidationRule ValidatesOnTargetUpdated="True" />
						</Binding.ValidationRules>
					</Binding>
				</TextBox.Text>
			</TextBox>
		</Grid>
		<!-- Third panel, contains the OK and Cancel buttons -->
		<StackPanel HorizontalAlignment="Right" Orientation="Horizontal">
			<Button Name="m_OKButton" Width="75" Command="{Binding SaveCommand}" HorizontalAlignment="Right" IsDefault="True" Margin="5">
				<Button.Style>
					<Style TargetType="Button">
						<!-- Sets this button to be enabled or disabled based on the trigger conditions -->
						<Setter Property="IsEnabled" Value="False"/>
						<Style.Triggers>
							<MultiDataTrigger>
								<MultiDataTrigger.Conditions>
									<!-- The trigger conditions are validation errors on the following controls -->
									<Condition Binding="{Binding Path=(Validation.HasError), ElementName=m_CategoryNumberDropDown}" Value="False"/>
									<Condition Binding="{Binding Path=(Validation.HasError), ElementName=m_DescriptionTextBox}" Value="False"/>
								</MultiDataTrigger.Conditions>
								<Setter Property="IsEnabled" Value="True"/>
							</MultiDataTrigger>
						</Style.Triggers>
					</Style>
				</Button.Style>
				OK
			</Button>
			<Button Name="m_CancelButton" Width="75" HorizontalAlignment="Right" IsCancel="True" Margin="5" Height="25">Cancel</Button>
		</StackPanel>
	</StackPanel>
</WPF:BaseWPFWindow>

I get that this is a learning exercise, but the actual thing I’d do, which you didn’t mention as an alternative, is to just bind both the label, the combobox, and the radio button to individual properties and have the viewmodel implement the decision logic.

Opulent Ceremony
Feb 22, 2012

roflsaurus posted:

Anyone have experience with Azure Cosmos DB / Document DB?

I'm looking at building a new email distribution engine, and wondering if it would be suitable. It would certainly make things nice being able to use Azure functions with a Cosmos DB trigger.

I'm new to NoSQL, so not sure how this would handle aggregate functions.

I have experience with Cosmos DB insomuch as I investigated it as a potential tool for a solution of my own several months ago and immediately ditched the idea when I read that the only aggregate function it supported was Count

Edit: looks like it does COUNT, MIN, MAX, SUM, and AVG with the DocumentDB API, but that is still far too limited for what I needed.

Opulent Ceremony fucked around with this message at 17:07 on Feb 2, 2018

Drastic Actions
Apr 7, 2009

FUCK YOU!
GET PUMPED!
Nap Ghost

B-Nasty posted:

That's not scale. Any real RDBMS will not even break a sweat handling that type of data. Use MSSQL if you have a license or if you don't want to pay, Postgres as leper khan said.

SQL Azure is pretty cheap as well.

MySQL and Postgres are also available in the portal as deployable previews as well, if you don't want to host a VM. They are previews though; you can't scale the space on your databases and the costs are static (As in, SQL Azure can scale with usage, Postgres and MySQL cost a flat rate iirc). But that should be cheaper than hosting a VM or container.

EDIT:

For Cosmos DB, if you have a need for it, is very good. But for 99% of use cases, you don't need NOSQL, and Cosmos DB can get very expensive, very quick, especially if you're not throwing a ton of documents into it.

B-Nasty
May 25, 2005

I'd point out another option is something like Azure Table Storage, which is sort of a lite document datastore that's easy to work with and extremely cheap.

Of course, the same caveats apply: default to a relational DB and add in a NoSql (Document/KV) datastore only once it is obviously the best choice or it fills a specific need. For example, I have an Azure App Service that writes its logs to Azure Tables, because there are limited options for logging in PaaS apps. This reduces r/w and data costs over writing it to a MSSQL table, and it is super inexpensive.

30 TO 50 FERAL HOG
Mar 2, 2005



raminasi posted:

I get that this is a learning exercise, but the actual thing I’d do, which you didn’t mention as an alternative, is to just bind both the label, the combobox, and the radio button to individual properties and have the viewmodel implement the decision logic.

Yeah, I think this is the best solution TBH. I was just trying to keep the code behind as minimal as possible

New Yorp New Yorp
Jul 18, 2003

Only in Kenya.
Pillbug

NEED MORE MILK posted:

Yeah, I think this is the best solution TBH. I was just trying to keep the code behind as minimal as possible

That solution is without codebehind. The viewmodel implements the logic to choose between modes and the view has databindings to display/not display things based on the logic in the viewmodel.

LongSack
Jan 17, 2003

Question about events and asynchrony (?).

My “workflow” (it’s not really workflow, but more event processing) currently works like this:

ViewModel method makes changes to the Ticket, calls WorkflowManager.TicketChanged(), then calls my DAL update method.

The TicketChanged method figures out what TicketChangedEventArgs to build then fires an event.

The event handler updates the Ticket as required.

So what are the odds that the DAL update method is called before the event has fired or the event handler has completed its changes? Should I handle this as method calls rather than with events to ensure that all the Ticket updates are complete before the update method is called?

I know that the event will be fired before the update, because that is all synchronous. But what about the event handler?

New Yorp New Yorp
Jul 18, 2003

Only in Kenya.
Pillbug

LongSack posted:

Question about events and asynchrony (?).

My “workflow” (it’s not really workflow, but more event processing) currently works like this:

ViewModel method makes changes to the Ticket, calls WorkflowManager.TicketChanged(), then calls my DAL update method.

The TicketChanged method figures out what TicketChangedEventArgs to build then fires an event.

The event handler updates the Ticket as required.

So what are the odds that the DAL update method is called before the event has fired or the event handler has completed its changes? Should I handle this as method calls rather than with events to ensure that all the Ticket updates are complete before the update method is called?

I know that the event will be fired before the update, because that is all synchronous. But what about the event handler?

Everything is synchronous unless you implement asynchrony.

LongSack
Jan 17, 2003

New Yorp New Yorp posted:

Everything is synchronous unless you implement asynchrony.

Ok good to know. Thanks.

EmmyOk
Aug 11, 2013

I'm having trouble getting to grips with performance counters, I've found some nice code samples of them but when I try to do them myself VS is convinced PerformanceCounters don't have a namespace etc. but I've found stuff from 2017 about them so I don't think they're a removed feature. This must sound really dumb :saddowns: any help is appreciated pals

EssOEss
Oct 23, 2006
128-bit approved
How about you post some actual code and errors? Does this code you can copy-paste straight from PerformanceCounter not work for you? https://msdn.microsoft.com/en-us/library/system.diagnostics.performancecounter(v=vs.110).aspx

raminasi
Jan 25, 2005

a last drink with no ice
This isn't .NET(maybe?), but it's a weird-rear end Windows thing, and I have no idea how to Google it, so I figured I might as well try asking here.

I'm writing a plugin for a particular piece of software. My plugin is a .NET assembly; it depends on a native DLL (specifically, SQLite.Interop.dll). The way the host software works is that when a plugin is loaded, and tries to load additional dependencies, the host software will check the plugin's for directory them (as well as everywhere else it would normally look).

Except, apparently, one particular directory on my hard drive. If I load the plugin from any other directory (seemingly), everything works fine, but if I put the plugin in this specific directory, the host won't look for the dependency there, and therefore won't find it at all. (I'm watching it with Process Monitor.) Sometimes. The problem first appeared a couple of weeks ago, and it was driving me nuts, and then it just went away on its own, and now it's back. I've never heard of this happening on any other machines, but I'm terrified that it might start, because I have no idea how to fix it. I know that the first step to solving it is figuring out exactly what circumstances under which it happens, but does this ring any bells for anyone?

e: It looks like two slightly different versions of SQLite.Interop.dll had slipped into this duct-tape-and-baling-wire excuse for a build system. Why that resulted in the host application developing prescience and refusing to even try to load the bad one I still don't understand, but getting rid of it seems to have fixed the problem.

raminasi fucked around with this message at 00:52 on Feb 5, 2018

Mr Shiny Pants
Nov 12, 2012
Get a clean Windows install and see if you can reproduce it, that would be my first step. A VM or something, if it runs under Mono, give Linux a shot.

If not, something is broken on your machine. That narrows it down.

If yes, dig further. :)

Mr Shiny Pants fucked around with this message at 06:58 on Feb 5, 2018

EmmyOk
Aug 11, 2013

EssOEss posted:

How about you post some actual code and errors? Does this code you can copy-paste straight from PerformanceCounter not work for you? https://msdn.microsoft.com/en-us/library/system.diagnostics.performancecounter(v=vs.110).aspx

Apologies I was frustrated and made a super vague post at the end of a long day that didn't really give a good rundown of my issues. This code did end up working for me and my old code didn't and I realised that I'd created a .NET Core project instead of .NET Framework :doh:

roflsaurus
Jun 5, 2004

GAOooooooh!! RAOR!!!
This isn't really a specific .Net question, but more general architecture advice using .Net and Azure.

I'm (re)building a mass email engine (not spam - transactional notifications e.g. invoices, receipts). Volumes anywhere from 10k to 300k per day. The email engine uses RazorEngine to compose emails from templates, and can optionally attach PDFs. We store the PDFs out of our system in batches of 1000 (to minimize storage costs with repeating logos, etc). We currently have an inhouse system in place, but it's single threaded and can't handle the volume. I'm mapping out the architecture for the queue processing, and whether to use Webjobs, Functions or a mix of both.

I'm thinking i'll need at least 2 components - scheduler, and composer. Scheduler would be responsible for releasing batches of 50 messages to a storage Queue, and Composer would be responsible for consuming these. The reason for batches of 50 is to implement custom per-hour throttling.

So Scheduler could add 10x50 batches to the Queue - probably as a webjob running on a triggered schedule. Composer could be either a webjob that consumes a storage queue, or it could be a function. It could also consume the batch of 50, or just a single message.

The tricky parts are :

Caching
Each batch of 50 will have a PDF it then splits up and attaches 1-2 pages to each record. The cost of retreiving and parsing this PDF is quite high, so i don't want to re-open it for every individual email. This means I need to implement caching - but Functions is supposed to be stateless?

Status updating
After each email is sent, I would want to update it's status in SQL to Sent. And then once a whole batch is sent, mark the whole batch is complete. If I did this in functions, that would be 5000 SQL queries. Unless I create another queue to process status updates?

Scaling
I want to implement autoscaling. e.g. if there's 5000 messages in the queue, scale up 4 or 5 instances to process. I don't think Webjobs can do scaling based on storage queues?


So if I go functions, I get scaling, but I lose Caching and bulk operations. If I go webjobs, I get caching and bulk ops, but need to implement custom scaling.

Anything else I've missed?



Still deciding on Cosmodb and SQL - Cosmo would definitely allow flexibility in data structures for the different doc types and searching, but would make searching on status (e.g. sent date, status, opened) more difficult, as i'd need to replace the document every time there's an event, or store the status info in SQL anyway.

GoodCleanFun
Jan 28, 2004

roflsaurus posted:

Status updating
After each email is sent, I would want to update it's status in SQL to Sent. And then once a whole batch is sent, mark the whole batch is complete. If I did this in functions, that would be 5000 SQL queries. Unless I create another queue to process status updates?

You definitely don't want to run that many sql queries although the engine can easily handle it.

Check out the bulk insert functionality of sql if you are using ms sql. I believe it's SqlBulkCopy. I have this wrapped in a library so I can quickly insert data into a temp table or an actual table. This is not a small amount of code, but once you build it you have a great tool.

In this example you could insert the data into a temp table and then use that to do this update in a single query or in batches.

Adbot
ADBOT LOVES YOU

roflsaurus
Jun 5, 2004

GAOooooooh!! RAOR!!!

GoodCleanFun posted:

You definitely don't want to run that many sql queries although the engine can easily handle it.

Check out the bulk insert functionality of sql if you are using ms sql. I believe it's SqlBulkCopy. I have this wrapped in a library so I can quickly insert data into a temp table or an actual table. This is not a small amount of code, but once you build it you have a great tool.

In this example you could insert the data into a temp table and then use that to do this update in a single query or in batches.

Yeah I definitely realise updating a DB after every function execution would be "A Bad Idea"

Where I'm stuck is how to batch it. Functions are stateless so I can't aggregate them in memory and use SqlBulkCopy. I would need to add an "Update status" message to another storage queue, and then consume this storage queue and perform a bulk update somehow.

  • 1
  • 2
  • 3
  • 4
  • 5
  • Post
  • Reply