【问题标题】:Link to a stored procedure result set from a .cshtml page in ASP.Net Razor Pages从 ASP.Net Razor 页面中的 .cshtml 页面链接到存储过程结果集
【发布时间】:2019-09-05 11:20:24
【问题描述】:

我想看某种媒体类型的电影(即 DVD、蓝光、VHS),所以我创建了存储过程来做到这一点。此存储过程返回具有与媒体类型关联的 ID 的电影。例如,ID 1 返回 DVD。我的媒体类型有一个页面,其中列出了媒体(剃须刀页面在搭建脚手架后自动为我创建了详细信息页面)。

  1. 我的存储过程代码如下:
using System;
using System.ComponentModel.DataAnnotations;
using System.Data;
using System.Data.SqlClient;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;

namespace Movies.Models
{
    public class MovieDataAccessLayer
    {
        string connectionString = "Server=ServerName;Database=DatabaseMovies;Trusted_Connection=True;MultipleActiveResultSets=true";

        public IEnumerable<Movies> MovieByMedia(byte? MediaID)
        {
            List<Movies> lstmovie = new List<Movies>();

            using (SqlConnection con = new SqlConnection(connectionString))
            {
                SqlCommand cmd = new SqlCommand("usp_MovieByMedia", con);
                cmd.CommandType = CommandType.StoredProcedure;

                con.Open();
                SqlDataReader rdr = cmd.ExecuteReader();

                while (rdr.Read())
                {
                    Movies movies = new Movies();

                    movies.MovieTitle = rdr["MovieTitle"].ToString();
                    movies.MovieYear = Convert.ToInt32(rdr["MovieYear"]);

                    lstmovie.Add(movies);
                }

                con.Close();

            }
            return lstmovie;
        }

    }
}

  1. 这是我的媒体详细信息 .cshtml.cs 代码
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.RazorPages;
using Microsoft.EntityFrameworkCore;
using Movies.Models;

namespace Movies.Pages.MediaDetails
{
    public class DetailsModel : PageModel
    {
        private readonly Movies.Models.MoviesContext _context;

        public DetailsModel(Movies.Models.MoviesContext context)
        {
            _context = context;
        }

        public Media Media { get; set; }

        public async Task<IActionResult> OnGetAsync(byte? id)
        {
            if (id == null)
            {
                return NotFound();
            }

            Media = await _context.Media.FirstOrDefaultAsync(m => m.MediaTypeID == id);


            if (Media == null)
            {
                return NotFound();
            }
            return Page();
        }
    }
}
  1. 这是我的 .cshtml 页面
@model Movies.Pages.MediaDetails.DetailsModel

@{
    ViewData["Title"] = "Details";
}

<h1>Details</h1>

<div>
    <h4>Media</h4>
    <hr />
    <dl class="row">
        <dt class="col-sm-2">
            @Html.DisplayNameFor(model => model.Media.MediaType)
        </dt>
        <dd class="col-sm-10">
            @Html.DisplayFor(model => model.Media.MediaType)
        </dd>
    </dl>
</div>
<div>
    <a asp-page="./Edit" asp-route-id="@Model.Media.MediaTypeID">Edit</a> |
    <a asp-page="./Index">Back to List</a>
</div>
  1. 这是我的电影详细信息 .cshtml 的代码,我尝试连接到我的媒体 .cshtml 但没有成功
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.RazorPages;
using Microsoft.EntityFrameworkCore;
using Movies.Models;

namespace Movies.Pages.MovieDetails
{
    public class DetailsModel : PageModel
    {
        private readonly Movies.Models.MoviesContext _context;

        public DetailsModel(Movies.Models.MoviesContext context)
        {
            _context = context;
        }

        MovieDataAccessLayer objmovie = new MovieDataAccessLayer();
        public Movies 
            Movies { get; set; }

        public async Task<IActionResult> OnGetAsync(byte id)
        {
            Movies = await _context.Movies.FirstOrDefaultAsync(m => m.MediaTypeID == id);

            if (Movies == null)
            {
                return NotFound();
            }
            return Page();
        }
    }
}

