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
User0015
Nov 24, 2007

Please don't talk about your sexuality unless it serves the ~narrative~!

Mr Shiny Pants posted:

You just have the file in your app directory, I wonder how they would lock that down.



I should be more specific. I was deploying via Elastic Beanstalk, which does two things: Blows away your instance when you deploy, and there's access issues when using an image from elastic beanstalk. Here's someone on SO complaining about it: https://stackoverflow.com/questions/49954268/elastic-beanstalk-does-not-work-with-sqlite

You can absolutely remote into your ec2 instance and fix the permission issues. But that doesn't help, because deploying wipes away your changes. It sucks and is bad, vs spinning up a DB on AWS itself.

side note - I have pulled down over 10 repositories on GitHub that say they are ready to deploy dockerized .NET applications with an angular front end. Current number of successful deployments with no modifications: 0 -- who the gently caress likes docker here? I must be doing something wrong.

User0015 fucked around with this message at 17:46 on Jan 9, 2019

Adbot
ADBOT LOVES YOU

Volguus
Mar 3, 2009
For that kind of a scenario you need a database server. No ifs nor buts. Has nothing to do with sqlite itself. MySQL locally on the instance won't work either. It has to be a separate instance to which you connect remotely. If you're hell bent on sqlite you can work around the lack of server for it by having an instance in AWS that doesn't change/move/redeploy and which exposes a NFS folder that is then mounted by the EB managed instances. That's insane though. Really ... just use RDS and don't worry about it.

Or use sqlite in an EC2 instance but not managed by anyone and anything. No scaling, no nothing.

EssOEss
Oct 23, 2006
128-bit approved

User0015 posted:

who the gently caress likes docker here? I must be doing something wrong.

Like most code in the world, most Docker images are poo poo. It is great when it is used well but it is no magic wand. Even some respectable 26000-star GitHub projects can have royally hosed up Docker images, as I have found out.

If you are doing anything wrong, it is expecting stuff to work out of the box :D

User0015
Nov 24, 2007

Please don't talk about your sexuality unless it serves the ~narrative~!

EssOEss posted:

If you are doing anything wrong, it is expecting stuff to work out of the box :D

Hah! True though.

Also, total time to deploy an app by hand, literally setting up an empty Ubuntu server from scratch? ~1 hour.

The bigger question is, is there any way to tell Visaul Studio to SFTP deploy into a box, then stop and restart automatically, after a publish? If I can get that working, gently caress all this docker deployment nonsense.

Pablo Nergigante
Apr 16, 2002

Soooooo I do Windows database application development using ancient technology that I’m not even going to mention. I started finally learning C# and I’ve been doing a lot of reading about the kind of stuff I should be doing like using WPF, MVVM, and Entity Framework but since I’m kind of a noob I’m having trouble figuring out how to tie all this stuff together. Does anyone have any suggestions for resources, tutorials, courses, etc. that might be helpful? Most of the stuff I’ve found on Udemy is WinForms poo poo which I’d rather not deal with. Thanks y’all

GoodCleanFun
Jan 28, 2004

Pablo Nergigante posted:

Soooooo I do Windows database application development using ancient technology that I’m not even going to mention. I started finally learning C# and I’ve been doing a lot of reading about the kind of stuff I should be doing like using WPF, MVVM, and Entity Framework but since I’m kind of a noob I’m having trouble figuring out how to tie all this stuff together. Does anyone have any suggestions for resources, tutorials, courses, etc. that might be helpful? Most of the stuff I’ve found on Udemy is WinForms poo poo which I’d rather not deal with. Thanks y’all

Is it foxpro?! I hope it's not.

You should look for Josh Smith's stuff on WPF and MVVM. That's a great starting point. I've also posted numerous times about both in this thread.

LongSack
Jan 17, 2003

Pablo Nergigante posted:

Soooooo I do Windows database application development using ancient technology that I’m not even going to mention. I started finally learning C# and I’ve been doing a lot of reading about the kind of stuff I should be doing like using WPF, MVVM, and Entity Framework but since I’m kind of a noob I’m having trouble figuring out how to tie all this stuff together. Does anyone have any suggestions for resources, tutorials, courses, etc. that might be helpful? Most of the stuff I’ve found on Udemy is WinForms poo poo which I’d rather not deal with. Thanks y’all

When I was learning WPF and C#, I found these books helpful: WPF 4.5 Unleashed and C# 7.0 in a Nutshell (although I have the 6.0 version). The former is a guide; the latter a (comprehensive, 1100+ page) reference. For MVVM, I didn’t get it until I really understood data binding, then it just clicked, so I don’t have a good book for that, but this looks like it might do the trick.

As for EF, I’m going to punt on that one. The only books I was able to find were very outdated, not using the newer data context thingies so they were pretty useless except for basic concepts. I, personally, abandoned EF after struggling with it for six months and went back to my ADO Data Access Layer classes. But then, I have that luxury - I’m not a developer, I’m a firewall engineer writing tools for me and my team to use.

Pablo Nergigante
Apr 16, 2002

GoodCleanFun posted:

Is it foxpro?! I hope it's not.

You should look for Josh Smith's stuff on WPF and MVVM. That's a great starting point. I've also posted numerous times about both in this thread.

LongSack posted:

When I was learning WPF and C#, I found these books helpful: WPF 4.5 Unleashed and C# 7.0 in a Nutshell (although I have the 6.0 version). The former is a guide; the latter a (comprehensive, 1100+ page) reference. For MVVM, I didn’t get it until I really understood data binding, then it just clicked, so I don’t have a good book for that, but this looks like it might do the trick.

