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
Kyte
Nov 19, 2013

Never quacked for this

epswing posted:

DevOps comes with X free seats (I think 5?), beyond which the admin needs to pay for additional seats. Unless they’re VS subs in which case there’s no charge. If you needed to provide your own VS sub I’d guess your client didn’t want to pay for your seat, which was my dilemma above.

At least, that’s my understanding, I’m still new at DevOps, we’ve been using it for less than a year.

I actually don't know if it was my management or the client that provided the sub but that makes sense.

Adbot
ADBOT LOVES YOU

New Yorp New Yorp
Jul 18, 2003

Only in Kenya.
Pillbug

epswing posted:

I think you got the story backwards, I'm the employer. The "you" you're describing is the developer, as far as I can tell.

When I say "I'll pay for the DevOps seat" I mean I the employer will pay for the contractor's SaaS service usage.

(I.e. I agree with you.)

Oh, yeah. I had it backwards.

New Yorp New Yorp
Jul 18, 2003

Only in Kenya.
Pillbug

epswing posted:

At least, that’s my understanding, I’m still new at DevOps, we’ve been using it for less than a year.

Be aware that Microsoft is not investing in Azure Devops anymore and the guidance I've been seeing is basically "use github" with asterisks for a few scenarios around project management, manual testing, or integration with other git providers like bitbucket.

IMO for enterprise usage Azure devops is superior in most ways but that's the way the wind is blowing at Microsoft.

fuf
Sep 12, 2004

haha
man I still have such issues with EF and saving related entities. I just don't get it at all.

I have a couple of related classes like this:
C# code:
public class Playlist{
   public Guid Id {get;set;}
   public string Name {get;set;}
   public Filter Filter {get;set;}
}

public class Filter{
   public Guid Id {get;set;}
   public string Name {get;set;}
}
If I try and insert a new Playlist with a Filter that is already in the DB:
C# code:
using var db = contextFactory.CreateDbContext();
db.Playlists.Add(playlist);
db.SaveChanges();
Then I always get this error:
SQLite Error 19: 'UNIQUE constraint failed: Filter.Id'.

Because for some reason it's trying to insert the Filter as a new row even though it already exists.

So then I think ok, maybe I need to attach the Filters in the db to the current context so that it doesn't try to insert duplicates? Like this:

C# code:
using var db = contextFactory.CreateDbContext();
var Filters = db.Filters.ToList();
db.Playlists.Add(playlist);
db.SaveChanges();
But then the error is:
"The instance of entity type 'Filter' cannot be tracked because another instance with the same key value for {'Id'} is already being tracked."

I think this is because the context now has two versions of the same Filter object: one from the DB and one attached to the Playlist object.

How can I tell the context that they're the same object??

The only way I can get it to work is like this, by actually changing the Playlist's Filter to the DB version:
C# code:
using var db = contextFactory.CreateDbContext();
playlist.Filter = db.Filters.Where(f => f.Id == playlist.Filter.Id).FirstOrDefault();
db.Playlists.Add(playlist);
db.SaveChanges();
But this is a pretty laborious workaround and gets really complicated as the models get more complex. There's gotta be an easier way of just making the context aware of what's already in the DB when inserting or updating rows and related entities.

e: actually I just realised my "solution" doesn't work anyway because it's not actually gonna save any changes to the Filter lol, just revert it back to the DB version

fuf fucked around with this message at 16:28 on Feb 5, 2022

No Pants
Dec 10, 2000

fuf posted:

man I still have such issues with EF and saving related entities. I just don't get it at all.

[...]

So then I think ok, maybe I need to attach the Filters in the db to the current context so that it doesn't try to insert duplicates? Like this:

C# code:
using var db = contextFactory.CreateDbContext();
var Filters = db.Filters.ToList();
db.Playlists.Add(playlist);
db.SaveChanges();
But then the error is:
"The instance of entity type 'Filter' cannot be tracked because another instance with the same key value for {'Id'} is already being tracked."

