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
kitten emergency
Jan 13, 2008

get meow this wack-ass crystal prison

Munkeymon posted:

They might prefer that, but I've worked with a lot of (usually older) guys who are just like "eh throw a WSDL out there and we're good" so I figure it's absence would cause a bunch of people a lot of angst. Sure, their clients/servers probably aren't just going to just stop working after a Windows update someday but they'd still be missing out stuck on the maintenance-mode framework.

I mean, I had to explain to an MS person that not every .NET developer can use NuGet this week so it might surprise you that there's some disconnect between the product side and the lived experience of their user base.

Adbot
ADBOT LOVES YOU

Munkeymon
Aug 14, 2003

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



uncurable mlady posted:

I mean, I had to explain to an MS person that not every .NET developer can use NuGet this week so it might surprise you that there's some disconnect between the product side and the lived experience of their user base.

JFC :cripes:

redleader
Aug 18, 2005

Engage according to operational parameters

Munkeymon posted:

Was all SOAP support part of WCF? As in, the input WSDL, get generated classes and methods that Just Work thing isn't something Standard will support?

i don't know poo poo about wcf except (1) it is colossally overengineered and (2) everyone just uses it for soap services and ignores the other 95% of wcf

ms used to have their xml web services "framework" - put a couple of attributes on a class and like magic you have a soap server. this - being easy to use and understand - was obviously deprecated in favor of wcf's wall-like learning curve and architectural astronauting

raminasi
Jan 25, 2005

a last drink with no ice
I write plugins for some .NET software and the host software is still only targeting .NET 4.5. I really want the shiny new stuff, but I also really don't want to try to explain to users why they need an additional framework update to be able to use my poo poo.

bobua
Mar 23, 2003
I'd trade it all for just a little more.

I'm working on building more advanced custom controls and larger datasets than I'm used to and I keep coming across small questions that are tough to google. I'm wondering if anyone has any general resource recommendations on the topic.


For example, I'm working with 'appointment' information that needs to be laid out across dates. Basically a custom scheduler\calendar. I pull the information around the selected date as it changes. Right now I just keep it simple and rerender everything on changes, but I'm sure I'll want to do more of a caching mechanism. On top of that, I don't have a full grasp on when certain things happen. If I have a usercontrol that contains a sub user control that's not immediately visible(like a dropdown button's dropdown contents) and I render 50 of the user controls, are the dropdown button's contents and their binding instantiated immediately or when the button is pressed? Is there an MVVM friendly way to control that?

I can step through\debug my way to an answer on that specific question, but what I'm looking for is a deeper resource I can sit down and read to get a bigger picture of the concepts. This is all WPF btw.

His Divine Shadow
Aug 7, 2000

I'm not a fascist. I'm a priest. Fascists dress up in black and tell people what to do.
Maybe this is peanuts to you guys but I have been struggling to do a simple "WHERE IN" query using Linq. I have a list of integers and wanted to do a select query on those, well it just wasn't possible, the extension .Contains was not supported for the field, seems like it wanted the field to be a string type.

I tried all kinds of solutions, even custom extensions but the answer which finally worked was super simple, go figure. Just swap the query around, instead of writing:

code:
where table.tableId.Contains(list)
just swap it around and write

code:
where list.Contains(table.tableId)

Mata
Dec 23, 2003
I would guess tableId is an integer or some such type, and doesnt have a Contains method.
It's hard to imagine an Id containing a list..

Cuntpunch
Oct 3, 2003

A monkey in a long line of kings

His Divine Shadow posted:

Maybe this is peanuts to you guys but I have been struggling to do a simple "WHERE IN" query using Linq. I have a list of integers and wanted to do a select query on those, well it just wasn't possible, the extension .Contains was not supported for the field, seems like it wanted the field to be a string type.

I tried all kinds of solutions, even custom extensions but the answer which finally worked was super simple, go figure. Just swap the query around, instead of writing:

code:
where table.tableId.Contains(list)
just swap it around and write

code:
where list.Contains(table.tableId)

Just to try to help explain: This isn't a LINQ-related thing, it seems like you might be coming from and thinking in a declarative language like SQL or R:

When we talk about 'table.tableId' we're not talking about *the field*, we're talking specifically about the single value for a single instance. In which case, trying to call the .Contains() method on it is subject to the type of that value.

