ASP.NET MVC - 在控制器之间共享会话状态

时间:2022-11-06 01:57:26

I am still mostly unfamiliar with Inversion of Control (although I am learning about it now) so if that is the solution to my question, just let me know and I'll get back to learning about it.

我仍然大多不熟悉控制反转(虽然我现在正在学习它)所以如果这是我的问题的解决方案,请告诉我,我会回过头来学习它。

I have a pair of controllers which need to a Session variable, naturally nothing too special has happen because of how Session works in the first place, but this got me wondering what the cleanest way to share related objects between two separate controllers is. In my specific scenario I have an UploadController and a ProductController which work in conjunction with one another to upload image files. As files are uploaded by the UploadController, data about the upload is stored in the Session. After this happens I need to access that Session data in the ProductController. If I create a get/set property for the Session variable containing my upload information in both controllers I'll be able to access that data, but at the same time I'll be violating all sorts of DRY, not to mention creating a, at best, confusing design where an object is shared and modified by two completely disconnected objects.

我有一对控制器需要一个Session变量,因为Session首先工作的方式自然没有什么特别的事情发生,但这让我想知道在两个独立的控制器之间共享相关对象最干净的方法是什么。在我的特定场景中,我有一个UploadController和一个ProductController,它们相互配合使用来上传图像文件。当UploadController上传文件时,有关上传的数据存储在Session中。发生这种情况后,我需要访问ProductController中的Session数据。如果我在两个控制器中为包含我的上传信息的Session变量创建一个get / set属性,我将能够访问该数据,但同时我将违反各种DRY,更不用说创建了一个,充其量,令人困惑的设计,其中一个对象由两个完全断开连接的对象共享和修改。

What do you suggest?

你有什么建议?

Exact Context:

A file upload View posts a file to UploadController.ImageWithpreview(), which then reads in the posted file and copies it to a temporary directory. After saving the file, another class produces a thumbnail of the uploaded image. The path to both the original file and the generated thumbnail are then returned with a JsonResult to a javascript callback which updates some dynamic content in a form on the page which can be "Saved" or "Cancelled". Whether the uploaded image is saved or it is skipped, I need to either move or delete both it and the generated thumbnail from the temporary directory. To facilitate this, UploadController keeps track of all of the upload files and their thumbnails in a Session-maintained Queue object.

文件上载View将文件发布到UploadController.ImageWithpreview(),然后读取已发布的文件并将其复制到临时目录。保存文件后,另一个类生成上传图像的缩略图。然后,使用JsonResult将原始文件和生成的缩略图的路径返回到javascript回调,该回调更新页面上可以“已保存”或“已取消”的表单中的某些动态内容。无论上传的图像是保存还是被跳过,我都需要从临时目录中移动或删除它和生成的缩略图。为此,UploadController在Session维护的Queue对象中跟踪所有上传文件及其缩略图。

Back in the View: after the form is populated with a generated thumbnail of the image that was uploaded, the form posts back to the ProductsController where the selected file is identified (currently I store the filename in a Hidden field, which I realize is a horrible vulnerability), and then copied out of the temp directory to a permanent location. Ideally, I would like to simply access the Queue I have stored in the Session so that the form does not need to contain the image location as it does now. This is how I have envisioned my solution, but I'll eagerly listen to any comments or criticisms.

返回视图:在使用生成的图像缩略图填充表单后,表单将回发到ProductsController,其中标识了所选文件(目前我将文件名存储在隐藏字段中,我意识到这是一个可怕的漏洞),然后从临时目录复制到永久位置。理想情况下,我想简单地访问我在Session中存储的队列,这样表单就不需要像现在一样包含图像位置。这就是我设想的解决方案,但我会热切地听取任何评论或批评。

2 个解决方案

#1


3  

