3
\$\begingroup\$

I have a DB with a master table called "facturas" and another detail table "facturas_detalle." I would like to insert to them, so this is a "Database-First".

I need some guidance or advise of best practice here. I've read some examples that are outdated versions of Entity Framework and ASP with no MVC approach.

**facturas**

folio PK
fecha
almacen FK
cliente FK
plaza  FK
usuario FK
id_factura
----------
**facturas_detalle**

folio FK
articulo FK 
cantidad
precio

I already created a model (using the assistant) from the database and I have stored procedures to write to them. Using the Store Procedure Mapping from VS 2015 I mapped the entities to those store procedures for Insert. I didn't marked pluralize option because some other tables names are uppercase and aren't nouns.

I also generated a controller and views for the "facturas" table, and it creates successfully entries in DB. How should I edit the view and controller to Insert one or more facturas_detalle in the same page? Is this approach correct?

facturas_pruebaController.cs:

using System;
using System.Collections.Generic;
using System.Data;
using System.Data.Entity;
using System.Linq;
using System.Net;
using System.Web;
using System.Web.Mvc;
using Abaco_ASP.Models;

namespace Abaco_ASP.Controllers
{
    public class facturas_pruebaController : Controller
    {
        private PAQAEntities db = new PAQAEntities();


        // GET: facturas_prueba/Create
        public ActionResult Create()
        {
            ViewBag.almacen = new SelectList(db.CATALMA, "COD_ALM", "NOM_ALM");
            ViewBag.cliente = new SelectList(db.CATCTES, "COD_CTE", "NOM_CTE");
            ViewBag.usuario = new SelectList(db.FACPARU, "cod_usu", "cod_Alm");
            ViewBag.plaza = new SelectList(db.PLAZAS, "PLAZA", "LAST_COD_CTE");
            return View();
        }

        // POST: facturas_prueba/Create
        // To protect from overposting attacks, please enable the specific properties you want to bind to, for 
        // more details see http://go.microsoft.com/fwlink/?LinkId=317598.
        [HttpPost]
        [ValidateAntiForgeryToken]
        public ActionResult Create([Bind(Include = "folio,fecha,almacen,cliente,plaza,usuario,id_factura")] facturas_prueba facturas_prueba)
        {
            if (ModelState.IsValid)
            {
                db.facturas_prueba.Add(facturas_prueba);
                db.SaveChanges();
                return RedirectToAction("Index");
            }

            ViewBag.almacen = new SelectList(db.CATALMA, "COD_ALM", "NOM_ALM", facturas_prueba.almacen);
            ViewBag.cliente = new SelectList(db.CATCTES, "COD_CTE", "NOM_CTE", facturas_prueba.cliente);
            ViewBag.usuario = new SelectList(db.FACPARU, "cod_usu", "cod_Alm", facturas_prueba.usuario);
            ViewBag.plaza = new SelectList(db.PLAZAS, "PLAZA", "LAST_COD_CTE", facturas_prueba.plaza);
            return View(facturas_prueba);
        }

        protected override void Dispose(bool disposing)
        {
            if (disposing)
            {
                db.Dispose();
            }
            base.Dispose(disposing);
        }
    }
}

Create.cshtml:

@model Abaco_ASP.Models.facturas_prueba

@{
    ViewBag.Title = "Create";
}

<h2>Create</h2>