In your case, there's also a little bit of an overlap: the String type exposes a Contains() method, but LINQ exposes a Contains() extension method on IEnumerable<T>. This is possibly why tooling or otherwise would be hinting that the type was mismatched and .Contains() wasn't possible.

To your more direct problem, though, you can LINQ-ify the solution a bit more (possibly, depending on the datastructures). I'm assuming from your description that you want to pull some amount of data out of *many* tables and that the data is all of the same type? And that you're iterating across tables to check whether their ID matches what you're looking for.

In which case, have you tried something like:
code:
 list.SelectMany(i => tables.Single(table => table.tableId == i).Rows) 
Alternately, if you are used to a declarative SQL-like syntax, have you looked into LINQ's query syntax?

His Divine Shadow
Aug 7, 2000

I'm not a fascist. I'm a priest. Fascists dress up in black and tell people what to do.
I am actually using the LINQ's query syntax already and yeah I'm used to SQL. The query compiles a list of items, it's very similar to a query which would list a bunch of items for a category, except instead of going by a category, it uses an array of id's instead, so I can create a custom list of any items I want to. Which is sometimes handy.

code:
int [] ids = new int[]  { 1, 2, 3, 4 }; //list is not defined manually like this, it's created based on inputs elsewhere, just for this example

var query = (from p in _context.table
                         *snip* 
                         where ids.Contains(p.tableId)
                         *snip* 
                          select new viewmodelname
                          {
                              /*stuff*/

                          }).ToList();

His Divine Shadow fucked around with this message at 09:51 on Sep 27, 2019

Cuntpunch
Oct 3, 2003

A monkey in a long line of kings

His Divine Shadow posted:

I am actually using the LINQ's query syntax already and yeah I'm used to SQL. The query compiles a list of items, it's very similar to a query which would list a bunch of items for a category, except instead of going by a category, it uses an array of id's instead, so I can create a custom list of any items I want to. Which is sometimes handy.

code:
int [] ids = new int[]  { 1, 2, 3, 4 }; //list is not defined manually like this, it's created based on inputs elsewhere, just for this example

var query = (from p in _context.table
                         *snip* 
                         where ids.Contains(p.tableId)
                         *snip* 
                          select new viewmodelname
                          {
                              /*stuff*/

                          }).ToList();

code:
	from i in ids
	join t in _context.tables on i equals t.tableId into tables
	from table in tables
	select new viewmodelname {}

qsvui
Aug 23, 2003
some crazy thing
Is there a preference for using the keyword or function syntax for LINQ? I always used the functions since it seems like there are more of them than keywords so you could potentially do more but I don't program primarily in C# so maybe I'm wrong.

mystes
May 31, 2006

qsvui posted:

Is there a preference for using the keyword or function syntax for LINQ? I always used the functions since it seems like there are more of them than keywords so you could potentially do more but I don't program primarily in C# so maybe I'm wrong.
LINQ query syntax is not very popular nowadays.

Cold on a Cob
Feb 6, 2006

i've seen so much, i'm going blind
and i'm brain dead virtually

College Slice
A few things are easier to express in query syntax (let, joins, etc) but functionally they are equivalent.

Cuntpunch
Oct 3, 2003

A monkey in a long line of kings
While the query syntax has a few benefits, I personally find it to be somewhat disruptive to the flow of the code.

Hammerite
Mar 9, 2007

And you don't remember what I said here, either, but it was pompous and stupid.
Jade Ear Joe
I am familiar with SQL but I don't use the SQL-like syntax for LINQ in C# code. I find it jarring for a couple of reasons, I think: firstly it is different enough from SQL that the similarities are not really helpful, secondly I don't find it easy to think in the two different paradigms when they are brought into such close juxtaposition. If I'm looking at the C-style syntax of C# code, I prefer to think in the "do this with it, then do this, then do this" paradigm of the method-chaining syntax.

LongSack
Jan 17, 2003

I'm trying to async my Data Access Layer code. I'll pick one class, as an example.

Here are the interfaces i'm coding to:
C# code:
public interface IDAL<TDTO, TEntity>
{
    int Count { get; }
    Task Insert(TDTO dto);
    Task Update(TDTO dto);
    Task Delete(TDTO dto);
    Task Delete(int id);
    IEnumerable<TDTO> Get(Expression<Func<TEntity, bool>> pred = null);
    TDTO Read(int id);
}