我想修改列出我的媒体类型的详细信息页面,以便当我单击相应媒体类型的“详细信息”时,我可以为我单击的媒体类型生成存储过程的结果。例如,当我单击“DVD”时,我的存储过程被执行以只显示我的 DVD。我该怎么做呢?我知道我必须以某种方式引用我的电影数据访问层来执行我的 SP。任何帮助将不胜感激。

【问题讨论】:

  • 你在GenreID 方法中作为参数使用MovieByGenre() 但你没有使用它,这是一个错字吗? MovieByGenre() 当前是否也返回数据库中的所有电影?
  • 感谢您指出这一点,Izzy。这是一个错字。我开始使用 GenreID 进行我的程序,但这会有点复杂,所以我切换到 MediaID 并且没有更新它。对困惑感到抱歉。它也应该是 MovieByMedia()。此过程返回某个 mediaID 的所有电影。因此,例如,MediaID 为 1 将返回所有 DVD 电影。
  • 但是您没有将 id 作为参数传递给 sproc,所以我对过滤是否完成感到困惑。同样在您的OnGetAsync 操作中不应该是Movies = objmovie.MovieByGenre(id).FirstOrDefault(i =&gt; i.MediaTypeID == id); 吗?当然,如果在数据库中完成过滤,则此处不需要 FirstOrDefault,您必须创建一个单独的方法,在其中返回单个 Movies 对象
  • 这是我不明白的一部分。我如何以及在何处传递参数以说“当我单击 DVD 时,将参数 1 传递给此存储过程”?我想我应该在 Media Details .cshtml 页面上这样做,但我不确定。我是 .Net 的新手,我正在尝试将其拼凑起来。
  • 您想在数据库或应用程序中进行过滤吗?因为你有两种方法可以解决你的问题。

标签: asp.net tsql stored-procedures razor razor-pages


【解决方案1】:

以下代码对我有用。 Izzy 的回复很有帮助,但需要进行一些调整。

我的数据访问层

using System;
using System.ComponentModel.DataAnnotations;
using System.Data;
using System.Data.SqlClient;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;

namespace MyMovies.Models
{
    public class MovieByMediaDataAccessLayer
    {
        public List<Movies> GetMovieByMedia(byte? MediaID)
        {
            string connectionString = "Server=Servername;Database=Movies;Trusted_Connection=True;MultipleActiveResultSets=true";
            var movies = new Movies();
            using (var con = new SqlConnection(connectionString))
            {
                using (var cmd = new SqlCommand("usp_MovieByMedia", con))
                {
                    cmd.Parameters.Add(new SqlParameter("@MediaID", SqlDbType.TinyInt) { Value = MediaID });
                    cmd.CommandType = CommandType.StoredProcedure;
                    con.Open();

                    using (var reader = cmd.ExecuteReader())
                    {
                        List<Movies> Movies = new List<Movies>();
                        while (reader.Read())
                        {
                            Movies movie = new Movies()
                            {
                                MovieTitle = reader["MovieTitle"].ToString(),
                                MovieYear = Convert.ToInt32(reader["MovieYear"])
                            };
                            Movies.Add(movie);
                        }
                        return Movies;
                    }
                }

            }
        }
    }
}

我的详细信息类,我返回从存储过程(.cshtml.cs 文件)执行的所有电影

using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.RazorPages;
using Microsoft.EntityFrameworkCore;
using MyMovies.Models;

namespace MyMovies.Pages.MovieDetails
{
    public class DetailsModel : PageModel
    {
        private readonly MyMovies.Models.MyMoviesContext _context;

        public DetailsModel(MyMovies.Models.MyMoviesContext context)
        {
            _context = context;
        }



        MovieByMediaDataAccessLayer objmovie = new MovieByMediaDataAccessLayer();

        public List<Movies> Movies { get; set; }


        public async Task<IActionResult> OnGetAsync(byte? id)
        {
            if (id == null)
            {
                return NotFound();
            }

            Movies = objmovie.GetMovieByMedia(id);
            if (Movies == null)
            {
                return NotFound();
            }
            return Page();
        }
    }
}

在我的媒体“详细信息”剃须刀页面上,我实际上只是继续发布了我的电影详细信息,因为对于每种媒体类型,我都想显示所有电影。

