Vous êtes sur la page 1sur 13

Construccion de proyecto web y mobil

1. Crear una nueva solución en blanco, con nombre Shop. Con esto la tenemos el lienzo
2. Crear en la solución un proyecto .Net Standart; lo llamamos con el nombre del proyecto
Shop.Common. con esto ya tenemos:

3. Ahora continuando con las partes del frontend y Backend, creamos carpetas para cada
uno. (Frontend) y (Backend).
4. Dentro del proyecto Backend creamos un proyecto ASP.NET Core Web Application, con el
nombre Shop.Web de forma model view controller habilitando el HTTPS, y seleccionando
el .NET Core. Con esto ya tendremos

5. Dentro del proyecto Frontend creamos dos subcarpetas (Form) y (Classic)


6. Dentro de la subcarpeta Forms creamos nuevo proyecto Mobile App (Xamarin.Forms) con
el nombre Shop.UIForms en blanco para Android y para IOS.
7. El visual me deja por fuera dos proyectos: el Shop.UIForms y el Shop.UIForms.IOS; los
movemos a la subcarpeta Forms. Con esto ya tendremos
8. En la carpeta Classic creamos un nuevo proyecto Class library (.NET Standart) con el
nombre Shop.UIClassic
9. Dentro de la misma carpeta creamos un nuevo proyecto Android App (Xamarin) con el
nombre Shop.UIClassic.Android en blanco
10. Dentro de la misma carpeta creamos un nuevo proyecto IOS App (Xamarin) con el nombre
Shop.UIClassic.IOS en blanco, para universal en versión 12.1. Asi tenemos

Hasta tener esta estructura

Creación de la base de datos


1. Dentro del proyecto Shop.Web crear una carpeta que se llame (Data).
2. Dentro de Data Crear una carpeta que se llame (Entities).
3. Dentro de Entities crear una nueva clase que se llame Products. En esta clase es donde
creamos la base de datos
4. Es recomendable pasar los using dentro de manespace.
5. Explicación del código:

public class Product


{
public int Id { get; set; } Commented [A1]: Id es la primary key del product y
por defecto el Framework lo hace auto incrementable
[MaxLength(50, ErrorMessage = “Mensaje que quiere que salga”)]
[Required] Commented [A2]: Exige un tamaño
public string Name { get; set; }
Commented [A3]: Al poner esto el campo se vuelve
[DisplayFormat(DataFormatString = "{0:C2}", ApplyFormatInEditMode = false)] obligatorio para la BD y para la API
public decimal Price { get; set; }
Commented [A4]: DisplayFormat da formato el
[Display(Name = "Image")] campo, {0} formato de moneda que tenga el equipo
configurado, {c2} separador de miles poniéndole dos
public string ImageUrl { get; set; }
decimales

[Display(Name = "Last Purchase")]


Commented [A5]: para que el usuario lo vea como
public DateTime? LastPurchase { get; set; } image y el programador como imageURL

[Display(Name = "Last Sale")]


public DateTime? LastSale { get; set; } Commented [A6]: El isgno de interrogacion es para
volver nulable el campo en la table de la base de
[Display(Name = "Is Availabe?")] datos.
public bool IsAvailabe { get; set; }

[DisplayFormat(DataFormatString = "{0:N2}", ApplyFormatInEditMode = false)]


public double Stock { get; set; } Commented [A7]: cuanta catidad tengo en
} inbentario de ese product {0:N2} pone separadores
de miles y marca los decimales
6. Dentro de la carpeta Data crear una clase que se llame DataContext. Es la clase
que marca la conexión con la base de datos.
7. Explicacion DataContext

using Common.Models;
using Microsoft.EntityFrameworkCore;

public class DataContext : DbContext { Commented [A8]: Hereda de la clase


public DbSet<Product> Products { get; set; } EntityFrameworkCore

public DataContext(DbContextOptions<DataContext> options) : base(options) Commented [A9]: crear la propiedades que estan
{ en la clase Product y le doy el numbre en singular de
} Products y ya los datos no se los paso como si
fueran tablas sino como una colección de objetos
} por la propiedad Products