public interface IPortfolioDAL : IDAL<Portfolio, PortfolioEntity>
{
    Portfolio Read(string name);
}
For most methods, it seems fairly straight forward, for example the Insert method looks like:
C# code:
public async Task Insert(Portfolio dto)
{
    PortfolioEntity entity = _mapper.Map<PortfolioEntity>(dto);
    using var context = new Context();
    await context.Portfolios.AddAsync(entity);
    await context.SaveChangesAsync();
    dto.Id = entity.Id;
}
My question is about the Get method. I couldn't figure out a way to return a task for this, and ended up doing it like this:
C# code:
public IEnumerable<Portfolio> Get(Expression<Func<PortfolioEntity, bool>> pred = null)
{
    using var context = new Context();
    List<PortfolioEntity> entities;
    if (pred is null)
    {
        entities = (from p in context.Portfolios 
                    orderby p.Name 
                    select p).AsNoTracking().ToListAsync().GetAwaiter().GetResult();
    }
    else
    {
        entities = (from p in context.Portfolios
                    .Where(pred)
                    orderby p.Name
                    select p).AsNoTracking().ToListAsync().GetAwaiter().GetResult();
    }
    return _mapper.Map<List<Portfolio>>(entities);
}
Is there a better way to handle the async reads and the mapping (and yes, I know, I'm mixing my LINQ syntaxes, that's a separate matter)?
TIA

redleader
Aug 18, 2005

Engage according to operational parameters
Your interface should return Task<IEnumerable<TDTO>> from Get(). Then your implementation can be an async method and you can use await for those ToListAsync() calls.

ljw1004
Jan 18, 2005

rum

LongSack posted:

My question is about the Get method. I couldn't figure out a way to return a task for this, and ended up doing it like this:
C# code:
public IEnumerable<Portfolio> Get(Expression<Func<PortfolioEntity, bool>> pred = null)

Depending on when the async work happens, another option is IAsyncEnumerable - https://dotnetcoretutorials.com/2019/01/09/iasyncenumerable-in-c-8/

Hammerite
Mar 9, 2007

And you don't remember what I said here, either, but it was pompous and stupid.
Jade Ear Joe
When you throw an ArgumentException, should the paramName always be the name of the parameter to the immediate method? Or can/ought it be the name of the parameter to the publicly-visible method that was called by client code?