I think this is because the context now has two versions of the same Filter object: one from the DB and one attached to the Playlist object.

How can I tell the context that they're the same object??

Assuming this is EF Core, you're on the right track. You can re-attach the Filter object with DbContext.Update():
C# code:
using var db = contextFactory.CreateDbContext();
db.Update(playlist.Filter);
db.Playlists.Add(playlist);
db.SaveChanges();
The documentation goes over some different scenarios.

No Pants fucked around with this message at 17:14 on Feb 5, 2022

fuf
Sep 12, 2004

haha
Thank you, that's helpful.

I actually took a hammer to the problem and ignored the official Blazor advice to use a separate dbcontext for every operation. I replaced the dbcontextfactory with just a single dbcontext and it has basically solved all of my problems because now the context is just keeping track of everything. Maybe it will cause loads of other problems down the line but we'll see...

I think the Blazor advice is because they assume you will be doing db operations within components, but I have all my db stuff in a separate service so maybe it'll be ok...

Kyte
Nov 19, 2013

Never quacked for this

fuf posted:

Thank you, that's helpful.

I actually took a hammer to the problem and ignored the official Blazor advice to use a separate dbcontext for every operation. I replaced the dbcontextfactory with just a single dbcontext and it has basically solved all of my problems because now the context is just keeping track of everything. Maybe it will cause loads of other problems down the line but we'll see...

I think the Blazor advice is because they assume you will be doing db operations within components, but I have all my db stuff in a separate service so maybe it'll be ok...
The problem is DbContext and especially its change tracker is not designed to last longer than a single unit of work, and it'll start getting wonky after a while.


fuf posted:

man I still have such issues with EF and saving related entities. I just don't get it at all.

I have a couple of related classes like this:
C# code:
public class Playlist{
   public Guid Id {get;set;}
   public string Name {get;set;}
   public Filter Filter {get;set;}
}

public class Filter{
   public Guid Id {get;set;}
   public string Name {get;set;}
}
If I try and insert a new Playlist with a Filter that is already in the DB:
C# code:
using var db = contextFactory.CreateDbContext();
db.Playlists.Add(playlist);
db.SaveChanges();
Then I always get this error:
SQLite Error 19: 'UNIQUE constraint failed: Filter.Id'.

Because for some reason it's trying to insert the Filter as a new row even though it already exists.

So then I think ok, maybe I need to attach the Filters in the db to the current context so that it doesn't try to insert duplicates? Like this:

C# code:
using var db = contextFactory.CreateDbContext();
var Filters = db.Filters.ToList();
db.Playlists.Add(playlist);
db.SaveChanges();
But then the error is:
"The instance of entity type 'Filter' cannot be tracked because another instance with the same key value for {'Id'} is already being tracked."

I think this is because the context now has two versions of the same Filter object: one from the DB and one attached to the Playlist object.

How can I tell the context that they're the same object??

The only way I can get it to work is like this, by actually changing the Playlist's Filter to the DB version:
C# code:
using var db = contextFactory.CreateDbContext();
playlist.Filter = db.Filters.Where(f => f.Id == playlist.Filter.Id).FirstOrDefault();
db.Playlists.Add(playlist);
db.SaveChanges();
But this is a pretty laborious workaround and gets really complicated as the models get more complex. There's gotta be an easier way of just making the context aware of what's already in the DB when inserting or updating rows and related entities.

e: actually I just realised my "solution" doesn't work anyway because it's not actually gonna save any changes to the Filter lol, just revert it back to the DB version

The problem in the first one is that you didn't load the Filter from database but rather created a new Filter object and added it to the navigation property without attaching it to the context. Since it doesn't exist in the context, the change tracker believes it's something new and tries to INSERT.

That can be solved by using Attach(), as you did in the second example, but that errors out if the context already had it attached as from a previous operation. You correctly understood the problem in the second example: you loaded every filter from database, but then instead of picking out the filter from the loaded list that DbContext already knows of, you tried to attach your own untracked filter.