@page
@model MyMovies.Pages.MovieDetails.DetailsModel

@{
    ViewData["Title"] = "Details";
}

<h4>Movies</h4>
<hr />
<div>
    <table>
        <tr>
            <th>Movie Title</th>
            <th>Movie Year</th>
        </tr>
        @foreach (var item in Model.Movies)
        {
            <tr>
                <td class="col-sm-10">
                    @Html.DisplayFor(Model => item.MovieTitle)
                </td>
                <td class="col-sm-10">
                    @Html.DisplayFor(Model => item.MovieYear)
                </td>
            </tr>
        }
    </table>
</div>

<div>
    <a asp-page="./Index">Back to List</a>
</div>


【讨论】:

    【解决方案2】:

    根据您的 cmets:

    您需要首先通过引入where 子句并接受参数来对存储过程进行更改。

    ALTER PROCEDURE usp_MovieByMedia
        @MediaID INT //This is the parameter we will accept in the stored procedure which will be passed via our c# code
        SELECT MovieTitle, MovieYear 
        FROM MOVIES 
        WHERE MediaTypeID = @MediaID   // I'm assuming the names here, change as required
    

    现在你想改变你的方法如下:

    public Movies GetMovieByMedia(int MediaID)
    {
       string connectionString = "Server=ServerName;Database=DatabaseMovies;Trusted_Connection=True;MultipleActiveResultSets=true";
       var movies = new Movies();
    
       using (var con = new SqlConnection(connectionString))
       {
         using (var cmd = new SqlCommand("usp_MovieByMedia", con))
         {
            cmd.Parameters.Add(new SqlParameter("@MediaID", SqlDbType.Int) { Value = MediaID }); //you was missing this.
            cmd.CommandType = CommandType.StoredProcedure;
            con.Open();
    
            using (var reader = cmd.ExecuteReader())
            {
               if (reader.HasRows)
               {
                  movies.MovieTitle = reader["MovieTitle"].ToString();
                  movies.MovieYear = Convert.ToInt32(reader["MovieYear"]);
               }
             }
          }
        }
        return movies;
     }
    

    这将返回一个 Movie 对象。

    请注意,我假设您的 MediaTypeID 列在数据库中是 INT 类型,而不是 varbinary(1)/binary(1)。如果这是不正确的,请根据需要进行更改。

    现在您可以在 OnGetAsync 操作中执行以下操作:

    Movies = objmovie.GetMovieByMedia(id);
    

    如果您不想在数据库中进行过滤,那么您可以使用 linq 在您的应用程序中进行过滤:

    Movies = objmovie.GetMovieByMedia(id).FirstOrDefault(i => i.MediaTypeID == id); //this will only work if your method stays with the return type of IEnumerable<Movies>
    

    【讨论】:

    • Izzy,我会试试这个,看看它对我有用。真的很感激。我很快就会回来更新。
    • 尝试此方法后,我确实有几个问题。 1. 既然我想点击媒体类型(在媒体详情页面),我的Async Action代码是否应该写在MediaDetails.cshtml.cs文件中?还是应该写在 MovieDetails.cshtml.cs 文件中? 2. 没有 Clinq 的代码给了我一个不同步的警告。系统提示我使用 await,但我不知道是否可以将其与新代码/格式一起使用。
    • 你想在显示电影细节​​的地方调用它。注意 - 如果你想返回多个项目,那么你的存储过程将只返回一个项目,然后在 where 子句中使用 IN。不,如果你想让它异步,你不能在这段代码中使用 await 然后看here
    • 我收到以下错误,尽管我知道数据存在。 InvalidOperationException: 不存在数据时尝试读取无效。
    • 我尝试使用 CLinq 并收到 CS1061 错误,指出 IEnumerable 不包含 FirstOrDefault 的定义。但除此之外,我希望能够列出不止一部电影,所以 FirstOrDefault 也不会真正满足我的需求。
    猜你喜欢
    • 2020-09-02
    • 2011-06-23
    • 2022-09-24
    • 1970-01-01
    • 2021-06-07
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-05-19
    相关资源
    最近更新 更多