Like if I have a public method Foo(string s) and a private method Bar(int x) and Foo calls Bar which throws ArgumentException because there is something wrong with x (which was derived from s, and let's assume here that I know when throwing that there must have been something wrong with s and what that something is). Ought the paramName to be "x" (because that's what the parameter to Bar was called), or ought it to be "s" (because that's the name of the faulty parameter to the publicly-visible method that got called)? Or is it a matter of style?

SirViver
Oct 22, 2008

Hammerite posted:

When you throw an ArgumentException, should the paramName always be the name of the parameter to the immediate method? Or can/ought it be the name of the parameter to the publicly-visible method that was called by client code?

Like if I have a public method Foo(string s) and a private method Bar(int x) and Foo calls Bar which throws ArgumentException because there is something wrong with x (which was derived from s, and let's assume here that I know when throwing that there must have been something wrong with s and what that something is). Ought the paramName to be "x" (because that's what the parameter to Bar was called), or ought it to be "s" (because that's the name of the faulty parameter to the publicly-visible method that got called)? Or is it a matter of style?

The one of the immediate method, definitely. And then the calling public method should catch the ArgumentException around the private method call and wrap it in its own ArgumentException (or mask/hide it entirely) detailing what was wrong with its immediate parameter that led to the inner exception. That's the platonic ideal, IMO, though I think even the .NET Framework library doesn't do this everywhere (or even only does it rarely), and I don't believe it's really feasible to do so for anything non-trivial; also kinda why checked exceptions aren't a thing in .NET.

The outside caller doesn't and shouldn't care about the classes private implementation details, but conversely and IMO more importantly the private method also shouldn't "know" where it is called from. If you ever call that private method from two different points then referring to one specific caller's parameter name would end up being completely wrong. If in doubt, I'd definitely err on the side of providing the outside caller with "useless" implementation detail specific information (e.g. internal parameter names) rather than trying to be helpful by tuning the exception info to the caller's current implementation.

Bruegels Fuckbooks
Sep 14, 2004

Now, listen - I know the two of you are very different from each other in a lot of ways, but you have to understand that as far as Grandpa's concerned, you're both pieces of shit! Yeah. I can prove it mathematically.

Hammerite posted:

When you throw an ArgumentException, should the paramName always be the name of the parameter to the immediate method? Or can/ought it be the name of the parameter to the publicly-visible method that was called by client code?

Like if I have a public method Foo(string s) and a private method Bar(int x) and Foo calls Bar which throws ArgumentException because there is something wrong with x (which was derived from s, and let's assume here that I know when throwing that there must have been something wrong with s and what that something is). Ought the paramName to be "x" (because that's what the parameter to Bar was called), or ought it to be "s" (because that's the name of the faulty parameter to the publicly-visible method that got called)? Or is it a matter of style?

I would only ever throw an argument exception on a public method - the point of the argument exception is to tell the caller that they're using the class incorrectly. For the labeling, I'll generally go with the interface definition convention if the code is not available to the caller (e.g. it's packaged as a dll), or the method definition if the code is available to the caller (e.g. it's just a static class in another project) - although ideally interface definition and method definition would be the same unless there's some reason for them not to be, like a convention. With private methods, you should just write the code so it provides the appropriate arguments, but if for some reason this isn't an option or you want your tests to break when a parameter is missing in a private method rather than at the invariable null reference exception, use Assert().

Bruegels Fuckbooks fucked around with this message at 11:39 on Oct 2, 2019

Hammerite
Mar 9, 2007

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

SirViver posted:

The outside caller doesn't and shouldn't care about the classes private implementation details, but conversely and IMO more importantly the private method also shouldn't "know" where it is called from. If you ever call that private method from two different points then referring to one specific caller's parameter name would end up being completely wrong.

You are correct about this. I omitted from my original post that I hadn't meant to imply that there should be any coupling between the public method and the private one. The implementation I had in mind (which wasn't communicated in my post) is that if Bar() were to use Foo()'s name for the parameter, it would have to take an additional string parameter which would be the name of the parameter to the public method.

code:
public void Foo(string s)
{
    Bar(int.Parse(s), nameof(s));
}

private void Bar(int x, string parameterName)
{
    if (x <= 0)
    {
        throw new ArgumentException("Should be positive", parameterName);
    }
}

Hammerite
Mar 9, 2007

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

Bruegels Fuckbooks posted:

I would only ever throw an argument exception on a public method - the point of the argument exception is to tell the caller that they're using the class incorrectly.

The reasoning you purport to give here is a non-sequitur. There is no reason why the public method can't delegate its checks on its arguments to another method (private or otherwise), and in that way ensure that the caller is told that they're using the class incorrectly.

Perhaps I have a lot of checks to run against the argument, and the entire purpose of that private method is "check this argument for things that could be wrong with it" - and I want the call to it to be a one-liner, the better to keep the implementation of the public method simple and readable.

Both of you advocate for putting checks in the public method itself - not indirectly via another method - either in the form of checking parameters or wrapping exceptions coming from the private method. The problem I have with this is that it's boilerplate. What if I have several public methods that all take the same parameter, and I want to carry out the same check (or laundry list of checks) on that parameter in each one? The proposition that one should repeat the same checks on the argument in each of the public methods directly contradicts the idea of "don't repeat yourself"! The suggestion of wrapping the private method call in a try-catch and rethrowing is less bad but is still boilerplate.

SirViver
Oct 22, 2008

Hammerite posted:

...take an additional string parameter which would be the name of the parameter to the public method.

Well, if the sole purpose of "Bar" is to only validate a parameter (so it would in actuality be something like "ValidatePositiveInteger" or whatever), then that seems perfectly fine to me as the parameter name is actually relevant information to that method.
E: ^ You really should have specified so in your original question however, as that detail actually makes a whole lot of difference :)

E: I'll leave this here, though it actually doesn't apply to your case:
If, however, Bar is also doing something else with the validation just being incidental then I'd still choose the wrapped exception approach, because adding an extra parameter just to provide additional context information for possible exceptions seems dirty, and who is to say that the input value is derived from a parameter in the first place?

SirViver fucked around with this message at 12:18 on Oct 2, 2019

Hammerite
Mar 9, 2007

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

SirViver posted:

Well, if the sole purpose of "Bar" is to only validate a parameter (so it would in actuality be something like "ValidatePositiveInteger" or whatever), then that seems perfectly fine to me as the parameter name is actually relevant information to that method.
E: ^ You really should have specified so in your original question however, as that detail actually makes a whole lot of difference :)

