I have a login system that record every time that users login on my website.
I'm using MVC template that gives me a login system.
I had my 'Activity log' working. On the view it shows the data I had on Model, so, I needed to display more data, that were on Users table.
To do that I followed the 3rd answer of this question.
Because I created the model that contains info about others 2 Models (user and activity), the system have created this table on database too (it's empty).
There are some bad pratices here? It's normal, in this situation, the system create the table?
Another question, I do querys on controller, I'm doing it right?
ActivityLogViewModels.cs
using System;
using System.Data.Entity;
namespace AccountSystem.Models
{
public class ActivityLogModels
{
public int ID { get; set; }
public string userID { get; set; }
public DateTime Acess { get; set; }
}
public class ActivityLogViewData
{
public int ID { get; set; }
public int accessID { get; set; }
public string userID { get; set; }
public DateTime Acess { get; set; }
public string UserName { get; set; }
public string Email { get; set; }
}
}
ActivityLogController.cs
using System;
...
namespace AccountSystem.Controllers
{
public class ActivityLogController : Controller
{
private ApplicationDbContext db = new ApplicationDbContext();
// GET: ActivityLog
public ActionResult Index()
{
IEnumerable<ActivityLogViewData> model = null;
List<ActivityLogViewData> verify = new List<ActivityLogViewData>();
SqlConnection connection = new SqlConnection("xxx");
connection.Open();
SqlCommand command = new SqlCommand("", connection);
command.CommandText = "select dbo.AspNetUsers.Email,dbo.AspNetUsers.UserName,dbo.ActivityLogModels.ID,dbo.ActivityLogModels.Acess,dbo.AspNetUsers.Id from dbo.ActivityLogModels inner join dbo.AspNetUsers on dbo.ActivityLogModels.userID=dbo.AspNetUsers.Id";
SqlDataReader dr = command.ExecuteReader();
while (dr.Read())
{
verify.Add(new ActivityLogViewData { Email = dr[0].ToString(), UserName = dr[1].ToString(), accessID = Convert.ToInt32(dr[2].ToString()), Acess = Convert.ToDateTime(dr[3].ToString()), userID = dr[4].ToString()});
}
connection.Close();
//return View(db.ActivityLog.ToList());
return View(verify);
}
The Page (Create, Edit, Details and delete don't work after my changes)
Table empty
Related
I have the model that has multiple entities. For example I have an order that have CustomerId to foreign key. In the model I have some fields from the order and some fields from the customer entity. When I am saving new record I would like to save customer first, get newly generated identity value and put it to the order table. How to achieve that? I can do that by getting MAX(ID) from the customer table, however I am pretty sure that there is better way to handle that.
This is my controller method:
public ActionResult Create(OrderModels model, FormCollection form)
{
try
{
Customer customer = new Customer() { FirstName = model.FirstName, MiddleName = model.MiddleName, SecondName = model.SecondName, Email = model.Email, PhoneNbr = model.PhoneNbr };
int orderSource = Int32.Parse(form["OrderSourceList"]);
int paymentType = Int32.Parse(form["PaymentTypeList"]);
string warehouseGuid = form["Warehouses"];
ProductLogic productLogic = new ProductLogic();
Product product = productLogic.GetProductByArticle(model.Article);
DateTime now = DateTime.Now;
SalesOrder salesOrder = new SalesOrder()
{
OrderNbr = model.OrderNbr,
OrderSourceId = orderSource,
NpWarehouseRef = new Guid(warehouseGuid),
TTN = model.TTN,
OrderStatusId = 1,
PaymentTypeId = paymentType,
OrderDate = DateTime.Now
};
using (AccountingRepository repository = new AccountingRepository())
{
repository.AddOrUpdate<Customer>(customer);
repository.AddOrUpdate<SalesOrder>(salesOrder);
}
return RedirectToAction("Index");
}
catch (Exception e)
{
return View();
}
}
AccountingRepository has dispose method
public void Dispose()
{
if (_context != null)
{
_context.SaveChanges();
_context.Dispose();
}
}
My Order class:
using System;
using System.ComponentModel.DataAnnotations;
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations.Schema;
using System.Data.Entity.ModelConfiguration;
namespace Accounting.Entity
{
[Table("SalesOrder", Schema = "dbo")]
public class SalesOrder
{
[Key]
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public int? Id { get; set; }
...
public int? CustomerId { get; set; }
...
public virtual Customer Customer { get; set; }
internal class Config : EntityTypeConfiguration<SalesOrder>
{
public Config()
{
HasRequired(r => r.Customer)
.WithMany(r => r.SalesOrder)
.HasForeignKey(r => r.CustomerId);
}
}
}
}
My Customer class:
using System.ComponentModel.DataAnnotations;
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations.Schema;
using System.Data.Entity.ModelConfiguration;
namespace Accounting.Entity
{
[Table("Customer", Schema = "dbo")]
public class Customer
{
[Key]
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public int? Id { get; set; }
public string FirstName { get; set; }
public string MiddleName { get; set; }
public string SecondName { get; set; }
public string PhoneNbr { get; set; }
public string Email { get; set; }
public virtual HashSet<SalesOrder> SalesOrder { get; set; }
internal class Config : EntityTypeConfiguration<Customer>
{
public Config()
{
}
}
}
}
Two ways:
Save the customer entity, and Entity Framework will back fill the PK property with it's ID. You can then use this for the order entity.
db.Customers.Add(customer);
db.SaveChanges();
order.CustomerId = customer.Id; // has a value now
Just associate the customer with the order via a navigation property. When you save everything Entity Framework works out which relationships need to be saved first and then fills in the appropriate FK ids in the related entities.
order.Customer = customer;
db.Orders.Add(order);
db.SaveChanges(); // customer is saved first and the id is set for order.CustomerId automatically
I use Asp.Net MVC, Entity Framework. I have a form it looks like below.
Here, dropdownlist is filled from a table(types). Checkboxes is filled from another table(test). Tables are like below:
public class Types
{
public int TypesID{get;set;}
public string TestName { get; set; }
public string TestExplanation { get; set; }
public int TestTime { get; set; }
}
public class Tests
{
public int TestID{get;set;
public string Name { get; set; }
public string Code { get; set; }
}
public class Types_Tests
{
public int Types_TestsID{ get; set; }
public int TypesID { get; set; }
public int TestsID { get; set; }
public virtual Types Types { get; set; }
public virtual Tests Tests { get; set; }
}
Types_test table is relation table between Types and Tests. When I click Kaydet button, it shuld save type and checked tests. I made this operation using ViewBag, javascript and hdnvalue.I added checked checkboz values to a hdntext. I made saving process like below:
[HttpPost]
public ActionResult Index(string drpType, string hdntesttypes)
{
var TypeList = Types.GetAll();
ViewBag.TypesList = new SelectList(TypeList, "Id", "Name");
var testypeList = testTypes.GetAll();
ViewBag.TestTypesList = new SelectList(testypeList, "Id", "TestName");
GenericRepository<TestDisabledTypes> testDisabledRepository = new GenericRepository<TestDisabledTypes>(_context);
if (!string.IsNullOrEmpty(hdntesttypes))
{
string[] disabletypesArray = hdntesttypes.Split(',');
using (TransactionScope trns = new TransactionScope())
{
for (int i = 0; i < disabletypesArray.Length; i++)
{
Test_Types types = new Test_Types ();
types.TestTypesID = Convert.ToInt32(disabletypesArray[i]);
types.TypesID = Convert.ToInt32(drpType);
testDisabledRepository.Insert(types);
}
trns.Complete();
}
}
return View();
}
It wokrs. But I search better solution for this process. Can someone give me any idea?
Thanks.
If you don't need additional attributes for your entity class, you don't need create link table.
Just define the following class, and EF will generate the link table for you automatically.
public class Type
{
public int TypesID{get;set;}
public string TestName { get; set; }
public string TestExplanation { get; set; }
public int TestTime { get; set; }
public ICollection<Test> Tests { get; set; }
}
public class Test
{
public int TestID{get;set;
public string Name { get; set; }
public string Code { get; set; }
public ICollection<Type> Types {get;set;}
}
Well, in EntityFramework if you want to create a many to many relation object you need to create new object of "linking" entity. Unfortunately, it is not possible to add first object, add second object and say "Guys, you are in many to many relationships. Are you happy then?" :) You need to create relation object, set appropriate fields in it (I think these are ids of two objects itself) and add it to relation collection (entity) in your model. But before doing so you need to be sure that objects with data you are linking with are already exists in database. Otherwise you'll get an error
Also it's not necessary to create manually transaction because EF does it for you automatically each time you get/save your data
I've been trying to implement a custom version of the new Identity features in ASP.NET 4.5 (Microsoft.AspNet.Identity), using Visual Studio 2013. After many hours of playing around with this, I've simplified my code in an effort to get it running without errors. I've listed my code below. When doing a Local Registration, the database tables are created, but the CreateLocalUser method fails. I'm hoping that someone can help me identify the changes needed.
Models/MembershipModel.cs
using Microsoft.AspNet.Identity;
using Microsoft.AspNet.Identity.EntityFramework;
using System;
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
using System.ComponentModel.DataAnnotations.Schema;
using System.Data.Entity;
using System.Linq;
using System.Web;
namespace thePulse.web.Models
{
public class PulseUser : IUser
{
public PulseUser() { }
public PulseUser(string userName)
{
UserName = userName;
}
[Key]
public string Id { get; set; }
[Required]
[StringLength(20)]
public string UserName { get; set; }
[StringLength(100)]
public string Email { get; set; }
[Column(TypeName = "Date")]
public DateTime? BirthDate { get; set; }
[StringLength(1)]
public string Gender { get; set; }
}
public class PulseUserClaim : IUserClaim
{
public PulseUserClaim() { }
[Key]
public string Key { get; set; }
public string UserId { get; set; }
public string ClaimType { get; set; }
public string ClaimValue { get; set; }
}
public class PulseUserSecret : IUserSecret
{
public PulseUserSecret() { }
public PulseUserSecret(string userName, string secret)
{
UserName = userName;
Secret = secret;
}
[Key]
public string UserName { get; set; }
public string Secret { get; set; }
}
public class PulseUserLogin : IUserLogin
{
public PulseUserLogin() { }
public PulseUserLogin(string userId, string loginProvider, string providerKey)
{
LoginProvider = LoginProvider;
ProviderKey = providerKey;
UserId = userId;
}
[Key, Column(Order = 0)]
public string LoginProvider { get; set; }
[Key, Column(Order = 1)]
public string ProviderKey { get; set; }
public string UserId { get; set; }
}
public class PulseRole : IRole
{
public PulseRole() { }
public PulseRole(string roleId)
{
Id = roleId;
}
[Key]
public string Id { get; set; }
}
public class PulseUserRole : IUserRole
{
public PulseUserRole() { }
[Key, Column(Order = 0)]
public string RoleId { get; set; }
[Key, Column(Order = 1)]
public string UserId { get; set; }
}
public class PulseUserContext : IdentityStoreContext
{
public PulseUserContext(DbContext db) : base(db)
{
Users = new UserStore<PulseUser>(db);
Logins = new UserLoginStore<PulseUserLogin>(db);
Roles = new RoleStore<PulseRole, PulseUserRole>(db);
Secrets = new UserSecretStore<PulseUserSecret>(db);
UserClaims = new UserClaimStore<PulseUserClaim>(db);
}
}
public class PulseDbContext : IdentityDbContext<PulseUser, PulseUserClaim, PulseUserSecret, PulseUserLogin, PulseRole, PulseUserRole>
{
}
}
Changes to Controllers/AccountController.cs
public AccountController()
{
IdentityStore = new IdentityStoreManager(new PulseUserContext(new PulseDbContext()));
AuthenticationManager = new IdentityAuthenticationManager(IdentityStore);
}
//
// POST: /Account/Register
[HttpPost]
[AllowAnonymous]
[ValidateAntiForgeryToken]
public async Task<ActionResult> Register(RegisterViewModel model)
{
if (ModelState.IsValid)
{
try
{
// Create a profile, password, and link the local login before signing in the user
PulseUser user = new PulseUser(model.UserName);
if (await IdentityStore.CreateLocalUser(user, model.Password))
{
await AuthenticationManager.SignIn(HttpContext, user.Id, isPersistent: false);
return RedirectToAction("Index", "Home");
}
else
{
ModelState.AddModelError("", "Failed to register user name: " + model.UserName);
}
}
catch (IdentityException e)
{
ModelState.AddModelError("", e.Message);
}
}
// If we got this far, something failed, redisplay form
return View(model);
}
As I said above, this implementation fails when the CreateLocalUser method fails (Microsoft.AspNet.Identity.EntityFramework). I cannot figure out why.
The issue here is that IdentityStoreManager has strong dependency on the default implementation of identity EF models. For example, the CreateLocalUser method will create UserSecret and UserLogin objects and save them to stores, which won't work if the store is not using the default model type. So if you customize the model type, it won't work smoothly with IdentityStoreManager.
Since you only customize the IUser model, I simplified the code to inherit custom user from default identity user and reuse other models from identity EF models.
using Microsoft.AspNet.Identity;
using Microsoft.AspNet.Identity.EntityFramework;
using System;
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
using System.ComponentModel.DataAnnotations.Schema;
using System.Data.Entity;
using System.Linq;
using System.Web;
namespace WebApplication11.Models
{
public class PulseUser : User
{
public PulseUser() { }
public PulseUser(string userName) : base(userName)
{
}
[StringLength(100)]
public string Email { get; set; }
[Column(TypeName = "Date")]
public DateTime? BirthDate { get; set; }
[StringLength(1)]
public string Gender { get; set; }
}
public class PulseUserContext : IdentityStoreContext
{
public PulseUserContext(DbContext db) : base(db)
{
this.Users = new UserStore<PulseUser>(this.DbContext);
}
}
public class PulseDbContext : IdentityDbContext<PulseUser, UserClaim, UserSecret, UserLogin, Role, UserRole>
{
}
}
The code above should work with preview version of Identity API.
The IdentityStoreManager API in upcoming release is already aware of this issue and changed all the non-EF dependency code into a base class so that you can customize it by inheriting from it. It should solve all the problems here. Thanks.
PulseUser.Id is defined as a string but doesn't appear to be set to a value. Were you meant to be using a GUID for the Id? If so, initialise it in the constructor.
public PulseUser() : this(String.Empty) { }
public PulseUser(string userName)
{
UserName = userName;
Id = Guid.NewGuid().ToString();
}
You will also want to perform a check that the user name doesn't already exist. Look at overriding DbEntityValidationResult in PulseDbContext. Do a new MVC project in VS2013 to see an example.
Since there are alot of changes on this when going to RTM, i have updated the SPA template that uses a WebApi controller for all the identity signin and such. Its a really cool template , if you havent seen it.
I put all my code here:
https://github.com/s093294/aspnet-identity-rtm/tree/master
(Do note, its only for inspiration. I only made it work and nothing more. Properly have a bug or two also).
I am just getting into MVC 4 and Entity Framework 5 and want to know if what I am doing is correct?
I have a UserObject and a JobObject, the jobObject has a reference to a User Object.
public class Job
{
public int id { get; set; }
public virtual MyUser User { get; set; }
public JobType JobType { get; set; }
}
When I want to create an instance of the Job I am passing in the query string a parameter UserID, but the Job only deals with an instance of MyUser.
Is the following the correct way to associate the user to the job?
[HttpPost]
public ActionResult Create(Job job, int userid)
{
if (ModelState.IsValid)
{
MyUser staffmember = db.MyUsers.Find(userid);
if (staffmember == null)
{
return View("StaffMemberNotFound");
}
job.User = staffmember;
db.Jobs.Add(job);
db.SaveChanges();
}
}
Or is there a better way to associate the user to the job?
Your way will work but I prefer to simply work with ids if possible.
What I would suggest is that you add a MyUserId property to your Job class (remember to update the database if you are using codefirst):
public class Job
{
public int id { get; set; }
[ForeignKey("User")]
public int MyUserId { get; set: }
public virtual MyUser User { get; set; }
public JobType JobType { get; set; }
}
Then simply populate the MyUserId. You can also change your check to simply check if the id exists as apposed to finding an object and letting EF map that to a class before returning it to you
[HttpPost]
public ActionResult Create(Job job, int userid)
{
if (ModelState.IsValid)
{
if (!db.MyUsers.Any(u => u.Id == userid)
{
return View("StaffMemberNotFound");
}
job.MyUserId = userid;
db.Jobs.Add(job);
db.SaveChanges();
}
}
EF will do the rest of the mapping for you when you next retrieve the record from the database.
Your approach works fine, the only small optmization you could make is not taking the "retrieval hit" of MyUser staffmember = db.MyUsers.Find(userid); since you already have the userid.
I am using ASP.NET MVC 4 and Entity Framework 5.0, and here is my code (different model objects, but same intent as what you are doing).
Note: I let EF generate my model classes by right-clicking on the Models folder and choosing Add->ADO.NET Entity Data Model in VS.NET 2012.
Store.Models.Product
namespace Store.Models
{
using System;
using System.Collections.Generic;
public partial class Product
{
public long Id { get; set; }
public string ProductName { get; set; }
public decimal Price { get; set; }
public int Quantity { get; set; }
public System.DateTime DateAdded { get; set; }
public Nullable<long> CategoryId { get; set; }
public virtual Category Category { get; set; }
}
}
Store.Models.Category
namespace Store.Models
{
using System;
using System.Collections.Generic;
public partial class Category
{
public Category()
{
this.Products = new HashSet<Product>();
}
public long Id { get; set; }
public string CategoryName { get; set; }
public System.DateTime DateAdded { get; set; }
public virtual ICollection<Product> Products { get; set; }
}
}
On my Create.cshtml page, I have the User select the CategoryId from the drop-down list. This Category Id is bound to Product.CategoryId. All I do in my method is this:
ProductController
public class ProductController : Controller
{
...
[HttpPost]
public ActionResult Create(Product product)
{
product.DateAdded = DateTime.Now;
if (dbContext != null)
{
dbContext.Products.Add(product);
dbContext.SaveChanges();
}
return RedirectToAction("Index");
}
...
}
I know there are a lot of posts on the subject but I cannot find one that helps me do what I want. I know that I will eventually be using Automapper but before I start playing with it, I want to learn how to do things manually. I want to create a ViewModel, populate it with values from my entities by way of a repository and send it to my View. As simple as this sounds, I am stuggling to get it done. I'm using MVC 3, EF 4.3, Database First. I have auto-generated my classes. I'm posting the relevant entities (abbreviated/renamed for this post) and classes, here is what I have so far:
Aggregate Entity: Shipping Header
using System;
using System.Collections.Generic;
namespace My.Models
{
public partial class ShippingHdr
{
public ShippingHdr()
{
this.ShippingLI = new HashSet<ShippingLI>();
}
public int ID { get; set; }
public int ShipToSiteID { get; set; }
public Nullable<System.DateTime> DateShipped { get; set; }
public Nullable<System.DateTime> EstDeliveryDate { get; set; }
public string FromSitePOC { get; set; }
public Nullable<int> ShipperID { get; set; }
public string TrackingNo { get; set; }
public string Comments { get; set;}
public virtual Shippers Shippers { get; set; }
public virtual ICollection<ShippingLI> ShippingLI { get; set; }
}
}
Here is my ViewModel
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
namespace My.Models.ViewModels
{
public class ShippingHeaderSummaryVM
{
public int ID { get; set; }
public string Site { get; set; }
public Nullable<System.DateTime> DateShipped { get; set; }
public Nullable<System.DateTime> EstDeliveryDate { get; set; }
public string TrackingNo { get; set; }
public string HeaderComments { get; set; }
public string Shipper { get; set; }
public int NumOrders { get; set; }
public string Site { get; set; }
}
}
Here is a query I got to return the items I want to use to populate my Viewmodel with. I believe the best place for this is in a Repository. I verified it returns the data I want using LinqPad (hence the missing reference to my dbContxt). I just don't know how to get the values from the query to the ViewModel:
var shipments = from h in c.ShippingHdrs
where (h.ShippingLI.Count > 1)
join
e in c.vHr_Employees on h.CreatedBy equals e.ID
join
s in c.Shippers on h.ShipperID equals s.ShipperID
join
r in vAaiomsSites on h.ShipToSiteID equals r.SiteID
select new
{
h.ID,
r.Site,
h.EstDeliveryDate,
h.DateShipped,
h.TrackingNumber,
h.HeaderComments,
e.LastName,
h.ShippingLI.Count,
s.Shipper
};
So what I want to do, again without using Automapper, is to populate the ViewModel with all of the rows from the ShippingHdr entity and pass it to my view.
Here are the filelds that need to be mapped:
ShippingHeaderSummaryVM mapped from shipments
ID = h.ID
Site = r.Site
DateShipped = h.DateShipped
EstDeliveryDate = h.EstDeliveryDate
TrackingNo = h.TrackingNumber
FromSitePOC = e.LastName
NumOrders = h.ShippingLI.Count
Shipper = s.Shipper
HeaderComments = h.HeaderComments
I am stuck here.
How do I populate the ViewModel from the query?
How then do I then call that action from my controller?
I hope I have given enough information, any help would be appreciated.
In order to populate a list of shipments based on your view model object you would need to create a mapping method to map from your collection of shipments from your database to a collection of shipments based on your view model:
var model = new List<ShippingHeaderSummaryVM>();
foreach(var h in shipments)
{
var viewModel = new ShippingHeaderSummaryVM
{
ID = h.ID
Site = r.Site
DateShipped = h.DateShipped
EstDeliveryDate = h.EstDeliveryDate
TrackingNo = h.TrackingNumber
FromSitePOC = e.LastName
NumOrders = h.ShippingLI.Count
Shipper = s.Shipper
HeaderComments = h.HeaderComments
}
model.Add(viewModel);
}
return model;
As a side note, this becomes a one liner after you have AutoMapper up and running:
var model = Mapper.Map<IEnumerable<ShippingHdr>, IEnumerable<ShippingHeaderSummaryVM>>(shipments);
While, learning how to do things manually is great. Manually mapping models doesn't really benefit you in any way or form. Go with AutoMapper.
You can also use Linq to do something like this...
shipments.Select(h => new ShippingHeaderSummaryVM(){
ID = h.ID,
Site = r.Site,
DateShipped = h.DateShipped,
EstDeliveryDate = h.EstDeliveryDate,
TrackingNo = h.TrackingNumber,
FromSitePOC = e.LastName,
NumOrders = h.ShippingLI.Count,
Shipper = s.Shipper,
HeaderComments = h.HeaderComments
});
Note that while mapping view models is great for passing to a view, always do it manually when reading from a view model to update your database.
Edit: Thanks for the typo correction:-)