A couple of solutions come to mind. You could use a "SessionState" class that maps into the request and gets/sets the info as such (I'm doing this from memory so this is unlikely to compile and is meant to convey the point):

我想到了几个解决方案。您可以使用映射到请求的“SessionState”类并获取/设置信息(我正在从内存中执行此操作,因此这不太可能编译并且意味着传达这一点):

internal class SessionState
{
  string ImageName
  {
    get { return HttpContext.Current.Session["ImageName"]; }
    set { HttpContext.Current.Session["ImageName"] = value; }
  }
}

And then from the controller, do something like:

然后从控制器,执行以下操作:

  var sessionState = new SessionState();
  sessionState.ImageName = "xyz";
  /* Or */
  var imageName = sessionState.ImageName;

Alternatively, you could create a controller extension method:

或者,您可以创建控制器扩展方法:

public static class SessionControllerExtensions
{
  public static string GetImageName(this IController controller)
  {
    return HttpContext.Current.Session["ImageName"];
  }

  public static string SetImageName(this IController controller, string imageName)
  {
    HttpContext.Current.Session["ImageName"] = imageName;
  }
}

Then from the controller:

然后从控制器:

  this.SetImageName("xyz");
  /* or */
  var imageName = this.GetImageName();

This is certainly DRY. That said, I don't particularly like either of these solutions as I prefer to store as little data, if any, in session. But if you're intent is to hold onto all of this information without having to load/discern it from some other source, this is the quickest (dirtiest) way I can think of to do it. I'm quite certain there's a much more elegant solution, but I don't have all of the information about what it is you're trying to do and what the problem domain is.

这肯定是干的。也就是说,我并不特别喜欢这些解决方案中的任何一种,因为我更喜欢在会话中存储尽可能少的数据(如果有的话)。但是如果你的意图是保留所有这些信息而不必从其他来源加载/辨别它,这是我能想到的最快(最脏)的方式。我非常肯定有一个更优雅的解决方案,但我没有关于你正在尝试做什么以及问题域是什么的所有信息。

Keep in mind that when storing information in the session, you will have to dehydrate/rehydrate the objects via serialization and you may not be getting the performance you think you are from doing it this way.

请记住,在会话中存储信息时,您将不得不通过序列化对对象进行脱水/再水化,并且您可能无法通过这种方式获得您认为的性能。

Hope this helps.

希望这可以帮助。

EDIT: In response to additional information Not sure on where you're looking to deploy this, but processing images "real-time" is a sure fire way to be hit with a DoS attack. My suggestion to you is as follows -- this is assuming that this is public facing and anyone can upload an image:

编辑:回应其他信息不确定你要在哪里部署这个,但处理图像“实时”是一个肯定的火灾方式与DoS攻击。我给你的建议如下 - 假设这是面向公众的,任何人都可以上传图片:

1) Allow the user to upload an image. This image goes into the processing queue for background processing by the application or some service. Additionally, the name of the image goes into the user's personal processing queue -- likely a table in the database. Information about background processing in a web app can be found @ Schedule a job in hosted web server

1)允许用户上传图像。该图像进入处理队列以供应用程序或某些服务进行后台处理。此外,图像的名称会进入用户的个人处理队列 - 可能是数据库中的表。有关Web应用程序中后台处理的信息,请参阅@在托管Web服务器中安排作业

2) Process these images and, while processing, display a "processing graphic". You can have an ajax request on the product page that checks for images being processed and trys to reload them every X seconds.

2)处理这些图像,并在处理时显示“处理图形”。您可以在产品页面上有一个ajax请求,用于检查正在处理的图像,并尝试每隔X秒重新加载它们。

3) While an image is being "processed", the user can opt out of processing assuming they're the one that uploaded the image. This is available either on the product page(s) that display the image or on a separate "user queue" view that will allow them to remove the image from consideration.

3)当图像被“处理”时,用户可以选择退出处理,假设它们是上载图像的那个。这可以在显示图像的产品页面上显示,也可以在单独的“用户队列”视图中使用,以便从图像中删除图像。

So, you end up with some more domain objects and those objects are managed by the queue. I'm a strong advocate of convention over configuration so the final destination of the product image(s) should be predefined. Something like:

因此,您最终会得到更多域对象,并且这些对象由队列管理。我强烈支持约定优于配置,因此应该预先定义产品图像的最终目的地。就像是:

images/products/{id}.jpg or, if a collection, images/products/{id}/{sequence}.jpg.

images / products / {id} .jpg或者,如果是一个集合,images / products / {id} / {sequence} .jpg。

You then don't need to know the destination in the form. It's the same for all images.

然后,您无需知道表单中的目的地。所有图像都是一样的。

