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
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
Ignore the above. I found the culprit. Typically, the previous coder had no idea how async code works:
code:
public void Dispose()
{
    DoDispose();
}

private void DoDispose(int retryCount = 0)
{
    // delay disposal of services scope for up to 10 seconds if background cache item refresh is in progress
    // note: required to prevent EF context from being immediately disposed on request end
    if (retryCount < 10 && CacheService().BackgroundRefreshInProgress)
    {
        Task.Run(async () =>
        {
            await Task.Delay(1000);
            DoDispose(retryCount + 1);
        });
    }
    else
    {
        Scope.Dispose();
    }
}
As you can see, this is not going to do what the comment claims at all.

Adbot
ADBOT LOVES YOU

ljw1004
Jan 18, 2005

rum

a hot gujju bhabhi posted:

As you can see, this is not going to do what the comment claims at all.

Oh! It looks to me like it will do what the comment claims... Could you talk through your reasoning?

What I think this code will do: starting at time=0, and thereafter once a second, it will check whether BackgroundRefreshInProgress is true or whether 10 once-per-second retries are up. If it is true and retries aren't up, it will wait a second and try again. The Scope.Dispose() call will either run on the calling thread and propagate exceptions (if done at time=0) or on a background thread and the exception will be unhandled.

(Uncertain: I don't know if this overload of Task.Run takes a void-returning async lambda - in which case I think any unhandled exception will get passed to the execution-context exception handler - or if it takes a Task-returning async lambda, in which case the exception will be silently ignored.)

Jen heir rick
Aug 4, 2004
when a woman says something's not funny, you better not laugh your ass off

ljw1004 posted:

Oh! It looks to me like it will do what the comment claims... Could you talk through your reasoning?


Not the op, but what I think happens is: the runtime calls dispose right before garbage collection. It doesn’t know to wait for the thread to complete. Race condition ensues, program blows up.

EssOEss
Oct 23, 2006
128-bit approved
In general, the pattern of Dispose being called from the finalizer before GC is such a rare scenario that it would be better if everyone pretended it did not exist - it confuses people more often than not. Yes, for releasing some unmanaged resources it is what needs to happen but if we are talking of managed resources, you should only ever be calling Dispose() explicitly (or via using block) and there should not be any finalizer defined. We can be pretty sure that Dispose() is being called here intentionally and not by a finalizer, as the Scope variable sounds like a data context of some sort (a managed object, not an unmanaged resource like a file on disk or some raw handle).

While the code does not impress in some ways, I would also concur that the implementation seems to match the comment. It just starts a new task with a delay and calls itself again a maximum of 10 times or until BackgroundRefreshInProgress is false, after which it really disposes the Scope.

Jen heir rick
Aug 4, 2004
when a woman says something's not funny, you better not laugh your ass off
Maybe, but the op mentioned in this, or another thread that his service was crashing intermittently with an exception related to dispose, and he said he tracked it down to this method. My guess is some kind of race condition.

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
Hmm maybe there's more going on than I thought. After looking at it again I can see what you guys are saying, but making that part synchronous so that disposal had to wait for the CacheService to be finished fixed the problem. I'll do my best to explain what this thing is doing, I'd really like to understand why it's breaking, seems like my initial thought was wrong.

The class is called UnitOfWork and it extends a base class called ServiceScope. The constructor creates a new Autofac container with a bunch of registrations set to the Lifetime scope. The constructor then immediately calls "BeginLifetimeScope".

Among those registrations is an Entity Framework context.

When I first came across that code, I read the comment as "wait for the CacheService to finish before disposing this class" so I thought the code was wrong, but I see what you guys are saying now. But clearly something about this logic is causing the DbContext to be disposed before it should be, anyone have any ideas what it could be?

Possibly worth noting that the Dispose method is not called explicitly, but the instance is inside a using statement.

code:
using (var unitOfWork = new UnitOfWork())
{
    ....
}

putin is a cunt fucked around with this message at 22:02 on Jan 22, 2020

Faldoncow
Jun 29, 2007
Munchin' on some steak
The call to UnitOfWork.Dispose() will immediately return while the delay will continue asynchronously. The Dispose call returning immediately and allowing the calling code to continue could be the issue, if running synchronously and waiting for Dispose to complete fixed the problem you were having.

*Edit: Your coding horror post said maybe you were the horror, however I agree with ljw1004 about the uncertainty of the Task.Run here. What seems very very likely to me (and a quick test appears to confirm it) is that any exceptions thrown by DoDispose will get silently eaten by the anonymous Task returned by Task.Run. Maybe it's guaranteed that Scope.Dispose will never, ever throw an exception...but that's a pretty risky assumption to make from a completely different classes dispose function. While this code may do what the comment indicates, it's not great code either.

Faldoncow fucked around with this message at 22:37 on Jan 22, 2020

ljw1004
Jan 18, 2005

rum

a hot gujju bhabhi posted:

Hmm maybe there's more going on than I thought. After looking at it again I can see what you guys are saying, but making that part synchronous so that disposal had to wait for the CacheService to be finished fixed the problem.

I wonder if the call to Dispose() fails when run on a different thread due to a race condition somewhere?

Chrungka
Jan 27, 2015
Is Scope/CacheService() shared between multiple instances of UnitOfWork classes by any chance? While calling subsequent Dispose() should be a no-op, I have yet to see implementation of it that's thread safe. And EF Context is definitely not thread safe by any chance. So if my first assumption holds, it could be possible to:
1. First UnitOfWork does something.
2. First UnitOfWork tries to Dispose, but CacheService().BackgroundRefreshInProgress is true.
3. Second UnitOfWork does something.
4a. Second UnitOfWork tries to Dispose and CacheService().BackgroundRefreshInProgress is false.
4b. First UnitOfWork retries to Dispose at the same time, on different thread.
5. EF craps its pants.

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

Faldoncow posted:

The call to UnitOfWork.Dispose() will immediately return while the delay will continue asynchronously. The Dispose call returning immediately and allowing the calling code to continue could be the issue, if running synchronously and waiting for Dispose to complete fixed the problem you were having.

*Edit: Your coding horror post said maybe you were the horror, however I agree with ljw1004 about the uncertainty of the Task.Run here. What seems very very likely to me (and a quick test appears to confirm it) is that any exceptions thrown by DoDispose will get silently eaten by the anonymous Task returned by Task.Run. Maybe it's guaranteed that Scope.Dispose will never, ever throw an exception...but that's a pretty risky assumption to make from a completely different classes dispose function. While this code may do what the comment indicates, it's not great code either.

Yeah I thought (maybe incorrectly) that if the Dispose method completes then the runtime will immediately move on to disposing the resources belonging to the UnitOfWork, including the Autofac scope that was created in the constructor and is supposed to be (per the comment) disposed after a delay. This is where I think maybe I'm wrong, from what people are saying here?

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
I take it all back, it seems like the error is still there somewhere. gently caress my life. This is seriously making me miserable.

Funking Giblet
Jun 28, 2004

Jiglightful!
Does your UoW inject an EF context which has a different lifecycle? Calling Dispose in your UoW should not manually dispose the EF Context, it should live inside the same scope as the EF Context, and Autofac handles disposing both (or your UoW doesn't dipose EF, either or).

MisterZimbu
Mar 13, 2006
Entity Framework. Never.

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

MisterZimbu posted:

Entity Framework. Never.

I've used Entity Framework in heaps of projects over the years without issue. This isn't an Entity Framework issue.

redleader
Aug 18, 2005

Engage according to operational parameters
this product is the first time i've had the pleasure of working with ef core. i've been bitten by enough weird bugs from both ef and the way we use it that i'd be extremely cautious about using it in the future

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

redleader posted:

this product is the first time i've had the pleasure of working with ef core. i've been bitten by enough weird bugs from both ef and the way we use it that i'd be extremely cautious about using it in the future

What are the EF bugs you've been bitten by in EF Core?

EssOEss
Oct 23, 2006
128-bit approved
Well, it depends on the usage but I can certainly agree that horrible experiences with EF are possible. The main issues with EF (Core) that I have encountered are:

1. Migrations sometimes glitch out and EF thinks you need to apply some nonsense migration that is a no-op at best but can sometimes even be destructive.
2. LINQ->SQL transformations are extremely limited once you move on from basic "SELECT X FROM Y WHERE Z" types of queries.
3. No built-in bulk operation support.

These are the big pain points for me but life with EF is certainly better than without EF. However, I am careful to only use the "good parts" of EF. For example, I would never touch the inheritance hierarchy modeling functionality, I treat lazy loading as cancer to be rooted out, I keep any writable EF object lifetimes constrained to a single C# method (any EF objects returned are strictly read-only and never used for pushing data to the database).

It took me years to learn the right patterns and they are not really documented anywhere - if you read guides, they just tell you what features do, rarely when it is appropriate to use them (or even more importantly, to avoid them). Given that, I would say that you need to know how to use it and you'll be fine. Use it as a minimal C#<->SQL translation layer and you'll be fine. But if you just take all the documented features and apply them in a spray-and-pray pattern in your business logic, you will suffer greatly.

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

EssOEss posted:

Well, it depends on the usage but I can certainly agree that horrible experiences with EF are possible. The main issues with EF (Core) that I have encountered are:

1. Migrations sometimes glitch out and EF thinks you need to apply some nonsense migration that is a no-op at best but can sometimes even be destructive.
2. LINQ->SQL transformations are extremely limited once you move on from basic "SELECT X FROM Y WHERE Z" types of queries.
3. No built-in bulk operation support.

These are the big pain points for me but life with EF is certainly better than without EF. However, I am careful to only use the "good parts" of EF. For example, I would never touch the inheritance hierarchy modeling functionality, I treat lazy loading as cancer to be rooted out, I keep any writable EF object lifetimes constrained to a single C# method (any EF objects returned are strictly read-only and never used for pushing data to the database).

It took me years to learn the right patterns and they are not really documented anywhere - if you read guides, they just tell you what features do, rarely when it is appropriate to use them (or even more importantly, to avoid them). Given that, I would say that you need to know how to use it and you'll be fine. Use it as a minimal C#<->SQL translation layer and you'll be fine. But if you just take all the documented features and apply them in a spray-and-pray pattern in your business logic, you will suffer greatly.

Exactly, it's just about knowing what you're doing and designing your code appropriately. Lazy loading should never be a problem because you should really be projecting your queries to domain models at the outset. If you're gonna do an update, use a dto to represent the change, fetch the object and update and save it in the same method as you said.

Look I agree that getting to know EF well is hard work, but the benefits it brings are immense.

B-Nasty
May 25, 2005

EssOEss posted:


These are the big pain points for me but life with EF is certainly better than without EF. However, I am careful to only use the "good parts" of EF. For example, I would never touch the inheritance hierarchy modeling functionality, I treat lazy loading as cancer to be rooted out, I keep any writable EF object lifetimes constrained to a single C# method (any EF objects returned are strictly read-only and never used for pushing data to the database).


So, why bother with EF at all. You could just use something like Dapper with a simple DB Migration tool like DBUp or even homegrown. You don't get automatic migrations from your domain objects, but I always found that clunky and annoying anyway - like I was trying to guide it to write the DDL SQL I could just write myself, cleaner.

I've also used the Dapper Contrib stuff for when I'm feeling lazy and want something to generate INSERT/UPDATE statements for simple domain objects.

ThePeavstenator
Dec 18, 2012

:burger::burger::burger::burger::burger:

Establish the Buns

:burger::burger::burger::burger::burger:
Communicating over a network, communicating with a database using an agreed-upon protocol, deserializing data retrieved from a database, and serializing data that you want a database to write are all solved problems. Managing a database schema, managing data object lifecycles during application execution, scoping transactions, writing queries, and migrating data are problems that have solutions. These things are subtly different.

Micro-ORMs like Dapper only solve the former while traditional, thicker ORMs like EF claim to also be able to solve the latter. Often they're able to, until they can't. Complexity is inevitable if your application is going to do anything useful, and at some point you're going to have to make a change to your software that reveals someone made a bad assumption somewhere. That's how you get into poo poo like this:

a hot gujju bhabhi posted:

These days I'm working on a large repository of .NET Framework horseshit and I'm having some trouble with Entity Framework because the people who originally built this out clearly didn't know how to use it. I have a Windows service, with various unnecessary layers of complexity behind it, and something in there is causing the service to "randomly" crash.

Nothing is shown in the NLog logs, which means the application is obviously crashing before the exception can be written out. However, in the Event Viewer I found the crash exception:
code:
Application: MyAwfulWindowsService.exe
Framework Version: v4.0.30319
Description: The process was terminated due to an unhandled exception.
Exception Info: System.ObjectDisposedException
   at System.Data.Entity.Core.Objects.ObjectContext.ReleaseConnection()
   at System.Data.Entity.Core.Objects.ObjectContext+<ExecuteInTransactionAsync>d__3d`1[[System.Int32, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089]].MoveNext()
   at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(System.Threading.Tasks.Task)
   at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(System.Threading.Tasks.Task)
   at System.Data.Entity.Core.Objects.ObjectContext+<SaveChangesToStoreAsync>d__39.MoveNext()
   at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(System.Threading.Tasks.Task)
   at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(System.Threading.Tasks.Task)
   at System.Data.Entity.SqlServer.DefaultSqlExecutionStrategy+<ExecuteAsyncImplementation>d__9`1[[System.Int32, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089]].MoveNext()
   at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(System.Threading.Tasks.Task)
   at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(System.Threading.Tasks.Task)
   at System.Data.Entity.Core.Objects.ObjectContext+<SaveChangesInternalAsync>d__31.MoveNext()
   at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(System.Threading.Tasks.Task)
   at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(System.Threading.Tasks.Task)
   at RIS.Racenet.Repository.HorseRepositoryContext+<CommitChangesAsync>d__4.MoveNext()
   at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(System.Threading.Tasks.Task)
   at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(System.Threading.Tasks.Task)
   at MyAwfulWindowsService.UnitofWork+<CommitChangesAsync>d__0.MoveNext()
   at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(System.Threading.Tasks.Task)
   at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(System.Threading.Tasks.Task)
   at MyAwfulWindowsService.DataImportService+<AttachExternalCode>d__41.MoveNext()
   at System.Runtime.CompilerServices.AsyncMethodBuilderCore+<>c.<ThrowAsync>b__6_1(System.Object)
   at System.Threading.QueueUserWorkItemCallback.WaitCallback_Context(System.Object)
   at System.Threading.ExecutionContext.RunInternal(System.Threading.ExecutionContext, System.Threading.ContextCallback, System.Object, Boolean)
   at System.Threading.ExecutionContext.Run(System.Threading.ExecutionContext, System.Threading.ContextCallback, System.Object, Boolean)
   at System.Threading.QueueUserWorkItemCallback.System.Threading.IThreadPoolWorkItem.ExecuteWorkItem()
   at System.Threading.ThreadPoolWorkQueue.Dispatch()
   at System.Threading._ThreadPoolWaitCallback.PerformWaitCallback()
From Googling around, I've found that this is usually caused by trying to do something to an attached entity after the context has been disposed, but honestly I have no idea. I'm really hoping that this is a somewhat frequent issue that someone here might see and go "oh that stupid thing, yeah that's because X". If not I won't be surprised, but I had to try, I'm getting desperate.

The people that wrote that could just suck, or maybe they did need to add complexity because the business requirements demanded it but EF didn't leave them any room to manage that complexity in a more sane manner.

EssOEss posted:

Well, it depends on the usage but I can certainly agree that horrible experiences with EF are possible. The main issues with EF (Core) that I have encountered are:

1. Migrations sometimes glitch out and EF thinks you need to apply some nonsense migration that is a no-op at best but can sometimes even be destructive.
2. LINQ->SQL transformations are extremely limited once you move on from basic "SELECT X FROM Y WHERE Z" types of queries.
3. No built-in bulk operation support.

These are the big pain points for me but life with EF is certainly better than without EF. However, I am careful to only use the "good parts" of EF. For example, I would never touch the inheritance hierarchy modeling functionality, I treat lazy loading as cancer to be rooted out, I keep any writable EF object lifetimes constrained to a single C# method (any EF objects returned are strictly read-only and never used for pushing data to the database).

It took me years to learn the right patterns and they are not really documented anywhere - if you read guides, they just tell you what features do, rarely when it is appropriate to use them (or even more importantly, to avoid them). Given that, I would say that you need to know how to use it and you'll be fine. Use it as a minimal C#<->SQL translation layer and you'll be fine. But if you just take all the documented features and apply them in a spray-and-pray pattern in your business logic, you will suffer greatly.

a hot gujju bhabhi posted:

Exactly, it's just about knowing what you're doing and designing your code appropriately. Lazy loading should never be a problem because you should really be projecting your queries to domain models at the outset. If you're gonna do an update, use a dto to represent the change, fetch the object and update and save it in the same method as you said.

Look I agree that getting to know EF well is hard work, but the benefits it brings are immense.

If EF brings all these footguns if it's used beyond performing basic queries or simple data writes, why not just write the SQL directly? I agree that manually mapping data between something like ADO.NET and POCOs is a huge pain, but Dapper solves that problem and seems like a better fit than what you're using EF for.

ThePeavstenator fucked around with this message at 22:47 on Jan 25, 2020

redleader
Aug 18, 2005

Engage according to operational parameters

a hot gujju bhabhi posted:

What are the EF bugs you've been bitten by in EF Core?

it's not a bug (because it's documented), but the most egregious foogun i've found with ef core is mentioned only in a sidebar:

https://docs.microsoft.com/en-us/ef/core/querying/related-data posted:

Entity Framework Core will automatically fix-up navigation properties to any other entities that were previously loaded into the context instance. So even if you don't explicitly include the data for a navigation property, the property may still be populated if some or all of the related entities were previously loaded.

in other words, ef does some magical bullshit to map up child and child-of-child properties invisibly, that can break when you remove an earlier and apparently unrelated db call, or even reorder some operations. this happens even when eager loading, when you think you've got all the Includes().ThenIncludes() that you need

other issues i have with it have been called out by other posters

some of my problems could be described as us doing it wrong... but ms offers no guidance as to how and when to use ef and when to look elsewhere (which is a pattern i've seen with every other ms product i guess)

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
If you're using .Includes and ESPECIALLY .ThenIncludes then you are doing it wrong. And yes, the fact this isn't better understood is definitely a Microsoft failing, but it doesn't make EF any less poo poo when you use it correctly.

To expand: EF is a DAL, but too many people try to write a DAL of their own using EF as the communication mechanism. EF is a strongly typed representation of your database and you should take that literally (never pull out entities as they are, pull them out as domain models using projections) so that you don't wind up passing database records around. Database -> Domain logic is a domain barrier and you should only cross it using intermediate DTO objects, not actual database records.

putin is a cunt fucked around with this message at 08:51 on Jan 26, 2020

redleader
Aug 18, 2005

Engage according to operational parameters
on that note, any ways to make generating and mapping dtos from aspnet request dto -> domain model -> database dto and vice versa? it is deeply, deeply tedious, especially when creating multiply nested objects

EssOEss
Oct 23, 2006
128-bit approved

ThePeavstenator posted:

If EF brings all these footguns if it's used beyond performing basic queries or simple data writes, why not just write the SQL directly? I agree that manually mapping data between something like ADO.NET and POCOs is a huge pain, but Dapper solves that problem and seems like a better fit than what you're using EF for.

That's a fair question. I have used Dapper for a while, as well, and in the end found EF still more productive to work with. One comes with a bag of footguns, the other just comes with not quite enough substance, I feel. On the balance, EF won out for me.

I wish there were some middle ground option between them but it seems that libraries often come in "bare minimum" and "all the trinkets in the world" variants - as soon as the authors start going down the path of adding more features, they tend not to be able to stop.

SAVE-LISP-AND-DIE
Nov 4, 2010

a hot gujju bhabhi posted:

If you're using .Includes and ESPECIALLY .ThenIncludes then you are doing it wrong. And yes, the fact this isn't better understood is definitely a Microsoft failing, but it doesn't make EF any less poo poo when you use it correctly.

To expand: EF is a DAL, but too many people try to write a DAL of their own using EF as the communication mechanism. EF is a strongly typed representation of your database and you should take that literally (never pull out entities as they are, pull them out as domain models using projections) so that you don't wind up passing database records around. Database -> Domain logic is a domain barrier and you should only cross it using intermediate DTO objects, not actual database records.

Out of interest, doing things this way, how do you avoid 1/3rd of the codebase being overlapping model classes and their respective mappings?

EssOEss
Oct 23, 2006
128-bit approved
In the larger codebases where I worked, the initial "eww, do I really need to just make two very similar looking objects" feelings were quite quickly replaced by "phew, thank god I did not try using the same object for both of these layers". For trivial cases, the duplication might seem a bit time-wasting but once you get into more complex scenarios, it begins to pay off. Just accept the fact that the trivial objects will often be just very similar (or sometimes even equal) classes on multiple layers. The price has always ended up worth paying in my experience. Yeah, you might have 1/3 of your codebase filled with this stuff in some types of apps but if your app is data-operation-heavy then "spending" your code on the classes dealing with data is fine.

power crystals
Jun 6, 2007

Who wants a belly rub??

I have what I feel is a weird and highly specific question: I am looking for a .NET Lua implementation/binding but one that specifically supports setfenv() and friends. Every library I've seen didn't bother implementing those functions because, roughly speaking, "it's hard" (assuming they didn't just do about two thirds of the built-in functions and then get bored, which seems the most common). Does anyone here know of such a thing? I very much do not want to have to write my own wrapper around the C API if I can get away with it. For the sake of argument let's say performance is irrelevant.

B-Nasty
May 25, 2005

EssOEss posted:

In the larger codebases where I worked, the initial "eww, do I really need to just make two very similar looking objects" feelings were quite quickly replaced by "phew, thank god I did not try using the same object for both of these layers".

Anyone that has ever worked on a codebase of any significant size and age realizes strict adherence to DRY typically causes more problems than it helps. Same with domain -> dto -> model mapping. It's tedious, but future-you will be relieved that changing a domain object doesn't require versioning your API or changing tons of code upstream.

Also, as much as I love Jimmy, AutoMapper is the devil.

New Yorp New Yorp
Jul 18, 2003

Only in Kenya.
Pillbug

a hot gujju bhabhi posted:

To expand: EF is a DAL, but too many people try to write a DAL of their own using EF as the communication mechanism.

I contend that you should do that. EF is a database communication tool. Your application shouldn't know or care that the data is being persisted in a SQL database via EF. Your application should depend on an abstraction that allows you to persist data. The tool used to persist data and the place the data is persisted should be completely irrelevant to the application.

I've seen this come in handy when making the switch from monoliths to microservices, for example. Someone wrote a new implementation of the data access interfaces that used REST API calls to hit appropriate REST endpoints, and the core application was completely unperturbed that the data was going to and coming from a REST endpoint instead of directly from a SQL database. If EF code had been sprinkled willy-nilly throughout the application instead of nicely abstracted and isolated, replacing it would have been a huge effort bordering on total rewrite.

SAVE-LISP-AND-DIE
Nov 4, 2010

EssOEss posted:

In the larger codebases where I worked, the initial "eww, do I really need to just make two very similar looking objects" feelings were quite quickly replaced by "phew, thank god I did not try using the same object for both of these layers". For trivial cases, the duplication might seem a bit time-wasting but once you get into more complex scenarios, it begins to pay off. Just accept the fact that the trivial objects will often be just very similar (or sometimes even equal) classes on multiple layers. The price has always ended up worth paying in my experience. Yeah, you might have 1/3 of your codebase filled with this stuff in some types of apps but if your app is data-operation-heavy then "spending" your code on the classes dealing with data is fine.

I was hoping there was one weird trick Jon Skeet doesn't want you to know. I totally agree with "don't re-use the model" by the way. When it comes up at work I cite the security exploits you get when someone model binds their ASP.NET action methods to the EF code-generated model classes, thankfully people don't tend to contest anything that might result in a GDPR gently caress up.

epswing
Nov 4, 2003

Soiled Meat

redleader posted:

on that note, any ways to make generating and mapping dtos from aspnet request dto -> domain model -> database dto and vice versa? it is deeply, deeply tedious, especially when creating multiply nested objects

On the way out (from db entity to domain model), it’s pretty straightforward (because you’re creating new objects), you can use a mapping tool like AutoMapper or ValueInjecter and as long as you name and type the properties correctly it handles nested objects adequately.

On the way back in, e.g. when saving a record (from domain model to db entity), this isn’t trivial (because you’re updating existing objects). You’re actually pulling up the db entity, copying values from the domain model, and automatic mappers don’t handle nested records well. Each child record may be new, changed, or missing (deleted), and you’ll need to tell EF to insert, update, or delete a child row in each case.

I wrote a generic way to automate this into a one-liner years ago, something like UpdateManyToMany(domainModel.ChildList, dbEntity.ChildList), I should really publish the code in a blog post someday.

Edit: And you’re right, there are actually 3 sets of classes, the db entities and domain models (as mentioned above), and then the UI-specific classes. It’s a lot of classes but after many years of figuring out what parts of EF to use/avoid, this is what I’ve settled on.

epswing fucked around with this message at 20:20 on Jan 26, 2020

Rocko Bonaparte
Mar 12, 2002

Every day is Friday!

power crystals posted:

I have what I feel is a weird and highly specific question: I am looking for a .NET Lua implementation/binding but one that specifically supports setfenv() and friends.
I'm not really into Lua at all, but I'm more embedded code man right now and had some notion that my own Google-fu might dig something up. I think I did...

https://github.com/Myndale/KopiLua

It was encouraged to check the forks because it's not an ambitious project with super support.

It's kind of off-topic, but what the hell does setfenv do? I read it up and was really puzzled about it and kind of scared that it was some strange way of push/popping global variable state between calls or something.

power crystals
Jun 6, 2007

Who wants a belly rub??

Well I guess a C# port of the C API at least saves me having to do that myself even if I’d have preferred something slightly higher level.

And you aren’t far off. Basically, setfenv lets you swap out the _G global table when you call into another file. You can use this to prepopulate globals on the way in as a method of passing parameters, or read the file’s globals after it’s done as a way to get return values since lua has nothing approximating interfaces or a way to define an entry point in a file that you can call as a function, it just starts at character 0. You can also do setfenv(1, var) to swap out your own global table. I have yet to figure out why you’d ever want to do that, but you can.

bobthenameless
Jun 20, 2005

When I was looking at Lua/C# interop a bit ago I came across https://github.com/moonsharp-devs/moonsharp/

It's not really a Lua wrapper so it's not exactly what you're looking for, but it seemed intriguing to me at the time...I didn't actually get to use it, and I didn't see many opinions on it out there. But one day I'll give it a shot

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

New Yorp New Yorp posted:

I contend that you should do that. EF is a database communication tool. Your application shouldn't know or care that the data is being persisted in a SQL database via EF. Your application should depend on an abstraction that allows you to persist data. The tool used to persist data and the place the data is persisted should be completely irrelevant to the application.

I've seen this come in handy when making the switch from monoliths to microservices, for example. Someone wrote a new implementation of the data access interfaces that used REST API calls to hit appropriate REST endpoints, and the core application was completely unperturbed that the data was going to and coming from a REST endpoint instead of directly from a SQL database. If EF code had been sprinkled willy-nilly throughout the application instead of nicely abstracted and isolated, replacing it would have been a huge effort bordering on total rewrite.

My argument is that you should isolate all of that code to your domain layer which will serve as an internal API of sorts, and your EF implementation of that interface is just one of many possible implementations you may eventually end up writing. Which I think you agree with and I've just not explained properly what I mean. What I'm arguing against is this model:
code:
View => Application Layer => Domain Layer => Repository Layer => EF / REST API client => Database
Instead I think you should do this:
code:
View => Application Layer => Domain Layer => EF / REST API client => Database
I'm saying remove the repository layer and just have your domain/service layer talk directly to the database using EF as the abstraction layer. Your example of moving to microservices doesn't change anything, since you'll still need to modify the same amount of code - in the above example you would modify the repository layer, in the second example you would modify the domain layer (and write a client layer for communicating with the REST API). In other words, I'm saying EF is the functional equivalent of an API client, you don't need a layer between that and your domain because your domain is already a specific implementation of a series of interfaces that (if designed correctly) should be easily able to be swapped in and out with alternative implementations. Get rid of the repository layer because it adds no value.

power crystals
Jun 6, 2007

Who wants a belly rub??

bobthenameless posted:

When I was looking at Lua/C# interop a bit ago I came across https://github.com/moonsharp-devs/moonsharp/

It's not really a Lua wrapper so it's not exactly what you're looking for, but it seemed intriguing to me at the time...I didn't actually get to use it, and I didn't see many opinions on it out there. But one day I'll give it a shot

Moonsharp is actually what I started with. It does not support setfenv (it also doesn't implement table.maxn somehow??) but it's otherwise gotten me to where I've been able to get most of what I want done by intercepting script loads and editing the contents. If you don't have my specific requirements it's actually pretty solid and I'd recommend it in general.

power crystals fucked around with this message at 04:33 on Jan 28, 2020

New Yorp New Yorp
Jul 18, 2003

Only in Kenya.
Pillbug

a hot gujju bhabhi posted:

My argument is that you should isolate all of that code to your domain layer which will serve as an internal API of sorts, and your EF implementation of that interface is just one of many possible implementations you may eventually end up writing. Which I think you agree with and I've just not explained properly what I mean. What I'm arguing against is this model:
code:
View => Application Layer => Domain Layer => Repository Layer => EF / REST API client => Database
Instead I think you should do this:
code:
View => Application Layer => Domain Layer => EF / REST API client => Database
I'm saying remove the repository layer and just have your domain/service layer talk directly to the database using EF as the abstraction layer. Your example of moving to microservices doesn't change anything, since you'll still need to modify the same amount of code - in the above example you would modify the repository layer, in the second example you would modify the domain layer (and write a client layer for communicating with the REST API). In other words, I'm saying EF is the functional equivalent of an API client, you don't need a layer between that and your domain because your domain is already a specific implementation of a series of interfaces that (if designed correctly) should be easily able to be swapped in and out with alternative implementations. Get rid of the repository layer because it adds no value.

I want data access logic totally isolated behind bespoke, domain-specific interfaces that can be mocked for testing and swapped to alternate implementations without having any impact on consuming code other than changing the concrete implementation that's passed in. Whether you call that a repository layer or a domain layer is really just semantics. If you have separate domain and repository layers where the domain layer is nothing more than a thin wrapper that forwards calls to a repository and returns results, yeah, sure. Eliminate a layer. If your domain layer is doing more than that, the repository should remain separate.

Boz0r
Sep 7, 2006
The Rocketship in action.
Is there a Visual Studio tool to list the target frameworks of all projects in a solution?

SirViver
Oct 22, 2008

Boz0r posted:

Is there a Visual Studio tool to list the target frameworks of all projects in a solution?

Boz0r posted:

Is there an easy way to see the target frameworks of all projects in a solution in Visual Studio?

SirViver posted:

Don't know of any built-in way, but you can use the Target Framework Migrator extension to get a quick overview.

Adbot
ADBOT LOVES YOU

Boz0r
Sep 7, 2006
The Rocketship in action.

Woops, sorry about that. I forgot I already asked.

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