For the third case, it's easier if rather than using Where().FirstOrDefault() (which will always load from database, btw), you can use Find() to get the entity from the context (or query the DB if it wasn't already loaded).

The correct method to add a preexisting related entity to a new object is to:
1) Obtain your related entity from the context, usually through Find().
2) Add that entity object to the navigation property. Also do whatever updates you wanna do to it.
3) Save.


That said, sometimes you don't want to load a whole entity when a simple attach would work just fine. For that case, I made an extension method.

C# code:
public static TEntity FindTrackedOrAttach<TEntity>(this DbSet<TEntity> dbSet, int id)
    where TEntity : EntityBase, new()
{
    var e = dbSet.Local.FirstOrDefault(e => e.Id == id);
    if (e is null)
        {
            e = new TEntity { Id = id };
            dbSet.Attach(e);
        }
    return e;
}
It does have a very big disadvantage: If a later operation in the same DbContext (a Find() or navigation or such) happens to ask for the object, EF will happily provide me the empty object I attached, and since it's empty I get all kind of exciting NPEs and such.
(Incidentally this is one of the reasons why you limit your DbContext to a single unit of work)

Kyte fucked around with this message at 20:06 on Feb 5, 2022

EssOEss
Oct 23, 2006
128-bit approved

New Yorp New Yorp posted:

Be aware that Microsoft is not investing in Azure Devops anymore and the guidance I've been seeing is basically "use github" with asterisks for a few scenarios around project management, manual testing, or integration with other git providers like bitbucket.

IMO for enterprise usage Azure devops is superior in most ways but that's the way the wind is blowing at Microsoft.