The queue then needs to know where the temp image was uploaded and what the product id was. The queue worker pops items from the queue, processes them, and stores them accordingly.

然后,队列需要知道临时图像的上传位置以及产品ID。队列工作程序从队列中弹出项目,处理它们并相应地存储它们。

I know this sounds a little more "structured" than what you originally intended, but I think it's a little cleaner.

我知道这听起来比你原定的更“结构化”,但我认为它更清洁一些。

#2


1  

Is there complete equivalence between the UploadController and ProductController?

UploadController和ProductController之间是否完全等价?

As files are uploaded by the UploadController, data about the upload is stored in the Session. After this happens I need to access that Session data in the ProductController.

当UploadController上传文件时,有关上传的数据存储在Session中。发生这种情况后,我需要访问ProductController中的Session数据。

As I read that the UploadControl needs read and write access to Upload data, the ProductController needs only read.

当我读到UploadControl需要对上传数据的读写访问时,ProductController只需要读取。

If that's true then you can make it clear by using an immuatable wrapper around the upload information and have the UploadController put that into the session.

如果这是真的那么你可以通过在上传信息周围使用一个不可移动的包装器并让UploadController将它放入会话中来表明它。

The Session itself is by definiton a public shared noticeboard, decouples explicit relationships at the cost of allowing anyone to get and put. You could allow the ProductController to know about the UploadController and hence remove the need for passing the upload information via the session. My instinct is that the upload info is interesting to the public, so using Session is reasonable.

会话本身定义为公共共享布告板,以允许任何人获取和放置的成本分离显式关系。您可以允许ProductController了解UploadController,因此无需通过会话传递上载信息。我的直觉是上传信息对公众很有意思,所以使用Session是合理的。

I don't see any DRY violation here, we are explicitly trying to separate responsibilities.

我没有看到任何DRY违规行为,我们明确地试图分离责任。

#1


3  

A couple of solutions come to mind. You could use a "SessionState" class that maps into the request and gets/sets the info as such (I'm doing this from memory so this is unlikely to compile and is meant to convey the point):

我想到了几个解决方案。您可以使用映射到请求的“SessionState”类并获取/设置信息(我正在从内存中执行此操作,因此这不太可能编译并且意味着传达这一点):

internal class SessionState
{
  string ImageName
  {
    get { return HttpContext.Current.Session["ImageName"]; }
    set { HttpContext.Current.Session["ImageName"] = value; }
  }
}

And then from the controller, do something like:

然后从控制器,执行以下操作:

  var sessionState = new SessionState();
  sessionState.ImageName = "xyz";
  /* Or */
  var imageName = sessionState.ImageName;

Alternatively, you could create a controller extension method:

或者,您可以创建控制器扩展方法:

public static class SessionControllerExtensions
{
  public static string GetImageName(this IController controller)
  {
    return HttpContext.Current.Session["ImageName"];
  }

  public static string SetImageName(this IController controller, string imageName)
  {
    HttpContext.Current.Session["ImageName"] = imageName;
  }
}

Then from the controller:

然后从控制器:

  this.SetImageName("xyz");
  /* or */
  var imageName = this.GetImageName();

This is certainly DRY. That said, I don't particularly like either of these solutions as I prefer to store as little data, if any, in session. But if you're intent is to hold onto all of this information without having to load/discern it from some other source, this is the quickest (dirtiest) way I can think of to do it. I'm quite certain there's a much more elegant solution, but I don't have all of the information about what it is you're trying to do and what the problem domain is.

这肯定是干的。也就是说,我并不特别喜欢这些解决方案中的任何一种,因为我更喜欢在会话中存储尽可能少的数据(如果有的话)。但是如果你的意图是保留所有这些信息而不必从其他来源加载/辨别它,这是我能想到的最快(最脏)的方式。我非常肯定有一个更优雅的解决方案,但我没有关于你正在尝试做什么以及问题域是什么的所有信息。

Keep in mind that when storing information in the session, you will have to dehydrate/rehydrate the objects via serialization and you may not be getting the performance you think you are from doing it this way.

请记住,在会话中存储信息时,您将不得不通过序列化对对象进行脱水/再水化,并且您可能无法通过这种方式获得您认为的性能。

Hope this helps.

希望这可以帮助。

EDIT: In response to additional information Not sure on where you're looking to deploy this, but processing images "real-time" is a sure fire way to be hit with a DoS attack. My suggestion to you is as follows -- this is assuming that this is public facing and anyone can upload an image:

编辑:回应其他信息不确定你要在哪里部署这个,但处理图像“实时”是一个肯定的火灾方式与DoS攻击。我给你的建议如下 - 假设这是面向公众的,任何人都可以上传图片:

1) Allow the user to upload an image. This image goes into the processing queue for background processing by the application or some service. Additionally, the name of the image goes into the user's personal processing queue -- likely a table in the database. Information about background processing in a web app can be found @ Schedule a job in hosted web server

