ส่งผ่านข้อมูลไปยังเค้าโครงที่ใช้ร่วมกันกับทุกหน้า


125

ฉันมีเว็บไซต์ที่มีหน้าเค้าโครง อย่างไรก็ตามหน้าเลย์เอาต์นี้มีข้อมูลซึ่งโมเดลเพจทั้งหมดจะต้องระบุชื่อเพจชื่อเพจและตำแหน่งที่เราเป็นจริงสำหรับผู้ช่วย HTML ที่ฉันทำซึ่งดำเนินการบางอย่าง นอกจากนี้แต่ละเพจยังมีคุณสมบัติของโมเดลมุมมองของตนเอง

ฉันจะทำเช่นนี้ได้อย่างไร? ดูเหมือนว่าการพิมพ์เค้าโครงเป็นความคิดที่ดี แต่ฉันจะส่งข้อมูลเหล่านี้ได้อย่างไร


10
สำหรับใครก็ตามที่อ่านคำตอบที่นี่โปรดดูที่stackoverflow.com/a/21130867/706346ซึ่งคุณจะเห็นวิธีแก้ปัญหาที่ง่ายและดีกว่ามากซึ่งทุกสิ่งที่โพสต์ไว้ที่นี่
Avrohom Yisroel

5
@AvrohomYisroel คำแนะนำที่ดี อย่างไรก็ตามฉันชอบวิธีการของ @Colin Bacon เพราะพิมพ์แรงและไม่อยู่ในไฟล์ViewBag. บางทีอาจเป็นเรื่องของความชอบ เพิ่มคะแนนความคิดเห็นของคุณแม้ว่า
JP Hellemons

สำหรับ mvc 5 ดูคำตอบนี้: stackoverflow.com/a/46783375/5519026
Laz Ziya

คำตอบ:


143

หากคุณจำเป็นต้องส่งผ่านคุณสมบัติเดียวกันไปยังแต่ละเพจการสร้างโมเดลมุมมองพื้นฐานที่ใช้โดยโมเดลมุมมองทั้งหมดของคุณจะเป็นการดี จากนั้นหน้าเค้าโครงของคุณสามารถใช้โมเดลพื้นฐานนี้ได้

หากมีตรรกะที่จำเป็นอยู่เบื้องหลังข้อมูลนี้ควรใส่ลงในคอนโทรลเลอร์พื้นฐานที่คอนโทรลเลอร์ของคุณใช้ทั้งหมด

มีหลายสิ่งที่คุณทำได้วิธีการที่สำคัญคือไม่ต้องใช้รหัสเดียวกันซ้ำในหลาย ๆ ที่

แก้ไข: อัปเดตจากความคิดเห็นด้านล่าง

นี่คือตัวอย่างง่ายๆเพื่อแสดงแนวคิด

สร้างโมเดลมุมมองพื้นฐานที่โมเดลมุมมองทั้งหมดจะสืบทอดมา

public abstract class ViewModelBase
{
    public string Name { get; set; }
}

public class HomeViewModel : ViewModelBase
{
}

หน้าเค้าโครงของคุณสามารถใช้สิ่งนี้เป็นต้นแบบ

@model ViewModelBase
<!DOCTYPE html>
<html>
    <head>
        <meta name="viewport" content="width=device-width" />
        <title>Test</title>
    </head>
    <body>
        <header>
            Hello @Model.Name
        </header>
        <div>
            @this.RenderBody()
        </div>
    </body>
</html>

สุดท้ายตั้งค่าข้อมูลในวิธีการดำเนินการ

public class HomeController
{
    public ActionResult Index()
    {
        return this.View(new HomeViewModel { Name = "Bacon" });
    }
}

12
แต่ข้อมูลจะถูกใช้ในการจัดวาง ฉันจะส่งข้อมูลไปยังเค้าโครงได้อย่างไร
Rushino

2
ที่สมบูรณ์แบบ! ฉันเห็นข้อผิดพลาดของฉัน ฉันลืมที่จะส่งต่อโมเดลให้กับมุมมอง .. ช่างเป็นข้อผิดพลาดที่ไม่น่าเชื่อ ขอบคุณ!
Rushino

7
ปัญหาของแนวทางนี้คือบางครั้งไม่ใช่ทุกมุมมองที่มี ViewModel ดังนั้นจึงใช้ไม่ได้ในกรณีนั้น: O /
Cacho Santa