Yeah, but it will take a good 5+ years for GitHub to catch up to all that ADO can accomplish. This means we are going to have 5 more years of stagnation, during which any investments to improve ADO are deferred and GitHub remains not ready for serious enterprise usage :(

TheBlackVegetable
Oct 29, 2006

Kyte posted:

That said, sometimes you don't want to load a whole entity when a simple attach would work just fine. For that case, I made an extension method.

C# code:
public static TEntity FindTrackedOrAttach<TEntity>(this DbSet<TEntity> dbSet, int id)
    where TEntity : EntityBase, new()
{
    var e = dbSet.Local.FirstOrDefault(e => e.Id == id);
    if (e is null)
        {
            e = new TEntity { Id = id };
            dbSet.Attach(e);
        }
    return e;
}
It does have a very big disadvantage: If a later operation in the same DbContext (a Find() or navigation or such) happens to ask for the object, EF will happily provide me the empty object I attached, and since it's empty I get all kind of exciting NPEs and such.
(Incidentally this is one of the reasons why you limit your DbContext to a single unit of work)

What kind of NRE, do you mean like null lists of children references? Isn't the correct thing to initialise those in the entity's constructor?

And/or pass in an (optional) Action<TEntity> init to
FindTrackedOrAttach to initialise the entity if it is created during the call.

LongSack
Jan 17, 2003

fuf posted:

man I still have such issues with EF and saving related entities. I just don't get it at all.

I "cheat" to get around this. I have 2 attributes:
C# code:
[AttributeUsage(AttributeTargets.Class)]
public sealed class HasNullableMembersAttribute : Attribute
{
    public HasNullableMembersAttribute() => HasNullableMembers = true;
    public HasNullableMembersAttribute(bool hasNullableMembers) => HasNullableMembers = hasNullableMembers;
    public bool HasNullableMembers { get; }
}

[AttributeUsage(AttributeTargets.Property)]
public sealed class NullOnInsertOrUpdateAttribute : Attribute
{
    public NullOnInsertOrUpdateAttribute() => NullOnInsertOrUpdate = true;
    public NullOnInsertOrUpdateAttribute(bool nullOnInsertOrUpdate) => NullOnInsertOrUpdate = nullOnInsertOrUpdate;
    public bool NullOnInsertOrUpdate { get; }
}
and my Entity classes look like this:
C# code:
[HasNullableMembers]
public class AccountEntity : IIdEntity
{
    [Required]
    public int Id { get; set; }
    [Required]
    public int CompanyId { get; set; }
    [Required]
    public int AccountTypeId { get; set; }
    [Required, NonNegative]
    public DueDateType DueDateType { get; set; }
    [Required, NonNegative]
    public int Month { get; set; }
    [Required, NonNegative]
    public int Day { get; set; }
    [Required]
    public bool IsPayable { get; set; }
    [Required]
    public bool IsClosed { get; set; }
    [Required]
    public DateTime ClosedDate { get; set; }
    [Required]
    public string Comments { get; set; }
    [Required, MaxLength(Constants.NameLength)]
    public string Tag { get; set; }

    [NullOnInsertOrUpdate]
    public AccountTypeEntity? AccountType { get; set; }
    [NullOnInsertOrUpdate]
    public AccountNumberEntity? AccountNumber { get; set; }
}
Then in my Repository base class, I set a flag in the constructor if the class has the HasNullableMembers attribute, and if so in the Insert and Update methods I loop through all the public instance properties, and if any has the NullOnInsertOrUpdate attribute I set those properties to null before inserting up updating the item.

This works because all of the UI stuff deals with DTO objects not entity objects, and there is a services layer that sits between the UI layer(s) and the data access layer. The service layer converts from DTO objects to entity objects before calling methods in the repositories, so the repositories are free to mutate the entity object without fear of impacting the UI layer(s).

Also, the DbContext is as ephemeral as I can possibly make it. This is hard on dependency injection because a Service needs an instance of a Repository which needs an instance of the DbContext, so if I inject a service directly into a viewmodel or controller, it leads to problems with the tracker, so I end up having to inject a ServiceFactory and creating services for single operations to make sure the DbContext doesn't outlive its usefulness.

I've tried all the methods I've found on the net to deal with the tracker, like setting the State to "detached", or checking if it's tracked already before attaching an entity, and I haven't found any that work reliably. I've stepped through code that sets the state to "detached" only to check the context and see that the state is "unchanged". The only thing I've been able to get work reliably is Create DbContext -> perform ONE operation or transaction -> destroy DbContext.

In my newest project, I'm actually going back to ADO.NET and rolling my own minimal ORM just to see how much fun it is. Sure, I won't have LINQ, but I can hand craft all the SQL.

New Yorp New Yorp
Jul 18, 2003

Only in Kenya.
Pillbug

LongSack posted:



In my newest project, I'm actually going back to ADO.NET and rolling my own minimal ORM just to see how much fun it is. Sure, I won't have LINQ, but I can hand craft all the SQL.

That's called Dapper. Try it out.

LongSack
Jan 17, 2003

New Yorp New Yorp posted:

That's called Dapper. Try it out.

Just starting, and haven’t gotten to that point yet. I’m very aware of Dapper (Tim Corey is a big fan), but I’m starting with the creation of the database and tables from the entity classes, which I don’t think Dapper handles. Dapper is definitely on my radar though.

Kyte
Nov 19, 2013

Never quacked for this

TheBlackVegetable posted:

What kind of NRE, do you mean like null lists of children references? Isn't the correct thing to initialise those in the entity's constructor?

And/or pass in an (optional) Action<TEntity> init to
FindTrackedOrAttach to initialise the entity if it is created during the call.
I didn't mean literally NREs every time, it could just be that some part of the code didn't expect uninitialized values, but same thing really.
Also I used to be lazy about adding collection initializers to my entity classes because EF takes care of it when it loads an entity.

I meant stuff like, say you have an application form for, dunno, social media or whatever. And there's a dropdown to select countries. You received in your viewmodel the country IDs, so you do something like:
C# code:
var newEntity = new Entity {
  [copy over props from the viewmodel]
}
newForm.Countries.AddRange(vm.CountryIds.Select(id => db.Countries.FindTrackedOrAttach(id)));
db.SaveChanges();
I have no need to load or initialize anything about the country entity and I'd like to avoid the queries.

LongSack posted:

Also, the DbContext is as ephemeral as I can possibly make it. This is hard on dependency injection because a Service needs an instance of a Repository which needs an instance of the DbContext, so if I inject a service directly into a viewmodel or controller, it leads to problems with the tracker, so I end up having to inject a ServiceFactory and creating services for single operations to make sure the DbContext doesn't outlive its usefulness.

Isn't this what transient or scoped dependencies are for? (DbContext is scoped by default)
Or you like them to be even more short-lived than per-request?

Kyte fucked around with this message at 04:39 on Feb 6, 2022

insta
Jan 28, 2009

LongSack posted:

Just starting, and haven’t gotten to that point yet. I’m very aware of Dapper (Tim Corey is a big fan), but I’m starting with the creation of the database and tables from the entity classes, which I don’t think Dapper handles. Dapper is definitely on my radar though.

NPoco does table creation with a plug-in, and can use LINQ. It's pretty efficient.

zokie
Feb 13, 2006

Out of many, Sweden

LongSack posted:

Just starting, and haven’t gotten to that point yet. I’m very aware of Dapper (Tim Corey is a big fan), but I’m starting with the creation of the database and tables from the entity classes, which I don’t think Dapper handles. Dapper is definitely on my radar though.

I recently discovered .dacpac, dunno how I messed it for so long but that poo poo looks pretty neat to me.

LongSack
Jan 17, 2003

Kyte posted:

Isn't this what transient or scoped dependencies are for? (DbContext is scoped by default)
Or you like them to be even more short-lived than per-request?

It’s not really an issue with web apps, where a controller is being instantiated for a single request anyway - get me a list of Foos, update this Bar, delete this Dongle.

But in a desktop app, where you say go to the account type maintenance window, and can create / update / delete account types all in the same window with the same ViewModel, then the DbContext tracker can get in the way.

fuf
Sep 12, 2004

haha

EssOEss posted:

For the third case, it's easier if rather than using Where().FirstOrDefault() (which will always load from database, btw), you can use Find() to get the entity from the context (or query the DB if it wasn't already loaded).