Yes, it is the problem everyone runs into occasionally where you have been thinking about something for a while and when you come to explain it to someone else you omit important details because you forget that they need to be explained because they seem obvious to you (because you have been taking them for granted in your thinking for a while).

The limited endorsement of using the public method's name for the parameter reassures me somewhat that my thinking isn't too out of whack on this. The reason why I'm humming and hahing about it is that code I'm writing at the moment will typically be used elsewhere in the same solution, so in a context where a full stack trace from inside the library is available. I'm not sure whether it would be unhelpful to use the name of the parameter at the class's top-level interface, when from the point of view of someone reading the stack trace that's somewhere in the middle of the call stack - therefore it might seem more arbitrary than just using the parameter name for the method where the exception was actually thrown.

Jethro
Jun 1, 2000

I was raised on the dairy, Bitch!
What if you had a public method with multiple parameters that were then separately passed to the private method? In that case, the name of the parameter in the private method doesn't provide enough useful information. "Thanks for letting me know one of the strings I passed as a parameter was garbage. Could you tell me which one?"

Or another way to think about it, why do you throw an ArgumentException? To tell the caller they passed in garbage that you can't handle. Well, it's the parameter from the public method that is the source of the garbage, so that's what should be in the exception (at least at some level). If the exception thrown never references the public interface, how is the consumer supposed to know that it was something they did which caused the problem?

So, you want the exception to reference the specific public method parameter that caused the problem. The way to do that is to either wrap the call to the private method in a try-catch so you can add the public parameter name to a new ArgumentException which has the ArgumentException from the private method as an inner exception; have the parameter names just always be the same by convention (which is apparently not possible in your case); or pass the public parameter name to the private method (which, as mentioned, you should probably only ever do in this one specific case where the private method is only used for validation). And with the latter two possibilities, I would still be wary since even though the parameter name is the same, it might not be clear that the problem was the parameter they sent to the public method since it's not the public method that threw the exception.

Eggnogium
Jun 1, 2010

Never give an inch! Hnnnghhhhhh!
I don't think you can really give one answer to this question generally. Importantly, it's not a style question as you asked but a UX question, where the user in this case is a developer or ops. Imagine the user getting this exception: are they a developer at another organization who's just using your public interface and does not have access to the implementation source? Is it just you working in another project? Which would be more helpful for diagnosing what's wrong in those cases?

Edit: And just to be explicit, if the user is you I'd just throw the exception from inside the private and be done. You have the tools to easily trace back whether there's a bug in the derivation of x from s, or if Foo is being passed bad data. If the developer is implementation-blind then I would have the ArgumentException thrown from Foo and about s. Probably I would have a bool ValidateSParam(string), have Foo call it and throw if false.

Eggnogium fucked around with this message at 16:55 on Oct 2, 2019

Cuntpunch
Oct 3, 2003

A monkey in a long line of kings
The other not-quite indicated problem here is that if the goal is to reduce boilerplate, even Bar is getting moved at some point, because it's only functional for one type in one case.
So you either end up with a static class of
public static void ValidatePositiveIntegers(int i, string originalName)
public static void ValidateNonZeroUnsigned(uint i, string originalName)

clones everywhere for every possible corner case, or you end up doing
public void ValidateSomething<T>(T inputValue, string originalName, Func<T,bool> validation)

But that means needing a way to get *at* all of this, so you end up with a static class full of:

public void ValidatePositiveIntegers(int i, string originalName) => ValidateSomething(i,originalName, (i) => i > 0);

Otherwise, what you might look into is wrapping primitive types in types that handle the logic you're after. You wrap your int at one place, at the edge of your code, and then afterwards you have a nice pretty PositiveNumber type to use and you can trust that it's going to be a positive number.