1)允许用户上传图像。该图像进入处理队列以供应用程序或某些服务进行后台处理。此外,图像的名称会进入用户的个人处理队列 - 可能是数据库中的表。有关Web应用程序中后台处理的信息,请参阅@在托管Web服务器中安排作业

2) Process these images and, while processing, display a "processing graphic". You can have an ajax request on the product page that checks for images being processed and trys to reload them every X seconds.

2)处理这些图像,并在处理时显示“处理图形”。您可以在产品页面上有一个ajax请求,用于检查正在处理的图像,并尝试每隔X秒重新加载它们。

3) While an image is being "processed", the user can opt out of processing assuming they're the one that uploaded the image. This is available either on the product page(s) that display the image or on a separate "user queue" view that will allow them to remove the image from consideration.

3)当图像被“处理”时,用户可以选择退出处理,假设它们是上载图像的那个。这可以在显示图像的产品页面上显示,也可以在单独的“用户队列”视图中使用,以便从图像中删除图像。

So, you end up with some more domain objects and those objects are managed by the queue. I'm a strong advocate of convention over configuration so the final destination of the product image(s) should be predefined. Something like:

因此,您最终会得到更多域对象,并且这些对象由队列管理。我强烈支持约定优于配置,因此应该预先定义产品图像的最终目的地。就像是:

images/products/{id}.jpg or, if a collection, images/products/{id}/{sequence}.jpg.

images / products / {id} .jpg或者,如果是一个集合,images / products / {id} / {sequence} .jpg。

You then don't need to know the destination in the form. It's the same for all images.

然后,您无需知道表单中的目的地。所有图像都是一样的。

The queue then needs to know where the temp image was uploaded and what the product id was. The queue worker pops items from the queue, processes them, and stores them accordingly.

然后,队列需要知道临时图像的上传位置以及产品ID。队列工作程序从队列中弹出项目,处理它们并相应地存储它们。

I know this sounds a little more "structured" than what you originally intended, but I think it's a little cleaner.

我知道这听起来比你原定的更“结构化”,但我认为它更清洁一些。

#2


1  

Is there complete equivalence between the UploadController and ProductController?

UploadController和ProductController之间是否完全等价?

As files are uploaded by the UploadController, data about the upload is stored in the Session. After this happens I need to access that Session data in the ProductController.

当UploadController上传文件时,有关上传的数据存储在Session中。发生这种情况后,我需要访问ProductController中的Session数据。

As I read that the UploadControl needs read and write access to Upload data, the ProductController needs only read.

当我读到UploadControl需要对上传数据的读写访问时,ProductController只需要读取。

If that's true then you can make it clear by using an immuatable wrapper around the upload information and have the UploadController put that into the session.

如果这是真的那么你可以通过在上传信息周围使用一个不可移动的包装器并让UploadController将它放入会话中来表明它。

The Session itself is by definiton a public shared noticeboard, decouples explicit relationships at the cost of allowing anyone to get and put. You could allow the ProductController to know about the UploadController and hence remove the need for passing the upload information via the session. My instinct is that the upload info is interesting to the public, so using Session is reasonable.

会话本身定义为公共共享布告板,以允许任何人获取和放置的成本分离显式关系。您可以允许ProductController了解UploadController,因此无需通过会话传递上载信息。我的直觉是上传信息对公众很有意思,所以使用Session是合理的。

I don't see any DRY violation here, we are explicitly trying to separate responsibilities.

我没有看到任何DRY违规行为,我们明确地试图分离责任。