LongSack posted:

I loop through all the public instance properties, and if any has the NullOnInsertOrUpdate attribute I set those properties to null before inserting up updating the item.

Thanks for these, both very useful tips.

I am back to using the dbcontextfactory and creating a context for each operation, because it seems like it'll save problems in the long run.

Then I use a (way clunkier) version of LongSack's approach and null any properties I don't wanna save, do the db stuff, then set them back to their original values.

For the db stuff, I check the db for any related entities, and then either insert or update them first before updating the main entity.

I think it's probably pretty inefficient and overkill, but performance is very low on the list compared to getting something that works reliably.

Kyte
Nov 19, 2013

Never quacked for this

LongSack posted:

It’s not really an issue with web apps, where a controller is being instantiated for a single request anyway - get me a list of Foos, update this Bar, delete this Dongle.

But in a desktop app, where you say go to the account type maintenance window, and can create / update / delete account types all in the same window with the same ViewModel, then the DbContext tracker can get in the way.

Ah, makes sense. You mentioned controllers and viewmodels so I assumed you were talking web. Yeah, for desktop it makes much more sense to use a factory.

Rocko Bonaparte
Mar 12, 2002

Every day is Friday!
I am trying to handle Python-style printf formatting. I could just write a parser for this but I wonder if this might be one I can do in one (albeit kind of gross) regex.

The syntax if you're curious.

I feel like I could get the basics with just basic IndexOf to find % signs and gun it from there but I know it'll be quite a bit of coding. I wondered if it would make any sizable difference over a compiled regex. Of course, if the regex multiplies, it would hit my threshold for "write a parser god drat" (usually at four regexes) regardless.

NihilCredo
Jun 6, 2011

iram omni possibili modo preme:
plus una illa te diffamabit, quam multæ virtutes commendabunt

I had to do something very similar and I just wrote a FSM using StringBuilder.Append(char). Haven't had any issues and the performance has been fine, considering it's a very hot path (I use it for most logging). There are just three states: PlainText, PrintfParam, JustAfterPrintfParam.