As for EF, I’m going to punt on that one. The only books I was able to find were very outdated, not using the newer data context thingies so they were pretty useless except for basic concepts. I, personally, abandoned EF after struggling with it for six months and went back to my ADO Data Access Layer classes. But then, I have that luxury - I’m not a developer, I’m a firewall engineer writing tools for me and my team to use.

Awesome, thanks for the suggestions. I found a few other articles and YouTube videos so hopefully that will give me a decent starting point.

And it’s not as ancient as FoxPro but not much better tbh

qsvui
Aug 23, 2003
some crazy thing

Pablo Nergigante posted:

Soooooo I do Windows database application development using ancient technology that I’m not even going to mention. I started finally learning C# and I’ve been doing a lot of reading about the kind of stuff I should be doing like using WPF, MVVM, and Entity Framework but since I’m kind of a noob I’m having trouble figuring out how to tie all this stuff together. Does anyone have any suggestions for resources, tutorials, courses, etc. that might be helpful? Most of the stuff I’ve found on Udemy is WinForms poo poo which I’d rather not deal with. Thanks y’all

Wow, you sound like me a couple months ago. Someone in this thread at the time suggested Pro C# 7 which I found quite helpful. In addition to the language, it goes over ADO, EF, WPF, and MVVM. It is not a reference so it will cover some topics in more detail than others, but overall I found it to be a good introduction.

Pablo Nergigante
Apr 16, 2002

qsvui posted:

Wow, you sound like me a couple months ago. Someone in this thread at the time suggested Pro C# 7 which I found quite helpful. In addition to the language, it goes over ADO, EF, WPF, and MVVM. It is not a reference so it will cover some topics in more detail than others, but overall I found it to be a good introduction.

Wow that looks fantastic. Thanks!

New Yorp New Yorp
Jul 18, 2003

Only in Kenya.
Pillbug

Pablo Nergigante posted:

Wow that looks fantastic. Thanks!

Also, try not to jump into too many new concepts all at once, it's going to make you hit constant walls and be frustrated.

Pablo Nergigante
Apr 16, 2002

New Yorp New Yorp posted:

Also, try not to jump into too many new concepts all at once, it's going to make you hit constant walls and be frustrated.

Yeah there's no immediate urgency so I'm starting with the basics and I'll work my way up from there. I haven't learned anything new programming wise in god knows how long

EssOEss
Oct 23, 2006
128-bit approved
I am trying to expose an API that allows data to be queried in a reasonably freeform fashion from a browser. An OData endpoint would be absolutely perfect.

Unfortunately, the Microsoft OData library appears to be of dogshit quality and even basic things such as setting "show me the object metadata" start throwing exceptions deep inside the OData library. Not to mention a lovely API and a library design that is visibly burdered by legacy compatibility and half-baked features that were partially cut before every release.

An alternative I considered was GraphQL but it seems too new and risky to take up - I am not convinced that mature .NET libraries exist for it at present. Everything I see on GitHub seems experimental. Plus its non-REST nature will be a turn-off and major learning experience.

Is there a third alternative that I should consider here?

Mr Shiny Pants
Nov 12, 2012

EssOEss posted:

I am trying to expose an API that allows data to be queried in a reasonably freeform fashion from a browser. An OData endpoint would be absolutely perfect.

Unfortunately, the Microsoft OData library appears to be of dogshit quality and even basic things such as setting "show me the object metadata" start throwing exceptions deep inside the OData library. Not to mention a lovely API and a library design that is visibly burdered by legacy compatibility and half-baked features that were partially cut before every release.

An alternative I considered was GraphQL but it seems too new and risky to take up - I am not convinced that mature .NET libraries exist for it at present. Everything I see on GitHub seems experimental. Plus its non-REST nature will be a turn-off and major learning experience.

Is there a third alternative that I should consider here?

Take a look at tef's post about REST api's in the other thread about how to structure your API?

tef posted:

Here's a long post that tries to explain what the hell is REST, anyway.

Originating in a thesis, REST is an attempt to explain what makes the browser distinct from other networked applications.

You might be able to imagine a few reasons why: there's tabs, there's a back button too, but what makes the browser unique is that a browser can be used to check email, without knowing anything about POP3 or IMAP.

Although every piece of software inevitably grows to check email, the browser is unique in the ability to work with lots of different services without configuration—this is what REST is all about.

HTML only has links and forms, but it's enough to build incredibly complex applications. HTTP only has GET and POST, but that's enough to know when to cache things, HTTP uses URLs, so it's easy to route messages to different places too.

Unlike almost every other networked application, the browser is remarkably interoperable. The thesis was an attempt to explain how that came to be, and called the resulting style REST.

REST is about having a way to describe services (HTML), to identify them (URLs), and to talk to them (HTTP), where you can cache, proxy, or reroute messages, and break up large or long requests into smaller interlinked ones too.

How REST does this isn't exactly clear.

The thesis breaks down the design of the web into a number of constraints—Client-Server, Stateless, Caching, Uniformity, Layering, and Code-on-Demand—but it is all to easy to follow them and end up with something that can't be used in a browser.

REST without a browser means little more than "I have no idea what I am doing, but I think it is better than what you are doing.", or worse "We made our API look like a database table, we don't know why". Instead of interoperable tools, we have arguments about PUT or POST, endless debates over how a URL should look, and somehow always end up with a CRUD API and absolutely no browsing.

There are some examples of browsers that don't use HTML, but many of these HTML replacements are for describing collections, and as a result most of the browsers resemble file browsing more than web browsing. It's not to say you need a back and a next button, but it should be possible for one program to work with a variety of services.

For an RPC service you might think about a `curl` like tool for sending requests to a service:

code:
$ rpctl [url]http://service/[/url] describe MyService
methods: ...., my_method