17
แต่นี่ไม่จำเป็นต้องให้ทุกตัวควบคุมและทุกการกระทำมีรหัส {Name = "Bacon"} และถ้าฉันต้องการเพิ่มคุณสมบัติอื่นใน ViewModelBase ฉันจะต้องไปที่ตัวควบคุมทุกตัวและทุกการกระทำและเพิ่มรหัสเพื่อเติมคุณสมบัตินั้น? คุณพูดถึง "หากมีตรรกะที่จำเป็น [... ] สิ่งนี้ควรใส่ลงในตัวควบคุมฐาน [... ]" วิธีนี้จะกำจัดรหัสซ้ำในทุกคอนโทรลเลอร์และทุกการกระทำได้อย่างไร
ลี

5
@Lee ถ้าเป็นข้อมูลทั่วไปในทุกหน้าตัวควบคุมพื้นฐานคือที่ที่คุณจะใส่สิ่งนี้ ตัวควบคุมของคุณจะสืบทอดจากตัวควบคุมฐานนี้ เช่นpublic class HomeController : BaseController. ด้วยวิธีนี้โค้ดทั่วไปจะต้องเขียนเพียงครั้งเดียวและสามารถใช้ได้กับคอนโทรลเลอร์ทั้งหมด
Colin Bacon

73

ฉันใช้ตัวช่วย html ของ RenderAction สำหรับมีดโกนในเค้าโครง

@{
   Html.RenderAction("Action", "Controller");
 }

ฉันต้องการมันสำหรับสตริงธรรมดา ดังนั้นการกระทำของฉันจะส่งคืนสตริงและเขียนลงในมุมมองที่ง่าย แต่ถ้าคุณต้องการข้อมูลที่ซับซ้อนคุณสามารถส่งคืน PartialViewResult และโมเดลได้

 public PartialViewResult Action()
    {
        var model = someList;
        return PartialView("~/Views/Shared/_maPartialView.cshtml", model);
    }

คุณเพียงแค่ต้องใส่โมเดลของคุณโดยเริ่มจากมุมมองบางส่วน '_maPartialView.cshtml' ที่คุณสร้างขึ้น

@model List<WhatEverYourObjeIs>

จากนั้นคุณสามารถใช้ข้อมูลในโมเดลในมุมมองบางส่วนนั้นด้วย html


18
นี่คือคำตอบที่ดีที่สุด!
Gingerbreadboy

@gingerbreadboy เห็นด้วยว่าส่งเสริมการห่อหุ้มที่ดีและแยกความกังวล
A-Dubb

35

อีกทางเลือกหนึ่งคือการสร้างคลาส LayoutModel แยกต่างหากพร้อมคุณสมบัติทั้งหมดที่คุณต้องการในเค้าโครงจากนั้นใส่อินสแตนซ์ของคลาสนี้ลงใน ViewBag ฉันใช้วิธีการควบคุม OnActionExecuting เพื่อเติมข้อมูล จากนั้นในช่วงเริ่มต้นของการจัดวางคุณสามารถดึงวัตถุนี้กลับมาจาก ViewBag และเข้าถึงวัตถุที่พิมพ์หนักแน่นนี้ต่อไป


1
สิ่งนี้ฟังดูเหมือนวิธีแก้ปัญหาที่เจ็บปวดน้อยที่สุดมีข้อเสียหรือไม่? +1
formatc

2
ทางออกที่ดีที่สุดแน่นอนและฉันไม่เห็นข้อเสียใด ๆ
Wiktor Zychla

7
ฉันไม่เห็นสิ่งนี้ให้คุณ หากคุณมีคลาสที่มีคุณสมบัติทั้งหมดที่คุณต้องการสำหรับเลย์เอาต์ทำไมต้องเพิ่มลงใน ViewBag เพียง แต่ต้องส่งกลับอีกครั้ง ใช้โมเดลในมุมมองเค้าโครงคุณยังสามารถเติมข้อมูลโมเดลOnActionExecutingได้ การใช้ ViewBag ยังหมายความว่าคุณต้องสูญเสียความปลอดภัยในตัวควบคุมของคุณไม่ใช่เรื่องดี
Colin Bacon

3
สิ่งนี้ทำให้ฉันสามารถเพิ่มโมเดลสำหรับเลย์เอาต์โดยไม่ต้องปรับโครงสร้างโมเดลทั้งหมดเพื่อสืบทอดจากโมเดล "ซูเปอร์" เดียวในทุกวิธีของคอนโทรลเลอร์ทั้งหมดในโปรเจ็กต์ที่มีอยู่แล้ว หากคุณเริ่มต้นใหม่คุณอาจเลือกที่จะรับโมเดลทั้งหมดของคุณจากรูททั่วไปแทน
DenNukem