epswing
Nov 4, 2003

Soiled Meat

Rocko Bonaparte posted:

I am trying to handle Python-style printf formatting. I could just write a parser for this but I wonder if this might be one I can do in one (albeit kind of gross) regex.

The syntax if you're curious.

What I’m curious about is what’s missing from C# string formatting? Or is this just for fun?

Munkeymon
Aug 14, 2003

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



Potassium Problems posted:

I don't know what you mean, and maybe it's changed with .NET 5/6, but the way function parameter bindings happen (i.e., ILogger, Trigger-specific arguments like a service bus message or HTTP request object) isn't aware of the dependencies you wire up in Startup.cs, which is why the op is getting binding errors. You have to have a non-static function in a non-static class and wire up the constructor to take a dependency on IConfiguration.

<snip>

:doh: I missed that the generated class/function was static. Makes sense semantically, of course, but still :doh:

I just injected it into a property manually in Startup:
C# code:
ThrowawayFunction.Configuration = builder.GetContext().Configuration;

New Yorp New Yorp posted:

Be aware that Microsoft is not investing in Azure Devops anymore and the guidance I've been seeing is basically "use github" with asterisks for a few scenarios around project management, manual testing, or integration with other git providers like bitbucket.

IMO for enterprise usage Azure devops is superior in most ways but that's the way the wind is blowing at Microsoft.

I've heard that it's going to be maintained in parallel. I mean, the Windows team apparently uses it so it's load bearing.

New Yorp New Yorp
Jul 18, 2003

Only in Kenya.
Pillbug

Munkeymon posted:

I've heard that it's going to be maintained in parallel. I mean, the Windows team apparently uses it so it's load bearing.

Yeah but maintained in Microsoftese means "limited new feature development, if any". The bulk of the team was moved to GitHub a few years ago.

Drastic Actions
Apr 7, 2009

FUCK YOU!
GET PUMPED!
Nap Ghost

Munkeymon posted:

I've heard that it's going to be maintained in parallel. I mean, the Windows team apparently uses it so it's load bearing.

Visual Studio uses it too but...


New Yorp New Yorp posted:

Yeah but maintained in Microsoftese means "limited new feature development, if any". The bulk of the team was moved to GitHub a few years ago.


Yeah, this.

fuf
Sep 12, 2004

haha
I'm trying to get my head around something probably really basic.

C# code:

List<Tag> AllTags = new List<Tag>();
List<Tag> FilteredTags = new List<Tag>();

List<Tag> TagsOnPage = FilteredTags; // TagsOnPage is a reference to the FilteredTags List, which is what I want

FilteredTags = AllTags.Where(t => t.Name.Contains("hello")).ToList();
// TagsOnPage now no longer refers to the same List as FilteredTags, because FilteredTags has changed to a new List
Is there a way to keep TagsOnPage always as a reference to the same List as FilteredTags, even when FilteredTags changes to a new List?

Without having to explicitly do TagsOnPage = FilteredTags each time.

If that makes any sense...

Munkeymon
Aug 14, 2003

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



New Yorp New Yorp posted:

Yeah but maintained in Microsoftese means "limited new feature development, if any". The bulk of the team was moved to GitHub a few years ago.

To be fair, GitHub's CI stuff has a lot of catching up to do

Hammerite
Mar 9, 2007

And you don't remember what I said here, either, but it was pompous and stupid.
Jade Ear Joe

fuf posted:

I'm trying to get my head around something probably really basic.

C# code:

List<Tag> AllTags = new List<Tag>();
List<Tag> FilteredTags = new List<Tag>();

List<Tag> TagsOnPage = FilteredTags; // TagsOnPage is a reference to the FilteredTags List, which is what I want

FilteredTags = AllTags.Where(t => t.Name.Contains("hello")).ToList();
// TagsOnPage now no longer refers to the same List as FilteredTags, because FilteredTags has changed to a new List
Is there a way to keep TagsOnPage always as a reference to the same List as FilteredTags, even when FilteredTags changes to a new List?