8. Ahora en la AppSetting.Json definimos la cadena de conexión a la base de datos

{
"Logging": {
"LogLevel": {
"Default": "Warning"
}
},
"AllowedHosts": "*",
"ConnectionStrings": {
"DefaultConnection":
"Server=(localdb)\\ProjectsV13;Database=Shop;Trusted_Connection=True;Mult
ipleActiveResultSets=true"
}
}

9. Configuramos la inyeccion de dependecias de la base de datos en el proyecto


Strarup.cs y en la configuración del proyecto antes de los cookies agregamos la
conexión con
services.AddDbContext<DataContext>(cfg =>
{
cfg.UseSqlServer(this.Configuration.GetConnectionString("DefaultConnection"));
});
DefaultConnection es el nombre con el que definio en nombre de la conexion en el
archive json donde esta la conexion de la base de datos

10. Para crear o instanciar un nuevo servidor DB lo creamos por comandos del
sistema asi para en este caso “ProjectsV13”

11. Para crear la base de datos Shop en este servidor lo hacemos por comandos del
sistema
Asi ya tenemos la base de datos creada con todas las propiedades.

12. En la carpeta Controller creamos un nuevo controlador (Controlador de MCV


con vistas que usan Entities Framework). Clase de modelo: product. Clase de
contexto: DataContext.
Con esto ya creamos el ProductController junto con las vistas Create Delete
Details Edit, update. Que están dentro de la carpeta View.

13. En el archivo _Layout adicionamos un nuevo menú Product en el var.


<li><a asp-area="" asp-controller="Products" asp-
action="Index">Product</a></li>

Nota “Si se haceb cambioi a las entidades de la clase a la base de


datos se debe actualizar la base de datos con:
Dotnet ef migrations add ModifyProducts
Dotnet ef database update”.
14. Seed DB => el alimentador de la base de datos.
Lo que hace esta clase es añadir datos a la base de datos en todo momento que la
base de datos sea eliminada para no tener que crear datos manualmente cada que
se elimine la BD. También sirve para data generical como por ejemplo todas las
ciudades de Colombia; que cada que la BD se elimina esta clase me adicione
nuevamente todas las ciudades.
En la carpeta Data de la carpeta BackEnd Adicionamos una nueva clase que la
llamamos SeedDB

using System;
using System.Linq;
using System.Threading.Tasks;
using Common.Models;

public class SeedDb


{
private readonly DataContext context;
private Random random;

public SeedDb(DataContext context)


{
this.context = context;
this.random = new Random();
}

public async Task SeedAsync()


{
await this.context.Database.EnsureCreatedAsync();
if (!this.context.Products.Any())
{
this.AddProduct("First Product");
this.AddProduct("Second Product");
this.AddProduct("Third Product");
await this.context.SaveChangesAsync();
}
}

private void AddProduct(string name)


{
this.context.Products.Add(new Product
{
Name = name,
Price = this.random.Next(100),
IsAvailabe = true,
Stock = this.random.Next(100)
});
}
}

Para que los datos que ponemos en la clase Seed sean ingresadas a la base de datos
debemos cambiar el programa para que esta clase sea tenida en cuenta en el arranque.
Esto lo hacemos modificando la clase Program con este código.

15. Modify the Program class by:

using Data;
using Microsoft.AspNetCore;
using Microsoft.AspNetCore.Hosting;
using Microsoft.Extensions.DependencyInjection;

public class Program


{
public static void Main(string[] args)
{
var host = CreateWebHostBuilder(args).Build();
RunSeeding(host);
host.Run();
}

private static void RunSeeding(IWebHost host)


{
var scopeFactory = host.Services.GetService<IServiceScopeFactory>();
using (var scope = scopeFactory.CreateScope())
{
var seeder = scope.ServiceProvider.GetService<SeedDb>();
seeder.SeedAsync().Wait(); Commented [A10]: Aqui le decimos al programa que use
} la clase seed
}

public static IWebHostBuilder CreateWebHostBuilder(string[] args) =>


WebHost.CreateDefaultBuilder(args)
.UseStartup<Startup>();
}