LongSack
Jan 17, 2003

redleader posted:

Your interface should return Task<IEnumerable<TDTO>> from Get(). Then your implementation can be an async method and you can use await for those ToListAsync() calls.

Ended up reworking it like this:
C# code:
public async Task<List<Portfolio>> Get(Expression<Func<PortfolioEntity, bool>> pred = null)
{
    using var context = new Context();
    List<PortfolioEntity> entities;
    if (pred is null)
    {
        entities = await context.Portfolios
            .OrderBy(x => x.Name)
            .Select(x => x).AsNoTracking().ToListAsync();
    }
    else
    {
        entities = await context.Portfolios
            .Where(pred)
            .OrderBy(x => x.Name)
            .Select(x => x).AsNoTracking().ToListAsync();
    }
    return _mapper.Map<List<Portfolio>>(entities);
}
And now the Read methods look like this:
C# code:
public async Task<Portfolio> Read(int id) => (await Get(x => x.Id == id)).SingleOrDefault();

public async Task<Portfolio> Read(string name) => (await Get(x => x.Name == name)).SingleOrDefault();
Had to use Task<List<>>, it didn't like using IEnumerable

Funking Giblet
Jun 28, 2004

Jiglightful!

LongSack posted:

Ended up reworking it like this:
C# code:
public async Task<List<Portfolio>> Get(Expression<Func<PortfolioEntity, bool>> pred = null)
{
    using var context = new Context();
    List<PortfolioEntity> entities;
    if (pred is null)
    {
        entities = await context.Portfolios
            .OrderBy(x => x.Name)
            .Select(x => x).AsNoTracking().ToListAsync();
    }
    else
    {
        entities = await context.Portfolios
            .Where(pred)
            .OrderBy(x => x.Name)
            .Select(x => x).AsNoTracking().ToListAsync();
    }
    return _mapper.Map<List<Portfolio>>(entities);
}
And now the Read methods look like this:
C# code:
public async Task<Portfolio> Read(int id) => (await Get(x => x.Id == id)).SingleOrDefault();

public async Task<Portfolio> Read(string name) => (await Get(x => x.Name == name)).SingleOrDefault();
Had to use Task<List<>>, it didn't like using IEnumerable

Just a small comment here, never use a query where a single FindAsync might work,
ORMs tend to do a lot of optimisations around getting single items, vs queries, so try to use the native methods where possible. (Read(int id) being the culprit here)

Funking Giblet fucked around with this message at 20:04 on Oct 2, 2019

Cuntpunch
Oct 3, 2003

A monkey in a long line of kings

LongSack posted:

Had to use Task<List<>>, it didn't like using IEnumerable

I'd be curious what error you were seeing, as IEnumerable is in some possible ways preferable than List, and there's nothing that would prevent it being used as a Task's T

LongSack
Jan 17, 2003

Cuntpunch posted:

I'd be curious what error you were seeing, as IEnumerable is in some possible ways preferable than List, and there's nothing that would prevent it being used as a Task's T

I went to change it back to get the error message, and now i'm not getting one ... Must have been something else, I'm changing them back to IEnumerable

Funking Giblet posted:

Just a small comment here, never use a query where a single FindAsync might work,
ORMs tend to do a lot of optimisations around getting single items, vs queries, so try to use the native methods where possible. (Read(int id) being the culprit here)

I use the Get method for the Read methods because it keeps all the "get" logic in a single method, so if i need to mess around with .Includes, or whatever it's all in one spot. Also, correct me if I'm wrong, but since all the reads use .AsNoTracking(), there won't be anything to find, will there?

Withnail
Feb 11, 2004
In C#, what's the fastest and cleanest was to join data from two lists based on matching data in two columns.

So List 1 is a very large list that looks similar to this, with no rules on uniqueness

Part Number | Project Number | Part Type | Note (no data)

And List 2 is a much smaller List of meta data that looks like this, where Part Number and Project Number and always unique

Part Number | Project Number | Note (with data)

List 2 is essentially a many to one back to List 1. So anywhere in List 1 where there is a corresponding item in List 2 with the Part Number and Project number, I want to add in the Note value back to List 1.


Iterating and doing linq where is terrible performance. I can build List 2 to into a nested dictionary and use key lookups but it's ugly.