Without having to explicitly do TagsOnPage = FilteredTags each time.

If that makes any sense...

Why do you need to do this? What are you trying to achieve?

fuf
Sep 12, 2004

haha

Hammerite posted:

Why do you need to do this? What are you trying to achieve?

TagsOnPage is a list of tags that sometimes has its own list and sometimes just refers to FilteredTags.

When it is just referring to FilteredTags I want to be able to change the FilteredTags list and have that change reflected in TagsOnPage (because they are the same list in that instance).

Sorry if I'm not explaining properly...

insta
Jan 28, 2009
Use a method called GetTagsOnPage() and conditionally return which list you need?

NihilCredo
Jun 6, 2011

iram omni possibili modo preme:
plus una illa te diffamabit, quam multæ virtutes commendabunt

Yep, what you described as TagsOnPage isn't a value, it's a method that might return one of two values, value A when it's defined or value B when value A is missing.

C# code:
public List<Tag> TagsOnPage() => this.CustomTags ?? this.FilteredTags;
Changing variables based on what's in another variable is the most common path to spaghetti code. Keep your variables as simple as possible, then move any logic into methods / functions that access those variables.

Rocko Bonaparte
Mar 12, 2002

Every day is Friday!

epswing posted:

What I’m curious about is what’s missing from C# string formatting? Or is this just for fun?

I'm implementing Python % operator in a personal interpreter project so I'm having to accept printf-like formatting operators.


NihilCredo posted:

I had to do something very similar and I just wrote a FSM using StringBuilder.Append(char). Haven't had any issues and the performance has been fine, considering it's a very hot path (I use it for most logging). There are just three states: PlainText, PrintfParam, JustAfterPrintfParam.

Yeah I gave up and aimed for similar just because that regular expression smelled like an abandoned rake factory and I'd wind up in contortions dealing with something yet to be determined.

nielsm
Jun 1, 2009



Rocko Bonaparte posted:

I'm implementing Python % operator in a personal interpreter project so I'm having to accept printf-like formatting operators.

It's a mini language inside the language, just write a parser for it. You can try making it compile/translate to the .NET format string language if you think that's easier, just consider the impact for being able to accurately report errors.

fuf
Sep 12, 2004

haha

insta posted:

Use a method called GetTagsOnPage() and conditionally return which list you need?


NihilCredo posted:

Yep, what you described as TagsOnPage isn't a value, it's a method that might return one of two values, value A when it's defined or value B when value A is missing.

C# code:
public List<Tag> TagsOnPage() => this.CustomTags ?? this.FilteredTags;
Changing variables based on what's in another variable is the most common path to spaghetti code. Keep your variables as simple as possible, then move any logic into methods / functions that access those variables.

Yeah ok, thanks. I thought I was trying to do something clever but that makes sense.

One thing I'm realising about Blazor is that it's relatively easy to make changes filter though to components, but it's actually kind of a pain to trigger methods within components.