5
@ColinBacon ข้อดีอีกอย่างของตัวเลือกนี้คือการดำเนินการของคุณไม่จำเป็นต้องมีโมเดลมุมมองเสมอไป นอกจากนี้ฉันขอยืนยันว่านักพัฒนาจำเป็นต้องรู้ว่าพวกเขาต้องสืบทอดโมเดลมุมมองจากฐานเสมอเป็นข้อเสีย
Josh Noe

28

สันนิษฐานว่ากรณีการใช้งานหลักสำหรับสิ่งนี้คือการรับแบบจำลองพื้นฐานไปยังมุมมองสำหรับการกระทำของคอนโทรลเลอร์ทั้งหมด (หรือส่วนใหญ่)

ด้วยเหตุนี้ฉันจึงใช้คำตอบหลาย ๆ ข้อรวมกันการสนับสนุนลูกหมูหลักกับคำตอบของ Colin Bacon

ถูกต้องที่นี่ยังคงเป็นตรรกะของคอนโทรลเลอร์เนื่องจากเรากำลังเติม viewmodel เพื่อกลับไปที่มุมมอง ดังนั้นตำแหน่งที่ถูกต้องในการวางสิ่งนี้คือในคอนโทรลเลอร์

เราต้องการให้สิ่งนี้เกิดขึ้นกับตัวควบคุมทั้งหมดเพราะเราใช้สิ่งนี้สำหรับหน้าเค้าโครง ฉันใช้มันสำหรับมุมมองบางส่วนที่แสดงในหน้าเค้าโครง

เรายังต้องการประโยชน์เพิ่มเติมของ ViewModel ที่พิมพ์ผิด

ดังนั้นฉันจึงสร้าง BaseViewModel และ BaseController ViewModels Controllers ทั้งหมดจะสืบทอดจาก BaseViewModel และ BaseController ตามลำดับ

รหัส:

BaseController

public class BaseController : Controller
{
    protected override void OnActionExecuted(ActionExecutedContext filterContext)
    {
        base.OnActionExecuted(filterContext);

        var model = filterContext.Controller.ViewData.Model as BaseViewModel;

        model.AwesomeModelProperty = "Awesome Property Value";
        model.FooterModel = this.getFooterModel();
    }

    protected FooterModel getFooterModel()
    {
        FooterModel model = new FooterModel();
        model.FooterModelProperty = "OMG Becky!!! Another Awesome Property!";
    }
}

สังเกตการใช้OnActionExecutedตามที่นำมาจากโพสต์ SO นี้

HomeController

public class HomeController : BaseController
{
    public ActionResult Index(string id)
    {
        HomeIndexModel model = new HomeIndexModel();

        // populate HomeIndexModel ...

        return View(model);
    }
}

BaseViewModel

public class BaseViewModel
{
    public string AwesomeModelProperty { get; set; }
    public FooterModel FooterModel { get; set; }
}

HomeViewModel

public class HomeIndexModel : BaseViewModel
{

    public string FirstName { get; set; }

    // other awesome properties
}

FooterModel

public class FooterModel
{
    public string FooterModelProperty { get; set; }
}

Layout.cshtml

@model WebSite.Models.BaseViewModel
<!DOCTYPE html>
<html>
<head>
    < ... meta tags and styles and whatnot ... >
</head>
<body>
    <header>
        @{ Html.RenderPartial("_Nav", Model.FooterModel.FooterModelProperty);}
    </header>

    <main>
        <div class="container">
            @RenderBody()
        </div>

        @{ Html.RenderPartial("_AnotherPartial", Model); }
        @{ Html.RenderPartial("_Contact"); }
    </main>

    <footer>
        @{ Html.RenderPartial("_Footer", Model.FooterModel); }
    </footer>

    < ... render scripts ... >

    @RenderSection("scripts", required: false)
</body>
</html>

_Nav.cshtml

@model string
<nav>
    <ul>
        <li>
            <a href="@Model" target="_blank">Mind Blown!</a>
        </li>
    </ul>
</nav>

หวังว่านี่จะช่วยได้


2
ฉันใช้วิธีนี้ แต่ชอบการสืบทอดจากอินเทอร์เฟซมากกว่าคลาสพื้นฐาน ฉันเลยทำ: var model = filterContext.Controller.ViewData.Model เป็น IBaseViewModel if (model! = null) {model.AwesomeModelProperty = "Awesome Property Value"; }
Tom Gerken

2
คำตอบที่ดีฉันชอบคนอื่น ๆ ทั้งหมด
Jynn

1
คำตอบที่ดี แต่ฉันมีคำถาม "จะเกิดอะไรขึ้นถ้าฉันมีมุมมองที่ไม่มี ViewModels ... ?
Isma Haro