16. Lo añadimos a el startup Services..AddTransient<SeedDb>();

17. Implement the pattern repository


Cambiamos los controladores para que directamente no acceda a la
base de datos por medio del datacontext, sino que los controladores
acceda a la clase que llamaremos repositorio y luego el repositorio es el
que va acceder a la base de datos, para que en determinado momento
yo pueda engañar los controladores y cambiar las conexiones a las
bases de datos.

Agregamos una nueva clase en la carpeta Data con el nombre


Repository.

using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Common.Models;

public class Repository


{
private readonly DataContext context; Commented [A11]: Inicializa el context del DataContext

public Repository(DataContext context)


{
this.context = context;
} Commented [A12]: Contructor

public IEnumerable<Product> GetProducts() Commented [A13]: Devuelve una lista de productos no


{ instanciada
return this.context.Products.OrderBy(p => p.Name); Commented [A14]: Context: conexxion a la DB
} Products: Tabla
OrderBy: devuelvalos ordenados.
public Product GetProduct(int id) P => p.Name: exprecion linkq para los nombres
{ Commented [A15]: Retorna un objeto de la clase Product
return this.context.Products.Find(id); donde le pasamos por parameto un id
}

public void AddProduct(Product product) Commented [A16]: Adicionamos un Nuevo producto


{
this.context.Products.Add(product);
}
public void UpdateProduct(Product product) Commented [A17]: Actualizamos un producto
{
this.context.Update(product);
}

public void RemoveProduct(Product product) Commented [A18]: Elimina un product de este producto
{
this.context.Products.Remove(product);
}

public async Task<bool> SaveAllAsync() Commented [A19]: Los metodos anteriores dejan la
{ transaccion pendiente hasta que sea ejecutado este metodo
return await this.context.SaveChangesAsync() > 0; que guarda los cambios en la base de datos.
} Este método es asíncrono por eso tiene el async
Commented [A20]: Espera a que los grave los cambion en
public bool ProductExists(int id) la base de datos
{ Commented [A21]: Devuelve verdadero si existe o falso si
return this.context.Products.Any(p => p.Id == id); ep product no existe
}
}

18. Para crear la interfaz del repositorio le damos clic derecho en la clase (Acciones
rápidas y refactorizables) clic en (Extraer interfaz)

19. Cambiamos el código del ProductsController para que acceda al IRepository

using Data;
using Data.Entities;
using Microsoft.AspNetCore.Mvc;
using Microsoft.EntityFrameworkCore;
using System.Threading.Tasks;

public class ProductsController : Controller


{
private readonly IRepository repository;

public ProductsController(IRepository repository)


{
this.repository = repository;
}

public IActionResult Index()


{
return View(this.repository.GetProducts());
}

public IActionResult Details(int? id)


{
if (id == null)
{
return NotFound();
}

var product = this.repository.GetProduct(id.Value);


if (product == null)
{
return NotFound();
}

return View(product);
}

public IActionResult Create()


{
return View();
}

[HttpPost]
[ValidateAntiForgeryToken]
public async Task<IActionResult> Create(Product product)
{
if (ModelState.IsValid)
{
this.repository.AddProduct(product);
await this.repository.SaveAllAsync();
return RedirectToAction(nameof(Index));
}
return View(product);
}

public IActionResult Edit(int? id)


{
if (id == null)
{
return NotFound();
}

var product = this.repository.GetProduct(id.Value);


if (product == null)
{
return NotFound();
}

return View(product);
}

[HttpPost]
[ValidateAntiForgeryToken]
public async Task<IActionResult> Edit(Product product)
{
if (ModelState.IsValid)
{
try
{
this.repository.UpdateProduct(product);
await this.repository.SaveAllAsync();
}
catch (DbUpdateConcurrencyException)
{
if (!this.repository.ProductExists(product.Id))
{
return NotFound();
}
else
{
throw;
}
}
return RedirectToAction(nameof(Index));
}
return View(product);
}

public IActionResult Delete(int? id)


{
if (id == null)
{
return NotFound();
}

var product = this.repository.GetProduct(id.Value);


if (product == null)
{
return NotFound();
}

return View(product);
}

[HttpPost, ActionName("Delete")]
[ValidateAntiForgeryToken]
public async Task<IActionResult> DeleteConfirmed(int id)
{
var product = this.repository.GetProduct(id);
this.repository.RemoveProduct(product);
await this.repository.SaveAllAsync();
return RedirectToAction(nameof(Index));
}
}
20. Configurar la inyección del repositorio y para el SeedDb en el startup
services.AddTransient<SeedDb>(); Commented [A22]: El AddTransident tiene un ciclo de
services.AddScoped<IRepository, Repository>(); vida mas corto; se usa y se destruye.
Commented [A23]: AddScoped La inyeccion queda
21. Add User Identities. permanente durante toda la ejecucion para que sea
Creamos una nueva clase en la carpeta Data/Entities con el nombre utilizada las veces que sea necesaria
User.