$ rpctl [url]http://service/[/url] describe MyService.my_method
arguments: name, age

$ rpctl [url]http://service/[/url] call MyService.my_method --name="james" --age=31
Result:
   message: "Hello, James!"
You can also imagine a single command line tool for a databases that might resemble `kubectl`:

code:
$ dbctl [url]http://service/[/url] list ModelName --where-age=23
$ dbctl [url]http://service/[/url] create ModelName --name=Sam --age=23
$ ...
Now imagine using the same command line tool for both, and using the same command line tool for _every_ service—that's the point of REST. Almost.


code:
$ apictl call MyService:my_method --arg=...
$ apictl delete MyModel --where-arg=...
$ apictl tail MyContainers:logs --where ...
$ apictl help MyService
You could implement a command line tool like this without going through the hassle of reading a thesis. You could download a schema in advance, or load it at runtime, and use to create requests and parse responses.

REST is quite a bit more than being able to reflect, or describe a service at runtime. The REST constraints require using a common fomat for the contents of messages so that the command line tool doesnt need configuring, require sending the messages in a way that allows you to proxy, cache, or reroute them without fully understanding their contents.

REST is also a way to break apart long or large messages up into smaller ones linked together—something far moe than just learning what commands can be sent at runtime, but allowing a response to explain how to fetch the next part in sequence.

To demonstrate, take an RPC service with a long running method call:

code:
class MyService(Service):
    @rpc()
    def long_running_call(self, args: str) -> bool:
        id = third_party.start_process(args)
        while third_party.wait(id):
            pass
        return third_party.is_success(id)
When a response is too big, you have to break it down into smaller responses. When a method is slow, you have to break it down into one method to start the process, and another method to check if it's finished.

code:
class MyService(Service):
    @rpc()
    def start_long_running_call(self, args: str) -> str:
         ...
    @rpc()
    def wait_for_long_running_call(self, key: str) -> bool:
         ...

In some frameworks you can use a streaming API instead, but replacing streaming involves adding heartbeat messages, timeouts, and recovery, so many developers opt for polling instead, breaking the single request into two.

Both approaches require changing the client and the server code, and if another method needs breaking up you have to change all of the code again.

REST offers a different approach. We return a response that describes how to fetch another request, much like a HTTP redirect. In a client library, you could imagine handling these responses, much like an HTTP client handles redirects too.

code:
def long_running_call(self, args: str) -> bool:
    key = third_party.start_process(args)
    return Future("MyService.wait_for_long_running_call", {"key":key})

def wait_for_long_running_call(self, key: str) -> bool:
    if not third_party.wait(key):
        return third_party.is_success(key)
    else:
        return Future("MyService.wait_for_long_running_call", {"key":key})
code:
def fetch(request):
   response = make_api_call(request)
   while response.kind == 'Future':
       request = make_next_request(response.method_name, response.args)
       response = make_api_call(request)


For the more typed among you, please ignore the type annotations, pretend I called `raise Future(....)`, or imagine a `Result<bool>` that's a supertype. For the more operations minded, imagine I call `time.sleep()` inside the client, and maybe imagine the Future response has a duration inside.

The point is that by allowing a response to describe the next request in sequence, we've skipped over the problems of the other two approaches—we only need to implement the code once in the client. When a different method needs breaking up, you can return a `Future` and get on with your life.

In some ways it's as if you're returning a callback to the client, something the client knows how to run to produce a request. With `Future` objects, it's more like returning values for a template. This approach works for paginating too—breaking up a large response into smaller ones.

Pagination often looks something like this in an RPC system:

code:
cursor = rpc.open_cursor()
output = []
while cursor:
    output.append(cursor.values)
    cursor = rpc.move_cursor(cursor.id)
Or something like this:

code:
start = 0
output = []
while True:
    out = rpc.get_values(start, batch=30)
    output.append(out)
    start += len(out)
    if len(out) < 30:
        break
Iterating through a set of responses means keeping track of how far you've gotten so far. The first pagination example stores state on the server, and gives the client an Id to use in subsequent requests. The second pagination example stores state on the client, and constructs the correct request to make from the state.

Like before, REST offers a third approach. The server can return a `Cursor` response, much like a `Future`, with a set of values and a request message to send for the next chunk, and the client pages through the responses to build a list of values:

code:
cursor = rpc.get_values()
output = []
while cursor:
    output.append(cursor.values)
    cursor = cursor.move_next()
code:
class ValueService(Service):
    @rpc()
    def get_values(self):
        return Cursor("ValueService.get_cursor", {"start":0, "batch":30}, [])

    @rpc
    def get_cursor(start, batch):
        ...
        return Cursor("ValueService.get_cursor", {"start":start, "batch":batch}, values)
The REST approach offers something different. The state is created on the server, sent back to the client, and then sent back to the server. If a Server wants to, it can return a `Cursor` with a smaller set of values, and the client will just make more requests to get all of them.

`Future` and `Cursor` aren't the only kind we can parameterise—a `Service` can contain state to pass into methods, too.

To demonstrate why, imagine some worker that connects to a service, processes work, and uploads the results. The first attempt at server code might look like this:

code:
class WorkerApi(Service):
    def register_worker(self, name: str) -> str
        ...
   def lock_queue(self, worker_id:str, queue_name: str) -> str:
        ...
   def take_from_queue(self, worker_id: str, queue_name, queue_lock: str):
       ...
   def upload_result(self, worker_id, queue_name, queue_lock, next, result):
       ...
   def unlock_queue(self, worker_id, queue_name, queue_lock):
       ...
   def exit_wotker(self, worker_id):
       ...
Unfortunately, the client code looks much nastier:

code:
worker_id = rpc.register_worker(my_name)
lock = rpc.lock_queue(worker_id, queue_name)
while True:
    next = rpc.take_from_queue(worker_id, queue_name, lock)
    if next:
        result = process(next)
        rpc.upload_result(worker_id, queue_name, lock, next, result)
    else:
        break
rpc.unlock_queue(worker_id, queue_name, lock)
rpc.exit_worker(worker_id)
Each method requires a handful of parameters, relating to the current session open with the service. What we'd rather use is some API where the state between requests is handled for us:

code:
lease = rpc.register_worker(my_name)

queue = lease.lock_queue(queue_name)

while True:
    next = queue.take_next() 
    if next:
        next.upload_result(process(next))
    else:
        break
queue.unlock()
lease.expire()
The traditional way to achieve this is to build these wrappers by hand—creating special code on the client to wrap the responses, and call the right methods. If we can link together a large response, we should be able to link together the requests, and pass the state from one to the next just like a `Cursor` does.

Instead of one service, we now have four. Instead of returning identifiers to pass back in, we return a `Service` with those values filled in for us:

code:
class WorkerApi(Service):
    def register(self, worker_id):
        return Lease(worker_id)

class Lease(Service):
    worker_id: str

    @rpc()
     def lock_queue(self, name):
        ...
        return Queue(self.worker_id, name, lock)

class Queue(Service):
    name: str
    lock: str
    worker_id: str

    @rpc()
     def get_task(self):
        return Task(.., name, lock, worker_id)

class Task(Service)
    task_id: str
    worker_id: str

    @rpc()
     def upload(self, out):
        mark_done(self.task_id, self.actions, out)
The client code looks like the desired example aboce—instead of an id string, the client gets a 'Service' response, methods included, but with some state hidden inside. The client turns this into a normal service object, and when the methods get called, that state is added back into the request. You can even add new parameters in, without changing too much of the client code.

Although the `Future` looked like a callback, returning a `Service` feels like returning an object. This is the power of self description—unlike reflection where you can specify in advance every request that can be made—each response has the opportunity to define what new requests can be made.

It's this navigation through several linked responses that distinguishes a regular command line tool from one that browses—and where REST gets its name.

The passing back and forth of requests from server to client is where the 'state-transfer' part of REST comes from, and using a common `Result` or `Cursor` object is where the 'representational' comes from. Although a RESTful system is more than just these combined. Along with a reusable browser, you have reusable proxies.

In the same way that messages describe things to the client, they describe things to the proxy too. Using GET or POST, and distinct URLs is what allows caches to work across services. using a stateless protocol (HTTP) is what allows proxying to work so effortlessly. The trick with REST is that despite HTTP being stateless, and despite HTTP being simple, you can build complex, stateful services by threading the state invisibly between smaller messages.

Although the point of REST is to build a browser, the point is to using self-description and state-transfer to allow heavy amounts of interoperation—not just a reusable client, but reusable proxies, caches, or load balancers too. Going back to the constraints, you might be able to see how they things fit together to achieve this.

Client-Server, Stateless, Caching, Uniformity, Layering and Code-on-Demand. The first, Client-Server, feels a little obvious, but sets the background. A server waits for requests from a client, and issues responses.

The second, Stateless, is a little more confusing. If a HTTP proxy had to keep track of how requests link together, it would involve a lot more memory and processing. The point of the stateless constraint is that to a proxy, each request stands alone. The point is also that any stateful interactions should be handled by linking messages together.

Caching is the third constraint, and it's back to being obvious. Requests and Responses must have some description as to if the request must be resent or if the response can be resent. The fourth constraint, Uniformity, is the most difficult, so we'll cover it last. Layering is the fith, and it means "You can proxy it".

Code-on-demand is the final, optional, and most overlooked constraint, but it covers the use of Cursors, Futures, or Parameterised Services—the idea that despite using a simple means to describe services or responses, they can be used, or run, to create new requests to send. Code-on-demand takes that further, and imagines passing back code, rather than templates and values to assemble.

With the other constraints handled, it's time for uniformity. Like statelessness, this constraint is more about HTTP than it is about the system atop, and frequently misapplied. This is the reason why people keep making database APIs and calling them RESTful, but the constraint has nothing to do with CRUD.

The constraint is broken down into four ideas: self-descriptive messages, identification of resources, manipulation of resources through representations, hypermedia as the engine of application state, and we'll take them one by one.

Self-Description is at the heart of REST, and this subconstraint fills in the gaps between the Layering, Caching, and Stateless constraints. Sort-of. It means using 'GET' and 'POST' to indicate to a proxy how to handle things, and responses indicate if they can be cached. It also means using a `content-type` header.

The next subconstraint, identification, means using different URLs for different services. In the RPC examples above, it means having a common, standard way to address a service or method, as well as one with parameters. This ties into the next constraint, which is about using standard representations across services. This doesn't mean using special formats for every API request, but using the same underlying language to describe every response. The web works because everyone uses HTML.

Uniformity so far might as well mean use HTTP (self-description), URLs (identification) and HTML (manipulation through representations), but it's the last subconstraint thats causes most of the headaches: Hypermedia as the engine of application state.

This is a fancy way of talking about how large or long requests can be broken up into interlinked messages, or how a number of smaller requests can be threaded together, passing the state from one to the next. Hypermedia referres to using `Cursor`, `Future`, or `Service` objects, application state is the details passed around as hidden arguments, and being the 'engine' means using it to tie the whole system together.

Together they form the basis of the Representational State transfer style. More than half of these constraints can be satisfied by just using HTTP—and if you digital into the thesis, you'll discover that the other half isn't about picking the right URLs, or using PUT or PATCH, but hiding those details from the end user.