@using (Html.BeginForm()) 
{
    @Html.AntiForgeryToken()

    <div class="form-horizontal">
        <h4>facturas_prueba</h4>
        <hr />
        @Html.ValidationSummary(true, "", new { @class = "text-danger" })
        <div class="form-group">
            @Html.LabelFor(model => model.folio, htmlAttributes: new { @class = "control-label col-md-2" })
            <div class="col-md-10">
                @Html.EditorFor(model => model.folio, new { htmlAttributes = new { @class = "form-control" } })
                @Html.ValidationMessageFor(model => model.folio, "", new { @class = "text-danger" })
            </div>
        </div>

        <div class="form-group">
            @Html.LabelFor(model => model.fecha, htmlAttributes: new { @class = "control-label col-md-2" })
            <div class="col-md-10">
                @Html.EditorFor(model => model.fecha, new { htmlAttributes = new { @class = "form-control" } })
                @Html.ValidationMessageFor(model => model.fecha, "", new { @class = "text-danger" })
            </div>
        </div>

        <div class="form-group">
            @Html.LabelFor(model => model.almacen, "almacen", htmlAttributes: new { @class = "control-label col-md-2" })
            <div class="col-md-10">
                @Html.DropDownList("almacen", null, htmlAttributes: new { @class = "form-control" })
                @Html.ValidationMessageFor(model => model.almacen, "", new { @class = "text-danger" })
            </div>
        </div>

        <div class="form-group">
            @Html.LabelFor(model => model.cliente, "cliente", htmlAttributes: new { @class = "control-label col-md-2" })
            <div class="col-md-10">
                @Html.DropDownList("cliente", null, htmlAttributes: new { @class = "form-control" })
                @Html.ValidationMessageFor(model => model.cliente, "", new { @class = "text-danger" })
            </div>
        </div>

        <div class="form-group">
            @Html.LabelFor(model => model.plaza, "plaza", htmlAttributes: new { @class = "control-label col-md-2" })
            <div class="col-md-10">
                @Html.DropDownList("plaza", null, htmlAttributes: new { @class = "form-control" })
                @Html.ValidationMessageFor(model => model.plaza, "", new { @class = "text-danger" })
            </div>
        </div>

        <div class="form-group">
            @Html.LabelFor(model => model.usuario, "usuario", htmlAttributes: new { @class = "control-label col-md-2" })
            <div class="col-md-10">
                @Html.DropDownList("usuario", null, htmlAttributes: new { @class = "form-control" })
                @Html.ValidationMessageFor(model => model.usuario, "", new { @class = "text-danger" })
            </div>
        </div>

        <div class="form-group">
            <div class="col-md-offset-2 col-md-10">
                <input type="submit" value="Create" class="btn btn-default" />
            </div>
        </div>
    </div>
}

<div>
    @Html.ActionLink("Back to List", "Index")
</div>

@section Scripts {
    @Scripts.Render("~/bundles/jqueryval")
}
\$\endgroup\$
3
\$\begingroup\$

Be wary of ViewBag. It's convenient, but if you misspell something, you'll never know it. No compiler error or warning. No runtime exception. Nothing. The object just never gets passed to the view and no element gets rendered. I just recently ran into a bug caused by this in one of my own applications, so I know just how difficult it can be to even spot that something was ever wrong to begin with. I only spotted it as a fluke.

It's preferable to leverage strong typing of models in a view. However. To the best of my knowledge, you can only bind a single model to a view, so you'll need to use partial views to make the magic happen.

There is more that can be said about the code though.

  • I like that you're verifying model state before posting.
  • I don't care for the Create action that does nothing but return a view. I would expect it to be the Index action.
  • I would be inclined to keep your db context constrained to a using block instead of overriding the controller's Dispose method.
  • I would also be inclined to introduce a factory class for the db context and a constructor that takes an instance of that factory, or rather it's interface, in as an argument. Moving to dependency injection would allow you to unit test the controller (and you really should be unit testing).
\$\endgroup\$
2
\$\begingroup\$

I see that you have started with the boilerplate provided by the .net MVC template which is a good thing to start with but definitely not a way to stick with (you'll experience that later as the complexity starts to grow).

Also, I agree with the user RubberDuck in most of the things except for the binding multiple models to the view. For something like that the best thing would be to use the ViewModel.

As soon as you start using it you'll see how things can be simpler.

NOTE: Watch out for the Bind attribute as it can be a source of problems if you unintentionally leave out something you wanted to have included otherwise.

\$\endgroup\$
1
  • \$\begingroup\$ Can you elaborate on the view model? An example maybe? I meant to tell OP to use binding over the ViewBag, but perhaps I could have been clearer on that point. \$\endgroup\$ – RubberDuck Oct 19 '15 at 1:04
2
\$\begingroup\$

Some quick remarks.

Please follow the C# guidelines:


Be consistent.

I count at least three different naming methods here, and none seem correct to me:

ViewBag.almacen = new SelectList(db.CATALMA, "COD_ALM", "NOM_ALM");
ViewBag.cliente = new SelectList(db.CATCTES, "COD_CTE", "NOM_CTE");
ViewBag.usuario = new SelectList(db.FACPARU, "cod_usu", "cod_Alm");
ViewBag.plaza = new SelectList(db.PLAZAS, "PLAZA", "LAST_COD_CTE");

I must admit to not being a fan of using non-English to name variables etc. Code is meant to be "readable" and mixing two languages makes things a lot harder. I know some basic Spanish so I know what usuario and cliente and plaza means, but CATALMA, facturas_prueba and fecha are already harder.

\$\endgroup\$
1
  • \$\begingroup\$ It is difficult to stick to naming conventions when you're working with a legacy database, so I can't change their names so easily. At least, I could change those with underscores because they are provisional names. \$\endgroup\$ – Mr_LinDowsMac Oct 19 '15 at 14:47

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.