ลองใช้สิ่งนี้ แต่ใน Index Action OnActionExecuted จะเติม FooterModel จากนั้น HomeIndexModel ใหม่จะถูกสร้างขึ้นด้วย FooterModel ว่าง :(
SteveCav

1
@drizzie: ในตัวควบคุมพื้นฐานของคุณ model คือตัวแปรภายในในวิธีการกรอง: var model = filterContext.Controller.ViewData.Model เป็น BaseViewModel ฉันไม่เข้าใจว่า MVC เข้าใจได้อย่างไรว่าตัวแปรภายในเครื่องนี้เหมือนกับโมเดลที่ HomeController ส่งให้ดู
Hooman Bahreini

9

คุณไม่ต้องยุ่งกับการกระทำหรือเปลี่ยนโมเดลเพียงแค่ใช้ตัวควบคุมพื้นฐานและส่งตัวควบคุมที่มีอยู่จากมุมมองเค้าโครง

สร้างตัวควบคุมฐานด้วยข้อมูลทั่วไปที่ต้องการ (ชื่อ / หน้า / ตำแหน่ง ฯลฯ ) และการเริ่มต้นการดำเนินการ ...

public abstract class _BaseController:Controller {
    public Int32 MyCommonValue { get; private set; }

    protected override void OnActionExecuting(ActionExecutingContext filterContext) {

        MyCommonValue = 12345;

        base.OnActionExecuting(filterContext);
    }
}

ตรวจสอบให้แน่ใจว่าคอนโทรลเลอร์ทุกตัวใช้ตัวควบคุมพื้นฐาน ...

public class UserController:_BaseController {...

ส่งตัวควบคุมพื้นฐานที่มีอยู่จากบริบทมุมมองใน_Layout.cshmlเพจของคุณ...

@{
    var myController = (_BaseController)ViewContext.Controller;
}

ตอนนี้คุณสามารถอ้างถึงค่าในตัวควบคุมพื้นฐานของคุณได้จากหน้าเค้าโครงของคุณ

@myController.MyCommonValue

UPDATE

thisนอกจากนี้คุณยังสามารถสร้างส่วนขยายหน้าเว็บที่จะช่วยให้คุณใช้

//Allows typed "this.Controller()." in cshtml files
public static class MyPageExtensions {
    public static _BaseController Controller(this WebViewPage page) => Controller<_BaseController>(page);
    public static T Controller<T>(this WebViewPage page) where T : _BaseController => (T)page.ViewContext.Controller;
}

จากนั้นคุณต้องจำไว้ว่าจะใช้this.Controller()เมื่อคุณต้องการตัวควบคุมเท่านั้น

@{
    var myController = this.Controller(); //_BaseController
}

หรือตัวควบคุมเฉพาะที่สืบทอดมาจาก_BaseController...

@{
    var myController = this.Controller<MyControllerType>();
}

สิ่งนี้เทียบเท่ากับอะไรใน. net core? เนื่องจาก ViewContext.Controller ไม่มีอยู่และมีการเปลี่ยนแปลงบางอย่างในห่วงโซ่การสืบทอด
Jayanth Thyagarajan

4

หากคุณต้องการส่งแบบจำลองทั้งหมดให้ดำเนินการดังนี้:

@model ViewAsModelBase
<!DOCTYPE html>
<html>
<head>
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta charset="utf-8"/>
    <link href="/img/phytech_icon.ico" rel="shortcut icon" type="image/x-icon" />
    <title>@ViewBag.Title</title>
    @RenderSection("styles", required: false)    
    <script type="text/javascript" src="http://code.jquery.com/jquery-1.8.3.min.js"></script>
    @RenderSection("scripts", required: false)
    @RenderSection("head", required: false)
</head>
<body>
    @Html.Action("_Header","Controller", new {model = Model})
    <section id="content">
        @RenderBody()
    </section>      
    @RenderSection("footer", required: false)
</body>
</html>

และเพิ่มสิ่งนี้ในตัวควบคุม:

public ActionResult _Header(ViewAsModelBase model)

4

ฉันไม่คิดว่าคำตอบเหล่านี้จะยืดหยุ่นเพียงพอสำหรับแอปพลิเคชันระดับองค์กรขนาดใหญ่ ฉันไม่ใช่แฟนของการใช้ ViewBag มากเกินไป แต่ในกรณีนี้ฉันขอยกเว้นเพื่อความยืดหยุ่น นี่คือสิ่งที่ฉันจะทำ ...

คุณควรมีตัวควบคุมพื้นฐานบนคอนโทรลเลอร์ทั้งหมดของคุณ เพิ่มข้อมูลเค้าโครงของคุณ OnActionExecuting ในตัวควบคุมพื้นฐานของคุณ (หรือ OnActionExecuted ถ้าคุณต้องการเลื่อนออกไป) ...

public class BaseController : Controller
{
    protected override void OnActionExecuting(ActionExecutingContext     
        filterContext)
    {
        ViewBag.LayoutViewModel = MyLayoutViewModel;
    }
}

public class HomeController : BaseController
{
    public ActionResult Index()
    {
        return View(homeModel);
    }
}

จากนั้นใน _Layout.cshtml ของคุณดึง ViewModel ของคุณจาก ViewBag ...

@{
  LayoutViewModel model = (LayoutViewModel)ViewBag.LayoutViewModel;
}

<h1>@model.Title</h1>

หรือ...

<h1>@ViewBag.LayoutViewModel.Title</h1>

การทำเช่นนี้ไม่รบกวนการเข้ารหัสสำหรับตัวควบคุมเพจหรือโมเดลมุมมอง


ฉันชอบความคิดของคุณ แต่ถ้าคุณMyLayoutViewModelสร้างแบบไดนามิกฉันจะส่งผ่านพารามิเตอร์ไปยังOnActionExecutingเมธอดได้อย่างไร
Rajmond Burgaj

1
เอ่อยังต้องใช้วิธีbase.OnActionExecuting(filterContext)ของคุณOnActionExecuting!!!
ErikE

4

การสร้างมุมมองพื้นฐานซึ่งแสดงถึงโมเดลมุมมองเค้าโครงเป็นแนวทางที่แย่มาก ลองนึกภาพว่าคุณต้องการมีโมเดลที่แสดงถึงการนำทางที่กำหนดไว้ในเค้าโครง คุณจะทำCustomersViewModel : LayoutNavigationViewModelอย่างไร ทำไม? เหตุใดคุณจึงควรส่งผ่านข้อมูลโมเดลการนำทางผ่านโมเดลมุมมองเดียวทั้งหมดที่คุณมีในโซลูชัน

ควรทุ่มเทโมเดลมุมมองเค้าโครงด้วยตัวของมันเองและไม่ควรบังคับให้โมเดลมุมมองที่เหลือขึ้นอยู่กับโมเดลนั้น

แต่คุณสามารถทำได้ใน_Layout.cshtmlไฟล์ของคุณ:

@{ var model = DependencyResolver.Current.GetService<MyNamespace.LayoutViewModel>(); }

ที่สำคัญที่สุดเราไม่จำเป็นต้องทำnew LayoutViewModel()และเราจะได้รับการอ้างอิงทั้งหมดที่ได้รับการLayoutViewModelแก้ไขสำหรับเรา

เช่น

public class LayoutViewModel
{
    private readonly DataContext dataContext;
    private readonly ApplicationUserManager userManager;

    public LayoutViewModel(DataContext dataContext, ApplicationUserManager userManager)
    {
    }
}

คุณเติมโมเดลนี้ที่ไหน? ใน BaseController ด้วยหรือไม่?
ndberg

ฉันคิดว่านี่จะเป็นความคิดที่ดีสำหรับScopedอ็อบเจ็กต์แบบจำลองโครงร่างใน ASP .. Net Core เช่นกัน
James Wilkins

ฉันไม่มีมุมมองที่จะดึงการพึ่งพา นั่นไม่ใช่ "MVC" อย่างแน่นอน บริการสเป็นรูปแบบการป้องกัน
Jiveman

ตรงกันข้ามกับความเชื่อที่เป็นที่นิยม Service Locator ไม่ใช่การต่อต้านรูปแบบและจริงๆแล้วสิ่งนี้ไม่เกี่ยวข้องกับ MVC คุณแค่โยนคำพูดลอยๆ @Jiveman หรือไม่? blog.gauffin.org/2012/09/service-locator-is-not-an-anti-pattern
hyankov

ประเด็นหลักของ Jgauffin ในบทความนั้นดูเหมือนว่าไม่ควรใช้คำว่า "anti-pattern" กับ Service Locator เนื่องจากอาจมีการใช้ SL ที่ถูกต้องอยู่บ้าง จุดยุติธรรม อย่างไรก็ตามดังที่เห็นได้ชัดในความคิดเห็นการอภิปรายของเขาเองเขาแนะนำว่าแม้ว่า SL อาจเป็นแนวทางที่ถูกต้องเมื่อสร้างไลบรารีและเฟรมเวิร์ก แต่ก็ไม่จำเป็นต้องแนะนำเมื่อสร้างแอปพลิเคชัน (ซึ่งฉันจะพิจารณาคำถามของ OP และการสนทนานี้จะเป็น หมุนไปรอบ ๆ )
Jiveman

3

คำตอบอื่น ๆ ได้ครอบคลุมทุกอย่างเกี่ยวกับวิธีที่เราสามารถส่งโมเดลไปยังหน้าเค้าโครงของเราได้ แต่ฉันพบวิธีที่คุณสามารถส่งผ่านตัวแปรไปยังหน้าเลย์เอาต์ของคุณแบบไดนามิกโดยไม่ต้องใช้โมเดลหรือมุมมองบางส่วนในเลย์เอาต์ของคุณ ให้เราบอกว่าคุณมีรุ่นนี้ -

public class SubLocationsViewModel
{
    public string city { get; set; }
    public string state { get; set; }
}

และคุณต้องการรับเมืองและรัฐแบบไดนามิก สำหรับเช่น

ใน index.cshtml ของคุณคุณสามารถใส่ตัวแปรทั้งสองนี้ใน ViewBag

@model  MyProject.Models.ViewModel.SubLocationsViewModel
@{
    ViewBag.City = Model.city;
    ViewBag.State = Model.state;
}

จากนั้นใน layout.cshtml ของคุณคุณสามารถเข้าถึงตัวแปร viewbag เหล่านั้นได้

<div class="text-wrap">
    <div class="heading">@ViewBag.City @ViewBag.State</div>
</div>

วิธีนี้ใช้งานได้ดี @stun_Gravy มีข้อผิดพลาดในการใช้ ViewBag เพื่อส่งผ่านข้อมูลเช่นบทบาทหรือระดับการเข้าถึงของผู้ใช้หรือไม่?
3 น.

3

มีวิธีอื่นในการจัดการเรื่องนี้ อาจไม่ใช่วิธีที่สะอาดที่สุดจากมุมมองทางสถาปัตยกรรม แต่หลีกเลี่ยงความเจ็บปวดมากมายที่เกี่ยวข้องกับคำตอบอื่น ๆ เพียงแค่ฉีดบริการในรูปแบบ Razor จากนั้นเรียกใช้เมธอดที่ได้รับข้อมูลที่จำเป็น:

@inject IService myService

จากนั้นในมุมมองเค้าโครง:

@if (await myService.GetBoolValue()) {
   // Good to go...
}

อีกครั้งไม่สะอาดในแง่ของสถาปัตยกรรม (เห็นได้ชัดว่าไม่ควรฉีดบริการโดยตรงในมุมมอง) แต่ก็ทำให้งานเสร็จ


ไม่ใช่วิธีที่สะอาดที่สุด? ฉันไม่เห็นด้วย. ฉันคิดว่าสิ่งนี้สะอาดที่สุดเท่าที่จะทำได้: วัตถุจะถูกส่งผ่านจากสถานที่ที่สร้างขึ้นตรงไปยังสถานที่ที่คุณต้องการให้อยู่โดยไม่ทำให้ตัวควบคุมอื่น ๆ "ก่อมลพิษ" ด้วยสิ่งของที่ไม่จำเป็นต้องมองเห็น การใช้@injectเป็นทางออกที่ดีที่สุดในความคิดของฉัน
dasblinkenlight

1
คิดถึงเรื่องนี้มากกว่านี้บางทีคุณคิดถูก ความจริงที่ว่าวิธีนี้หลีกเลี่ยงความเจ็บปวดได้มากเป็นสัญญาณว่าอาจเป็นวิธีที่สะอาดที่สุด ฉันทำงานกับแอป ASP.NET Core ที่มีขนาดใหญ่มากและฉันใช้รูปแบบนี้สำหรับสิ่งต่างๆเช่นลอจิกเบรดครัมบ์การนำทางข้อมูลส่วนหัวที่อยู่ในหน้าส่วนใหญ่สิ่งต่างๆเช่นนั้น ฉันหลีกเลี่ยงความเจ็บปวดอย่างมากโดยทำแบบนี้แทน
Andrew

2

คุณยังสามารถใช้ประโยชน์จากRenderSectionซึ่งจะช่วยให้คุณฉีดModelข้อมูลของคุณลงในไฟล์_Layoutมุมมอง

คุณสามารถฉีดView Modelข้อมูลJson, Script, CSS,HTMLฯลฯ

ในตัวอย่างนี้ฉันกำลังฉีดJsonจากIndexมุมมองของฉันไปที่Layout View

Index.chtml

@section commonLayoutData{

    <script>

        var products = @Html.Raw(Json.Encode(Model.ToList()));

    </script>

    }

_Layout.cshtml

@RenderSection("commonLayoutData", false)

ทำให้ไม่จำเป็นต้องสร้างฐานแยกต่างหาก View Modelนี้ไม่จำเป็นต้องของการสร้างฐานที่แยกต่างหาก

ความหวังช่วยใครบางคน


1
โซลูชั่นที่สมบูรณ์แบบเมื่อคุณเพียงต้องการที่จะทำให้บางสิ่งบางอย่างที่เฉพาะเจาะจงสำหรับมุมมองที่ไม่กี่
Kunal

1

สิ่งที่ฉันทำนั้นง่ายมากและได้ผล

ประกาศคุณสมบัติคงที่ในคอนโทรลเลอร์ใด ๆ หรือคุณสามารถสร้างคลาสข้อมูลที่มีค่าคงที่หากคุณต้องการสิ่งนี้:

public static username = "Admin";
public static UserType = "Administrator";

ค่าเหล่านี้สามารถอัปเดตได้โดยตัวควบคุมตามการดำเนินการ ในภายหลังคุณสามารถใช้ใน _Layout ของคุณ

ใน _layout.cshtml

@project_name.Controllers.HomeController.username
@project_name.Controllers.HomeController.UserType

1
การเพิ่มคำอธิบายในคำตอบของคุณจะเป็นประโยชน์เสมอเพื่อให้ชัดเจนและเข้าใจได้มากขึ้น โปรดอ่านstackoverflow.com/help/how-to-answer
32cupo

0

ทำไมไม่มีใครแนะนำวิธีการขยายบน ViewData?

ตัวเลือกที่ 1

สำหรับฉันแล้วดูเหมือนว่าวิธีแก้ปัญหาที่ล่วงล้ำน้อยที่สุดและง่ายที่สุดสำหรับฉัน ไม่มีสตริงฮาร์ดโค้ด ไม่มีข้อ จำกัด ที่กำหนด ไม่มีการเข้ารหัสมายากล ไม่มีรหัสที่ซับซ้อน

public static class ViewDataExtensions
{
    private const string TitleData = "Title";
    public static void SetTitle<T>(this ViewDataDictionary<T> viewData, string value) => viewData[TitleData] = value;
    public static string GetTitle<T>(this ViewDataDictionary<T> viewData) => (string)viewData[TitleData] ?? "";
}

ตั้งค่าข้อมูลในหน้า

ViewData.SetTitle("abc");

ตัวเลือก # 2

อีกทางเลือกหนึ่งที่ทำให้การประกาศเขตข้อมูลง่ายขึ้น

public static class ViewDataExtensions
{
    public static ViewDataField<string, V> Title<V>(this ViewDataDictionary<V> viewData) => new ViewDataField<string, V>(viewData, "Title", "");
}

public class ViewDataField<T,V>
{
    private readonly ViewDataDictionary<V> _viewData;
    private readonly string _field;
    private readonly T _defaultValue;

    public ViewDataField(ViewDataDictionary<V> viewData, string field, T defaultValue)
    {
        _viewData = viewData;
        _field = field;
        _defaultValue = defaultValue;
    }

    public T Value {
        get => (T)(_viewData[_field] ?? _defaultValue);
        set => _viewData[_field] = value;
    }
}

ตั้งค่าข้อมูลในหน้า การประกาศง่ายกว่าตัวเลือกแรก แต่ไวยากรณ์การใช้งานยาวกว่าเล็กน้อย

ViewData.Title().Value = "abc";

ตัวเลือก # 3

จากนั้นสามารถรวมเข้ากับการส่งคืนอ็อบเจ็กต์เดียวที่มีฟิลด์ที่เกี่ยวข้องกับเค้าโครงทั้งหมดด้วยค่าเริ่มต้น

public static class ViewDataExtensions
{
    private const string LayoutField = "Layout";
    public static LayoutData Layout<T>(this ViewDataDictionary<T> viewData) => 
        (LayoutData)(viewData[LayoutField] ?? (viewData[LayoutField] = new LayoutData()));
}

public class LayoutData
{
    public string Title { get; set; } = "";
}

ตั้งค่าข้อมูลในหน้า

var layout = ViewData.Layout();
layout.Title = "abc";

ตัวเลือกที่สามนี้มีประโยชน์หลายประการและฉันคิดว่าเป็นตัวเลือกที่ดีที่สุดในกรณีส่วนใหญ่:

  • การประกาศฟิลด์และค่าเริ่มต้นที่ง่ายที่สุด

  • ไวยากรณ์การใช้งานที่ง่ายที่สุดเมื่อตั้งค่าหลายฟิลด์

  • อนุญาตให้ตั้งค่าข้อมูลประเภทต่างๆใน ViewData (เช่น Layout, Header, Navigation)

  • อนุญาตรหัสและตรรกะเพิ่มเติมภายในคลาส LayoutData

ปล. อย่าลืมเพิ่มเนมสเปซของ ViewDataExtensions ใน _ViewImports.cshtml


0

คุณสามารถสร้างไฟล์มีดโกนในโฟลเดอร์ App_Code จากนั้นเข้าถึงได้จากหน้ามุมมองของคุณ

โครงการ> Repository / IdentityRepository.cs

namespace Infrastructure.Repository
{
    public class IdentityRepository : IIdentityRepository
    {
        private readonly ISystemSettings _systemSettings;
        private readonly ISessionDataManager _sessionDataManager;

        public IdentityRepository(
            ISystemSettings systemSettings
            )
        {
            _systemSettings = systemSettings;
        }

        public string GetCurrentUserName()
        {
            return HttpContext.Current.User.Identity.Name;
        }
    }
}

โครงการ> App_Code / IdentityRepositoryViewFunctions.cshtml:

@using System.Web.Mvc
@using Infrastructure.Repository
@functions
{
    public static IIdentityRepository IdentityRepositoryInstance
    {
        get { return DependencyResolver.Current.GetService<IIdentityRepository>(); }
    }

    public static string GetCurrentUserName
    {
        get
        {
            var identityRepo = IdentityRepositoryInstance;
            if (identityRepo != null)
            {
                return identityRepo.GetCurrentUserName();
            }
            return null;
        }
    }
}

โครงการ> มุมมอง / แชร์ / _Layout.cshtml (หรือไฟล์. cshtml อื่น ๆ )

<div>
    @IdentityRepositoryViewFunctions.GetCurrentUserName
</div>

-1

แทนที่จะทำตามขั้นตอนนี้คุณสามารถใช้วิธีอื่นที่รวดเร็วเช่นกัน

สร้างมุมมองบางส่วนใหม่ในไดเรกทอรีที่ใช้ร่วมกันและเรียกมุมมองบางส่วนของคุณในเค้าโครงของคุณว่า

@Html.Partial("MyPartialView")

ในมุมมองบางส่วนของคุณคุณสามารถเรียกฐานข้อมูลของคุณและดำเนินการในสิ่งที่คุณต้องการทำ

@{
    IEnumerable<HOXAT.Models.CourseCategory> categories = new HOXAT.Models.HOXATEntities().CourseCategories;
}

<div>
//do what ever here
</div>

สมมติว่าคุณได้เพิ่มฐานข้อมูล Entity Framework ของคุณ


1
Downcasting เนื่องจากไม่ควรเป็นความรับผิดชอบของ View ในการรับ Model ของตนเอง
Oxonhammer

-1

มันเหลือเชื่อมากที่ไม่มีใครพูดถึงเรื่องนี้ การส่ง viewmodel ผ่านตัวควบคุมฐานเป็นเรื่องที่ยุ่งยาก เราใช้การอ้างสิทธิ์ของผู้ใช้เพื่อส่งข้อมูลไปยังหน้าเค้าโครง (สำหรับการแสดงข้อมูลผู้ใช้บนแถบนำทางเป็นต้น) มีข้อดีอีกอย่างหนึ่ง ข้อมูลจะถูกจัดเก็บผ่านคุกกี้ดังนั้นจึงไม่จำเป็นต้องดึงข้อมูลในแต่ละคำขอผ่านทางบางส่วน เพียงแค่ทำ googling "asp net identity Claim"


@CodeSmith อะไรนะ? ฉันกำลังหาวิธีแก้ปัญหา
makore

ฉันไม่ดีคิดว่านี่เป็นคำถาม แต่ดูว่ามันเป็นคำตอบตอนนี้ลบแล้ว
CodeSmith

หากนี่เป็นความพยายามในการตอบคำถามควรมีการชี้แจง ความเห็นเป็นที่ยอมรับ แต่ไม่ควรครอบงำคำตอบทั้งหมด นอกจากนี้เคล็ดลับในการทำอะไรกับ Google ก็ไม่ได้เป็นคำตอบที่ถูกต้อง
tripleee

-6

คุณสามารถใช้สิ่งนี้:

 @{ 
    ApplicationDbContext db = new ApplicationDbContext();
    IEnumerable<YourModel> bd_recent = db.YourModel.Where(m => m.Pin == true).OrderByDescending(m=>m.ID).Select(m => m);
}
<div class="col-md-12">
    <div class="panel panel-default">
        <div class="panel-body">
            <div class="baner1">
                <h3 class="bb-hred">Recent Posts</h3>
                @foreach(var item in bd_recent)
                {
                    <a href="/BaiDangs/BaiDangChiTiet/@item.ID">@item.Name</a>
                }
            </div>
        </div>
    </div>
</div>

7
การเชื่อมต่อกับฐานข้อมูลในมุมมองเป็นความคิดที่แย่มาก
1_bug
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.