Is there a more graceful solution that I'm not considering?

Nth Doctor
Sep 7, 2010

Darkrai used Dream Eater!
It's super effective!


e: thought I was in the SQL thread.

New Yorp New Yorp
Jul 18, 2003

Only in Kenya.
Pillbug

Withnail posted:

In C#, what's the fastest and cleanest was to join data from two lists based on matching data in two columns.

So List 1 is a very large list that looks similar to this, with no rules on uniqueness

Part Number | Project Number | Part Type | Note (no data)

And List 2 is a much smaller List of meta data that looks like this, where Part Number and Project Number and always unique

Part Number | Project Number | Note (with data)

List 2 is essentially a many to one back to List 1. So anywhere in List 1 where there is a corresponding item in List 2 with the Part Number and Project number, I want to add in the Note value back to List 1.


Iterating and doing linq where is terrible performance. I can build List 2 to into a nested dictionary and use key lookups but it's ugly.

Is there a more graceful solution that I'm not considering?

Did you try using Join? This seems like your scenario: https://stackoverflow.com/questions/6253656/how-do-i-join-two-lists-using-linq-or-lambda-expressions

Withnail
Feb 11, 2004
Does the join return new objects? List 1 are objects tracked in an entity framework context so i don't think I can return new objects.

NihilCredo
Jun 6, 2011

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

Withnail posted:

Does the join return new objects? List 1 are objects tracked in an entity framework context so i don't think I can return new objects.

It returns whatever you select, which is normally an anonymous type with the combined properties. But if you need to preserve identity you can do

code:
  list1.Join(list2
             ,  item1 => new { item1.PartNr, item1.ProjectNr }
             ,  item2 => new { item2.PartNr, item2.ProjectNr }
             , (item1, item2) => { item1.Note = item2.Note; return item2 }
             )
which is un-idiomatic but very efficient.

Also, IEnumerable.Join does a hash and match (as opposed to running an arbitrary boolean check) so it should be much more efficient than a manual double loop. Can't be sure without testing, though.

If you want to do the optimization manually, it shouldn't be too ugly though, even in c#:

code:
   var quickLookup = list2.ToDictionary ( x => new { x.PartNr, x.ProjectNr }, x => x.Note );
   for (var item1 in list1) { item1.Note = quickLookup[ new { item1.PartNr, item2.ProjectNr }] }
Anonymous types can be used reasonably efficiently as dictionary keys (they have proper GetHashCode implementations), but if you've got a truly humongous List1 I suppose you could roll your own:

code:
			
   var evenQuickerLookup = list2.ToDictionary ( x => x.PartNr * (PROJECT_NUMBER_MAXIMUM_VALUE + 1) + x.ProjectNr , x => x.Note );
   for (var item1 in list1) { item1.Note = evenQuickerLookup[  item1.PartNr * (PROJECT_NUMBER_MAXIMUM_VALUE + 1) + item1.ProjectNr ] }

NihilCredo fucked around with this message at 23:12 on Oct 2, 2019

Withnail
Feb 11, 2004
Thanks for the suggestions. I'm working with parts lists spacecraft, so extremely large collections.

SirViver
Oct 22, 2008

Withnail posted:

Is there a more graceful solution that I'm not considering?
Assuming you can use .NET Framework 4.7, just use value tuples.
C# code:
var list2Dict = list2.ToDictionary(e => (e.PartNr, e.ProjectNr));

foreach (var list1Entry in list1)
{
    if (list2Dict.TryGetValue((list1Entry.PartNr, list1Entry.ProjectNr), out var list2Entry))
    {
        list1Entry.Note = list2Entry.Note;
    }
}

Adbot
ADBOT LOVES YOU

Mr Shiny Pants
Nov 12, 2012
Is it safe to use recursive functions within AspNetCore?

I've written a Websocket handler for Giraffe, which runs on Kestrel, and after banging my head against the desk on how it all fits together I finally stumbled upon the part that you need to block the WebSocket receiver within the pipeline.

I was under the impression that the pipeline would receive the socket and hand it off to some other function thereby ending the pipeline, but it seems it remains in flight for the duration of the connection.

Inside the middleware I've written a recursive function that handles the received message and returns another receiveasync effectively blocking that specific pipeline.

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