Esta clase hereda de la clase IdentityUser. La clase IdentityUser son los


usuarios del sistema
using Microsoft.AspNetCore.Identity;

public class User : IdentityUser


{
public string FirstName { get; set; }

public string LastName { get; set; }


}

22. Modificamos la clase DataContext porque ya no va a heredar de ContextDb


sino de IdentityDbContext<User>
using Entities;
using Microsoft.AspNetCore.Identity.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore;

public class DataContext : IdentityDbContext<User> Commented [A24]: Añadimos la clase User para que lo
{ tome en cuenta junto con todas la tablas de seguridad del
public DbSet<Product> Products { get; set; } sistema

public DataContext(DbContextOptions<DataContext> options) :base(options)


{
}
}

23. Para crear una relación de uno a muchos de la clase Product a la clase
User, en la clase Product adicionamos

public User User { get; set; }

1. Drop the database and add the new migrations with those commands:

dotnet ef database drop


dotnet ef migrations add Users
dotnet ef database update

En la clase SeedDb sebemos decirle que herede también


public SeedDb(DataContext context, UserManager<User> userManager)
{

var user = await this.userManager.FindByEmailAsync("jzuluaga55@gmail.com");


if (user == null)
{
user = new User
{
FirstName = "Juan",
LastName = "Zuluaga",
Email = "jzuluaga55@gmail.com",
UserName = "jzuluaga55@gmail.com"
};

var result = await this.userManager.CreateAsync(user, "123456");


if (result != IdentityResult.Success)
{
throw new InvalidOperationException("Could not create the user in
seeder");
}
}

if (!this.context.Products.Any())
{
this.AddProduct("First Product", user);
this.AddProduct("Second Product", user);
this.AddProduct("Third Product", user);
await this.context.SaveChangesAsync();
}
}

private void AddProduct(string name, User user)


{
this.context.Products.Add(new Product
{
Name = name,
Price = this.random.Next(100),
IsAvailabe = true,
Stock = this.random.Next(100),
User = user
});
}
}

24. En el startup confuguramos y adicionamos


25. services.AddIdentity<User, IdentityRole>(cfg =>
26. {
27. cfg.User.RequireUniqueEmail = true;
28. cfg.Password.RequireDigit = false;
29. cfg.Password.RequiredUniqueChars = 0;
30. cfg.Password.RequireLowercase = false;
31. cfg.Password.RequireNonAlphanumeric = false;
32. cfg.Password.RequireUppercase = false;
33. })
34. .AddEntityFrameworkStores<DataContext>();

{
app.UseExceptionHandler("/Home/Error");
app.UseHsts();
}

app.UseHttpsRedirection();
app.UseStaticFiles();
app.UseAuthentication();
app.UseCookiePolicy();

app.UseMvc(routes =>
{

Vous aimerez peut-être aussi