Like something like this:
C# code:
public List<Tag> TagsOnPage() => this.CustomTags ?? this.FilteredTags;
I could either do it in OnInitialized or OnParametersSet.
OnInitialized means it wouldn't reflect the creation of a CustomTags List that happens during the component's lifetime.
OnParametersSet would work but it fires after a change in any of the component parameters (including EventCallback's triggered by the component itself) so it seems kind of inefficient.

The other option which I'm ending up using a lot is moving methods into a separate service, which is fine but it interrupts the neatness of having everything packaged into a single neat little component.

NihilCredo
Jun 6, 2011

iram omni possibili modo preme:
plus una illa te diffamabit, quam multæ virtutes commendabunt

fuf posted:

One thing I'm realising about Blazor is that it's relatively easy to make changes filter though to components, but it's actually kind of a pain to trigger methods within components.

Like something like this:
C# code:
public List<Tag> TagsOnPage() => this.CustomTags ?? this.FilteredTags;
I could either do it in OnInitialized or OnParametersSet.
OnInitialized means it wouldn't reflect the creation of a CustomTags List that happens during the component's lifetime.
OnParametersSet would work but it fires after a change in any of the component parameters (including EventCallback's triggered by the component itself) so it seems kind of inefficient.

Either I didn't understand your post, or you didn't understand the point of a getter like that.

You don't "trigger" it on any particular event. You don't call TagsOnPage() to set or update anything. Rather, whenever another component needs to access the list of tags on the page, it invokes TagsOnPage() every single time, so it always reflects the presence/absence of CustomTags. A function call and a null-coalescing operator costs effectively nothing.

Kyte
Nov 19, 2013

Never quacked for this
Given the usage pattern, wouldn't it make more sense to make it a property?

C# code:
public List<Tag> TagsOnPage => this.CustomTags ?? this.FilteredTags;
Literally the same minus the parentheses. Not all properties are autoprops.

NihilCredo
Jun 6, 2011

iram omni possibili modo preme:
plus una illa te diffamabit, quam multæ virtutes commendabunt

Kyte posted:

Literally the same minus the parentheses. Not all properties are autoprops.

Sure, it's a style choice.

One practical consideration might be that a lot of reflection-based tooling looks at properties but ignores methods. E.g. serializers, designers, data binders, ORMs. So if you want them to "see" TagsOnPage, make it a property; if you want them to ignore it, make it a method (you could also look up the right attribute to make them ignore it, but making it a getter method works every time on all such tools, although it's less explicit).

fuf
Sep 12, 2004

haha

NihilCredo posted:

Either I didn't understand your post, or you didn't understand the point of a getter like that.

Yeah sorry my bad, I wasn't actually looking at your code properly.

Using a getter like that (either as a method or a property) does actually look like it could help.

Man there's still so much basic stuff I don't know. This is the problem with doing like one day of courses on LinkedIn Learning and then just jumping in. I think I need to go back to doing some courses soon now that I have a sense for what the actual problems are and why the solutions are good solutions.

riichiee
Jul 5, 2007
I've got a WPF .NET Framework application that needs to be distrubuted externally.

Previously I just published it on an internal network share with ClickOnce, and auto-update just took care of itself.

Any ideas on what the cleanest, most reliable way to do this is?

We've got a sharepoint site that the external parties have access to, was thinking of hosting the files there. Not sure how well that would work with the ClickOnce auto-update though?

I considered hosting it on Azure blobs, but whenever setup.exe is downloaded from there, most anti-virus's throw a fit at having to run it. I could probably spin up an instance of IIS for it, but that feels like overkill.

Using Squirrel + Github involves downgrading to .NET Framework 4.6.1, which I'm not sure what knock-on effects that'll have.

Maybe I'm over complicating this!?

Any ideas?

Kyte
Nov 19, 2013

Never quacked for this
A github page, perhaps?

Adbot
ADBOT LOVES YOU

Just-In-Timeberlake
Aug 18, 2003

riichiee posted:

I've got a WPF .NET Framework application that needs to be distrubuted externally.

Previously I just published it on an internal network share with ClickOnce, and auto-update just took care of itself.

Any ideas on what the cleanest, most reliable way to do this is?

We've got a sharepoint site that the external parties have access to, was thinking of hosting the files there. Not sure how well that would work with the ClickOnce auto-update though?

I considered hosting it on Azure blobs, but whenever setup.exe is downloaded from there, most anti-virus's throw a fit at having to run it. I could probably spin up an instance of IIS for it, but that feels like overkill.

Using Squirrel + Github involves downgrading to .NET Framework 4.6.1, which I'm not sure what knock-on effects that'll have.

Maybe I'm over complicating this!?

Any ideas?

I currently do this by publishing the app to an Amazon S3 bucket, but that'll probably give you the same issue as using an Azure blob solution.

Are you signing the ClickOnce manifests? Pretty sure our users only have to explicitly allow it on the first install, after that it just runs/updates without issue.

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