REST at the end of the day is no more than a very long answer to explain what makes a web browser different, the latin name for opening a link in a new tab in a browser—the state is the part of the application you want to open (like your inbox), the repesentaton of the state is the URL in the link you're clicking on, and the transfer describes the whole back-and-forth of downloading the HTML with that URL in, and then requesting it.

Representational state transfer is what makes the back button work, why you can refresh a broken page and not have to restart from the beginning, and why you can open links in a new tab. (Unless it's twitter, where breaking the back button is but a footnote in the list of faults)

If you now find yourself understanding REST, I'm sorry. You're now cursed. Like a cross been the greek myths of Cassandra and Prometheus, you will be forced to explain the ideas over and over again to no avail. The terminology has been utterly destroyed to the point it has less meaning than 'Agile'.

Despite the well being throuroughly poisioned, these ideas of interoperability, self-description, and interlinked requests are surpisingly useful—you can break up large or slow responses, you can to browse or even parameterise services, and you can do it in a way that lets you re-use tools across services.

I haven't covered everything—there are still a few more tricks. Although a RESTful system doesn't have to offer a database like interface, it can. Along with `Service` or `Cursor`, you could imagine `Model` or `Rows` objects to return. For collection types, another trick is inlining.

Along with returning a request to make, a server can embed the result inside. A client can skip the network call and work directly on the inlined response. A server can even make this choice at runtime, opting to embed if the message is small enough.

Finally, with a RESTful system, you should be able to offer things in different encodings, depending on what the client asks for—even HTML. If you can build a reusable command line tool, generating a web interface isn't too difficult—at least this time you don't have to implement the browser from scratch.

In the end, being RESTful probably doesn't matter, your framework should take care of the details for you.

If interoperability, or common tools matter, then you might not care about the implementation details, but you should expect a little more from a RESTful system than just create, read, update and delete.


I like this one: https://vimeo.com/20781278

Mr Shiny Pants fucked around with this message at 07:25 on Jan 16, 2019

EssOEss
Oct 23, 2006
128-bit approved
I was thinking more of something that already exists that I can just throw a data model at, rather than manually implementing an API.

That's a cool post, though!

amotea
Mar 23, 2008
Grimey Drawer
https://swagger.io/ maybe? Kinda busy atm, so maybe I didn't understand what you need exactly, but maybe it fits your need. Never used it myself.

LongSack
Jan 17, 2003

So help me to make sure I understand things, please.

What I get from what I read about xxx Core is that it is designed to be used in libraries, with the GUI stuff separate. So, my app would have a common .NET core library (or libraries) with common code, EF core for database access, while the UI specific stuff (Windows vs iOS vs Android) floats “above” the core stuff.

Is that about right?

Xik
Mar 10, 2011

Dinosaur Gum
If you want to split it up, your common library can target some version of .NET Standard and then just consume it as normal from all your other apps. Check out the compatibility table on this page.

So if you target .NET Standard 2, you can consume it with Xamarin iOS, Android and a Windows form app with versions 10.14, 8.0, and .NET Framework > 4.6.1 respectively.

There are links in that link above that show you how to target .NET Standard from .NET Core tooling if that is what you would like to do.

e: Versions stated were just an example, just find the lowest version out of all your front ends and target that version of .NET Standard.

Xik fucked around with this message at 07:58 on Jan 17, 2019

amotea
Mar 23, 2008
Grimey Drawer
And please note that 4.6.1 can't actually be properly mixed with Standard 2.0. It works, kind of, but it's buggy as hell, see the Github or this thread a couple of pages back.

NihilCredo
Jun 6, 2011

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

Yeah, if you want to use a .NET Standard 2.0 library on Windows, your application should require the .NET Framework 4.7.2 for best compatibility.

Note that this is only an issue if the library is pure .NET Standard. If you look at the Dependencies list in e.g. JSON.NET, which is an extremely compatible library you'll see that it multi-targets two different levels of .NET Standard and four versions of the .NET Framework, so if you use it from a .NET Framework application it will be able to just use the ".NETFramework 4.5" .dlls and completely ignore the .NET Standard ones.

epswing
Nov 4, 2003

Soiled Meat
How do y'all solve the problem of setting the Owner property on modal dialogs in WPF, while using IoC and MVVM patterns? I've seen (and written) all sorts of hacks.

Here's a naive example of a window with a viewmodel containing an ICommand which prompts the user. It works by setting vm.Owner = this, and then using Owner later in a MessageBox when the command is called.

C# code:
public partial class MyWindow : Window
{
    private readonly MyWindowViewModel vm;
    
    public MyWindow(MyWindowViewModel vm)
    {
        InitializeComponent();
        DataContext = vm;
        vm.Owner = this;
    }
}

public class MyWindowViewModel
{
    private readonly RelayCommand<object> fooCommand;
    
    public ICommand FooCommand => fooCommand;
    public Window Owner { get; set; }
    
    public MyWindowViewModel()
    {
        fooCommand = new RelayCommand<object>(OnFooCommand);
    }
    
    private void OnFooCommand(object obj)
    {
        if (MessageBox.Show(Owner, "Are you sure?", MessageBoxButton.OKCancel) == MessageBoxResult.OK)
        {
            // ...
        }
    }
}
This works, but the ViewModel is doing View-related things like opening message boxes, and knowing who owns it.

Here's an example that abstracts the dialog into a Func, which is set in the constructor of the Window.

C# code:
public partial class MyWindow : Window
{
    private readonly MyWindowViewModel vm;
    
    public MyWindow(MyWindowViewModel vm)
    {
        InitializeComponent();
        DataContext = vm;
        vm.OkCancelBox = OkCancelBox;
    }
    
    private MessageBoxResult OkCancelBox(string text)
    {
        return MessageBox.Show(this, text, "Confirm", MessageBoxButton.OKCancel, MessageBoxImage.Question);
    }
}

public class MyWindowViewModel
{
    private readonly RelayCommand<object> fooCommand;
    
    public ICommand FooCommand => fooCommand;
    public Func<string, MessageBoxResult> OkCancelBox { get; set; } = s => throw new NotImplementedException();
    
    public MyWindowViewModel()
    {
        fooCommand = new RelayCommand<object>(OnFooCommand);
    }
    
    private void OnFooCommand(object obj)
    {
        if (OkCancelBox("Are you sure?") == MessageBoxResult.OK)
        {
            // ...
        }
    }
}
This feels better, now my ViewModel calls OkCancelBox and gets back a MessageBoxResult, without knowing anything about the View. If you "forget" to set OkCancelBox, you'll get a NotImplementedException. In a test, I can trivially set the Func to simulate the user clicking "OK":

C# code:
vm.OkCancelBox = s => MessageBoxResult.OK;
Still, I'm not sure I like it.

There are other solutions like passing in (or service-locating) an IMessageBoxService, but I don't fully understand how you can specify the owner of a dialog without the ViewModel knowing who owns it.

What do you do?

LongSack
Jan 17, 2003

epalm posted:

How do y'all solve the problem of setting the Owner property on modal dialogs in WPF

For my “tier 2” dialogs (i.e, those that appear above the “main window”), I use Application.Current.Mainwindow. For dialogs that are tier 3 (which for me are rare, and I can’t think of an instance where I went deeper), I don’t set it at all - I’ve not found a good solution to that, either. I just set the dialog to appear in the center of the screen. I’d be interested in a good solution to this as well.

GoodCleanFun
Jan 28, 2004
I took this as a starting point: https://www.codeproject.com/Articles/36745/Showing-Dialogs-When-Using-the-MVVM-Pattern?fid=1541292&fr=1#xx0xx.

Made some adjustments and I have it on my view model base class for use in all view models. Allows me to open new windows, show messages, or ask yes/no questions directly from any view model.

LongSack
Jan 17, 2003

Question about design.

Every example of EF that I’ve seen uses simple POCO classes with get/set auto properties.

Yet, my views require observable classes.

So, how do you handle this? Do you make your model classes observable? Do you create an intermediate observable class that “fronts” the model classes (even if there is a 1:1 relationship between the properties)? My response has been the former, but I’m curious — what do others do?

Hammerite
Mar 9, 2007

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

epalm posted:

How do y'all solve the problem of setting the Owner property on modal dialogs in WPF, while using IoC and MVVM patterns? I've seen (and written) all sorts of hacks.

Here's a naive example of a window with a viewmodel containing an ICommand which prompts the user. It works by setting vm.Owner = this, and then using Owner later in a MessageBox when the command is called.

C# code:
...
This works, but the ViewModel is doing View-related things like opening message boxes, and knowing who owns it.

Here's an example that abstracts the dialog into a Func, which is set in the constructor of the Window.

C# code:
...
This feels better, now my ViewModel calls OkCancelBox and gets back a MessageBoxResult, without knowing anything about the View. If you "forget" to set OkCancelBox, you'll get a NotImplementedException. In a test, I can trivially set the Func to simulate the user clicking "OK":

C# code:
vm.OkCancelBox = s => MessageBoxResult.OK;
Still, I'm not sure I like it.

There are other solutions like passing in (or service-locating) an IMessageBoxService, but I don't fully understand how you can specify the owner of a dialog without the ViewModel knowing who owns it.

What do you do?

I am pretty sure I've used code similar to your callback example to do this kind of thing. I went looking for an example in the code for an app I was working on a few months ago (now being maintained by another team), but my recollection of where to find it didn't hold up.

Scikar
Nov 20, 2005

5? Seriously?

LongSack posted:

Question about design.

Every example of EF that I’ve seen uses simple POCO classes with get/set auto properties.

Yet, my views require observable classes.

So, how do you handle this? Do you make your model classes observable? Do you create an intermediate observable class that “fronts” the model classes (even if there is a 1:1 relationship between the properties)? My response has been the former, but I’m curious — what do others do?

I don't really like the idea that a database entity class might have to change purely because of how I'm choosing to display that data, or that my display code would have to change if I reworked the data storage implementation. I'm pretty sure this scenario is exactly what AutoMapper was created for - you can have two separate objects that use the same data for different purposes without having to write loads of code to translate one to the other.

Automapper Docs posted:

Why use AutoMapper?
Mapping code is boring. Testing mapping code is even more boring. AutoMapper provides simple configuration of types, as well as simple testing of mappings. The real question may be “why use object-object mapping?” Mapping can occur in many places in an application, but mostly in the boundaries between layers, such as between the UI/Domain layers, or Service/Domain layers. Concerns of one layer often conflict with concerns in another, so object-object mapping leads to segregated models, where concerns for each layer can affect only types in that layer.

Scikar fucked around with this message at 12:03 on Jan 18, 2019

epswing
Nov 4, 2003

Soiled Meat

LongSack posted:

Question about design.

Every example of EF that I’ve seen uses simple POCO classes with get/set auto properties.

Yet, my views require observable classes.

So, how do you handle this? Do you make your model classes observable? Do you create an intermediate observable class that “fronts” the model classes (even if there is a 1:1 relationship between the properties)? My response has been the former, but I’m curious — what do others do?

Just by coincidence after reading your question above a I stumbled across this Q/A on softwareengineering.stackexchange.com, the first answer of which details a bunch of scenarios of how to deal with changes with/without the "intermediate" objects you mentioned (they're often called DTOs or Data Transfer Objects).

In my own experience, "weird things" happen when using EF entities in your UI rather than purpose-built DTOs. Those weird things include e.g. your objects becoming "dynamic proxies"

which has something to do with lazy loading, so the object is kinda there, but not really, unless you access it, but when does/should that happen(?), etc. EF applies some magic to your so called POCOs, and your UI sometimes breaks because of that (WPF bindings do, at least). (Edit: here's an example of said weirdness.)

Pull your entity out of the DB, use something like Automapper (mentioned above) to map the DB entities to DTOs, and then use those DTOs in your UI (or send them across the network, etc). Then on the way back (e.g. updating a record), pull the entity out of the DB, map the DTO on top of the entity, which kicks off EF's change tracking, etc.

epswing fucked around with this message at 20:46 on Jan 18, 2019

LongSack
Jan 17, 2003

Thanks, both. I wasn't aware of automapper, I'll take a look at it.

Nevett
Aug 31, 2001

I hate that most MVC tutorials teach you to just use the data layer objects in the UI when you really want to avoid that in any non-trivial project.

Scikar
Nov 20, 2005

5? Seriously?

epalm posted:

In my own experience, "weird things" happen when using EF entities in your UI rather than purpose-built DTOs. Those weird things include e.g. your objects becoming "dynamic proxies"
which has something to do with lazy loading, so the object is kinda there, but not really, unless you access it, but when does/should that happen(?), etc. EF applies some magic to your so called POCOs, and your UI sometimes breaks because of that (WPF bindings do, at least). (Edit: here's an example of said weirdness.)

Yeah this is basically the framework already dictating how your domain objects are constructed in terms of the database storage implementation side of things. The gist of it is that when you get your Customer from the DB, you may or may not want all of its properties to be retrieved as well (everything that has a foreign key to another table). So EF "helpfully" just gives you your Customer, but puts it behind a DynamicProxy so that if you do decide to access those properties further down the line, it can go back to the DB and get them as needed without you having to update the DB access code. If I understand right it's got some uses in prototyping or when the storage, application and UI are all local, but if you forget to turn it off in a web app and you're not careful you end up in a situation where expanding something in your UI triggers bunch of recursive queries to your DB and things start to fall over.

If memory serves lazy loading is enabled by default in EF6, but in EF Core I think you have to include an extra NuGet package before you can even turn it on.

LongSack
Jan 17, 2003

Scikar posted:

Yeah this is basically the framework already dictating how your domain objects are constructed in terms of the database storage implementation side of things. The gist of it is that when you get your Customer from the DB, you may or may not want all of its properties to be retrieved as well (everything that has a foreign key to another table). So EF "helpfully" just gives you your Customer, but puts it behind a DynamicProxy so that if you do decide to access those properties further down the line, it can go back to the DB and get them as needed without you having to update the DB access code. If I understand right it's got some uses in prototyping or when the storage, application and UI are all local, but if you forget to turn it off in a web app and you're not careful you end up in a situation where expanding something in your UI triggers bunch of recursive queries to your DB and things start to fall over.

If memory serves lazy loading is enabled by default in EF6, but in EF Core I think you have to include an extra NuGet package before you can even turn it on.

This was the source of my biggest frustration with EF 6.x, and it was basically due to my misunderstanding of how it all worked. When I failed to use all the .Include methods, so that later my value converters would throw exceptions because the context had already been disposed.

I’m trying to give EF a second chance, esp since now with EF Core, there are books that are written against the current state of EF. Hopefully, this will help me understand EF so that I can use it. I love being able to use LINQ.

Munkeymon
Aug 14, 2003

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



Today I learned that when the manual says that "A static constructor is called automatically to initialize the class before the first instance is created or any static members are referenced." that does not apply to the static constructors of types derived from the class containing the aforementioned static member, even if it's a template/generic class.

Hammerite
Mar 9, 2007

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

Munkeymon posted:

Today I learned that when the manual says that "A static constructor is called automatically to initialize the class before the first instance is created or any static members are referenced." that does not apply to the static constructors of types derived from the class containing the aforementioned static member, even if it's a template/generic class.

So let me see if I understand - you're saying

code:
class Program
{
    class C1
    {
        public static int x = 0;

        static C1()
        {
            Console.WriteLine("static constructor of C1");
        }
    }

    class C2 // edit: what a fuckwit, see my later post
    {
        static C2()
        {
            Console.WriteLine("static constructor of C2");
        }
    }

    static void Main(string[] args)
    {
        int x = C1.x;
    }
}
doesn't print out the "static constructor of C2" message? I tried it and indeed only the "C1" message prints, but I don't understand why you'd expect behaviour different from that.

Hammerite fucked around with this message at 11:03 on Jan 22, 2019

Night Shade
Jan 13, 2013

Old School

Munkeymon posted:

Today I learned that when the manual says that "A static constructor is called automatically to initialize the class before the first instance is created or any static members are referenced." that does not apply to the static constructors of types derived from the class containing the aforementioned static member, even if it's a template/generic class.

Let's pretend for a moment that it did:

code:
class Program {
        
    class Base {
        public static int I;
        static Base() {
            I = 4; // guaranteed to be random, selected by dice roll
        }
    }

    class Derived<T>: Base where T: new() {
        public static T _derivedThing;
        static Derived() {
            _derivedThing = new T();
        }
    }

    static void Main() {
        Console.WriteLine(Base.I);
    }
}
The runtime would need to invoke the static constructor of Derived<T> for every currently loaded type that satisfies the new() constraint, and any time from then on that the runtime loads an assembly repeat the process for the types in that assembly.

Now pretend that I'm also a jerk and declared a bunch of types in my malicious assembly that allocate a 1GB array when constructed.

Hammerite
Mar 9, 2007

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

Hammerite posted:

So let me see if I understand - you're saying

code:
class Program
{
    class C1
    {
        public static int x = 0;

        static C1()
        {
            Console.WriteLine("static constructor of C1");
        }
    }

    class C2
    {
        static C2()
        {
            Console.WriteLine("static constructor of C2");
        }
    }

    static void Main(string[] args)
    {
        int x = C1.x;
    }
}
doesn't print out the "static constructor of C2" message? I tried it and indeed only the "C1" message prints, but I don't understand why you'd expect behaviour different from that.

Look at this huge idiot who didn't make C2 inherit from C1.

I fixed it and the same behaviour is observed - and the same behaviour is observed even if Main() refers to x as "C2.x", which I can understand being a little surprising. But presumably the C# compiler transforms it into a reference to C1.x, since C1 is where it's actually defined and it's all known about at compile time, so not that surprising?

Munkeymon
Aug 14, 2003

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



Night Shade posted:

Let's pretend for a moment that it did:

code:
class Program {
        
    class Base {
        public static int I;
        static Base() {
            I = 4; // guaranteed to be random, selected by dice roll
        }
    }

    class Derived<T>: Base where T: new() {
        public static T _derivedThing;
        static Derived() {
            _derivedThing = new T();
        }
    }

    static void Main() {
        Console.WriteLine(Base.I);
    }
}
The runtime would need to invoke the static constructor of Derived<T> for every currently loaded type that satisfies the new() constraint, and any time from then on that the runtime loads an assembly repeat the process for the types in that assembly.

No, it's like this:
C# code:
abstract class Basic<T> {
    protected static readonly SortedDictionary<int, T> instances = new SortedDictionary<int, T>();

    public static T GetById(int id) {
        return instances[id];
    }
}

class Ya : Basic<Ya> {
    public static readonly zero = new Ya(0);
    
    private Ya(int id) {
        instances[id] = this;
    }
}

void Main() {
    try {
        Ya.GetById(0);//error because the dict is empty
    }
    Ya.zero; //works fine
    Ya.GetById(0); //works now because the previous line triggered initializer
    //IIRC that member access makes all the member constructors run but
    // I can't be assed to recreate the tests I did on that.
}
This is Surprising Behavior because I wouldn't expect the members of Basic<T> to exist absent the derived type since it's a generic class and, conceptually, doesn't really exist on its own terms.

quote:

Now pretend that I'm also a jerk and declared a bunch of types in my malicious assembly that allocate a 1GB array when constructed.

I'm pretending I decompiled your poo poo and saw that before I used it because I'm required to for compliance in my current job :)

Munkeymon fucked around with this message at 15:33 on Jan 22, 2019

Hammerite
Mar 9, 2007

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

Munkeymon posted:

No, it's like this:
C# code:
abstract class Basic<T> {
    protected static readonly SortedDictionary<int, T> instances = new SortedDictionary<int, T>();

    public static T GetById(int id) {
        return instances[id];
    }
}

class Ya : Basic<Ya> {
    public static readonly zero = new Ya(0);
    
    private Ya(int id) {
        instances[id] = this;
    }
}

void Main() {
    try {
        Ya.GetById(0);//error because the dict is empty
    }
    Ya.zero; //works fine
    Ya.GetById(0); //works now because the previous line triggered initializer
    //IIRC that member access makes all the member constructors run but
    // I can't be assed to recreate the tests I did on that.
}
This is Surprising Behavior because I wouldn't expect the members of Basic<T> to exist absent the derived type since it's a generic class and, conceptually, doesn't really exist on its own terms.

This is basically the example I gave, but dressed up with some unnecessary and distracting extra elements (the base class is generic and abstract). GetById() is defined on the base class, so when you access it (irrespective of the fact that you're referring to it as Ya.GetById()), the base class static constructor runs. You're not accessing anything that's actually part of Ya in that line.

Hammerite
Mar 9, 2007

And you don't remember what I said here, either, but it was pompous and stupid.
Jade Ear Joe
Also, you say that Basic<T> "conceptually doesn't exist on its own terms". I wouldn't presume to tell you how you ought to conceptualise things, but to say that Basic<T> doesn't exist on its own terms isn't true in .NET. It would be true of a template class in C++, say.

Munkeymon
Aug 14, 2003

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



Hammerite posted:

Also, you say that Basic<T> "conceptually doesn't exist on its own terms". I wouldn't presume to tell you how you ought to conceptualise things, but to say that Basic<T> doesn't exist on its own terms isn't true in .NET. It would be true of a template class in C++, say.

I mean that it exists as an instance in terms of something else. Namely, whatever the type parameter is.

I couldn't figure out what the 'final form' of your example was supposed to be but the inheritance is the point. It's surprising that only part of the inheritance hierarchy was initialized, but becomes less so when you find out that, since 4.0 (the framework, not the compiler), statics are so aggressively lazily initialized that it will avoid initializing the value of a static property as long as it can.

Additionally, just defining a static constructor can change the way initialization happens.

Anywho, you can Make It Go by adding System.Runtime.CompilerServices.RuntimeHelpers.RunClassConstructor(typeof(T).TypeHandle); to the static constructor of the base class.

e: because it's possible I described something wrong above but am too lazy to copy edit it for details, some further reading: http://csharpindepth.com/Articles/General/Beforefieldinit.aspx

Munkeymon fucked around with this message at 21:38 on Jan 22, 2019

Adbot
ADBOT LOVES YOU

EssOEss
Oct 23, 2006
128-bit approved
Oh wow, that really is surprising. I had better go review some of my code! Thanks for posting this!

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