I have a file field which is to be used to upload images and at the same time save the file path in the database. I followed this sample tutorial and adapted my code. Below is my model
using System;
using System.Web;
using System.Collections.Generic;
using System.ComponentModel;
using System.ComponentModel.DataAnnotations;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace BOL
{
public class TeamValidation
{
[Key]
public int teamID { get; set; }
[Required(ErrorMessage = "Please Enter Your Team Name")]
[Display(Name = "Team Name")]
public string teamName { get; set; }
[DisplayName("Team Picture")]
[Required(ErrorMessage = "Please Upload Team Picture")]
[ValidateFile]
public HttpPostedFileBase teamPicture { get; set; }
//public string teamPicture { get; set; }
[Required]
[Display(Name = "Description")]
public string description { get; set; }
//[AllowHtml]
[Required(ErrorMessage = "Please Enter Team Content")]
[Display(Name = "Content")]
[MaxLength(200)]
public string content { get; set; }
}
//Customized data annotation validator for uploading file
public class ValidateFileAttribute : ValidationAttribute
{
public override bool IsValid(object value)
{
int MaxContentLength = 1024 * 1024 * 3; //3 MB
string[] AllowedFileExtensions = new string[] { ".jpg", ".gif", ".png" };
var file = value as HttpPostedFileBase;
if (file == null)
return false;
else if (!AllowedFileExtensions.Contains(file.FileName.Substring(file.FileName.LastIndexOf('.'))))
{
ErrorMessage = "Please upload Your Photo of type: " + string.Join(", ", AllowedFileExtensions);
return false;
}
else if (file.ContentLength > MaxContentLength)
{
ErrorMessage = "Your Photo is too large, maximum allowed size is : " + (MaxContentLength / 1024).ToString() + "MB";
return false;
}
else
return true;
}
}
[MetadataType(typeof(TeamValidation))]
public partial class team
{
[Key]
public int teamID { get; set; }
public string teamName { get; set; }
public string teamPicture { get; set; }
public string description { get; set; }
public string content { get; set; }
}
}
and here is the controller
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Web;
using System.Web.Mvc;
using BOL;
namespace TeamBuildingCompetition.Areas.Admin.Controllers
{
public class TeamController : BaseAdminController
{
// GET: Admin/Team
public ActionResult Index()
{
return View();
}
public ActionResult teamView()
{
var teamList = objBs.teamBs.GetALL();
return View(teamList);
}
[HttpPost]
public ActionResult Create(team objTeam)
{
try
{
if (ModelState.IsValid)
{
var fileName = Path.GetFileName(objTeam.teamPicture.FileName);
var path = Path.Combine(Server.MapPath("~/Content/Upload"), fileName);
objTeam.teamPicture.SaveAs(path);
TempData["Msg"] = "Created Successfully!";
objBs.teamBs.Insert(objTeam);
return RedirectToAction("Index");
}
else
{
return View("Index");
}
}
catch (Exception e1)
{
TempData["Msg"] = "Create Failed! :" + e1.Message;
return RedirectToAction("Index");
}
}
}
}
I have the errors in the snip below so I'm not able to run the file. Below is the squiggly line of error from the controller:
and also squiggly line in the model as shown below:
On hovering over the Squiggly lines, I had this error
For the TeamValidation.cs
The Error is Generate Class for HttpPostedFileBase
While the error on the TeamController for the squiggly line .FileName is
'string' does not contain a defination for 'FileName' and no extension method 'FileName' accepting a first arguement of type 'string'
Your problem is the use a partial classes and the [MetadataType] attribute on class team. Your data model has property string teamPicture and your metadata class has a conflicting property HttpPostedFileBase teamPicture. Your controller method has parameter team objTeam so objTeam.teamPicture.FileName throws an error because teamPicture is typeof string. To solve this, remove the [MetadataType] attribute from your data model, and use a view model to represent what you want to edit in the view
Data model (in namespace BOL)
public class team
{
[Key]
public int teamID { get; set; }
public string teamName { get; set; }
public string teamPicture { get; set; }
public string description { get; set; }
public string content { get; set; }
}
Then create a new folder in your project for the view model (say ViewModels). Note the teamID should not be required since the view is for creating a new team
public class TeamVM
{
[Required(ErrorMessage = "Please Enter Your Team Name")]
[Display(Name = "Team Name")]
public string TeamName { get; set; }
[DisplayName("Team Picture")]
[Required(ErrorMessage = "Please Upload Team Picture")]
[ValidateFile]
public HttpPostedFileBase TeamPicture { get; set; }
[Required]
[Display(Name = "Description")]
public string Description { get; set; }
[Required(ErrorMessage = "Please Enter Team Content")]
[Display(Name = "Content")]
[MaxLength(200)]
public string Content { get; set; }
}
Your GET method should initialize and return an instance of TeamVM
[HttpGet]
public ActionResult Create()
{
TeamVM model = new TeamVM();
return View(model);
}
and the view will be #model yourAssembly.TeamVM
Then the POST method will be
[HttpPost]
public ActionResult Create(TeamVM model)
{
....
var fileName = Path.GetFileName(model.TeamPicture.FileName);
var path = Path.Combine(Server.MapPath("~/Content/Upload"), fileName);
model.TeamPicture.SaveAs(path);
// map the view model to a new instance of the data model
team objTeam = new team
{
teamName = model.TeamName,
teamPicture = path,
description = model.Description,
content = model.Content
};
// save and redirect
objBs.teamBs.Insert(objTeam);
....
}
You use MetadataTypeAttribute and in documentation is:
You then define the metadata type as a normal class, except that you declare simple properties for each of the members to which you want to apply validation attributes.
Example from documentation:
[MetadataType(typeof(ProductMetadata))]
public partial class Product
{
... Existing members defined here, but without attributes or annotations ...
}
Then your class team should be like this:
[MetadataType(typeof(TeamValidation))]
public partial class team
{
[Key]
public int teamID { get; set; }
public string teamName { get; set; }
public HttpPostedFileBase teamPicture { get; set; }
public string description { get; set; }
public string content { get; set; }
}
Related
Again...
I am doing a MVC with EF5 App. I have a Users Entity, that EF bind with Users table in Database... Looks like this.
public partial class Users
{
public long User_id { get; set; }
[Required]
[StringLength(30, ErrorMessage = "LastName cannot be longer than 30 characters.")]
public string LastName { get; set; }
[Required]
[StringLength(30, ErrorMessage = "Name cannot be longer than 30 characters.")]
public string Name { get; set; }
public int ProcessState_id { get; set; }
public string Sex { get; set; }
[Required,Range(1, int.MaxValue, ErrorMessage = "El País es Obligatorio")]
public int Country_id { get; set; }
[Required]
[EmailAddress(ErrorMessage = "Invalid Email Address")]
public string Email { get; set; }
public System.DateTime CreationDate { get; set; }
public Nullable<System.DateTime> UpDateTime { get; set; }
[RegularExpression(#"^.{5,}$", ErrorMessage = "Minimum 3 characters required")]
[Required]
[StringLength(9, MinimumLength = 3, ErrorMessage = "Password cannot be longer than 9 characters.")]
public string Password { get; set; }
public string Url { get; set; }
public byte[] Picture { get; set; }
public string CodArea { get; set; }
public string PhoneNumber { get; set; }
public virtual Countries Countries { get; set; }
public virtual ProcessStates ProcessStates { get; set; }
public virtual States States { get; set; }
[NotMapped] // Does not effect with your database
[RegularExpression(#"^.{5,}$", ErrorMessage = "Minimum 3 characters required")]
[StringLength(9, MinimumLength = 3, ErrorMessage = "Confirm Password cannot be longer than 9 characters.")]
[Compare("Password")]
public virtual string ConfirmPassword { get; set; }
}
I have a Model Class that i use it in my Create View....
public class UserViewModel
{
public Users user { get; set; }
public IList<SelectListItem> AvailableCountries { get; set; }
}
My Create Method in the Controller gets a UserViewModel instance...
My Create Method looks like this.
public async Task<ActionResult> Create(UserViewModel model, System.Web.HttpPostedFileBase image = null)
{
try
{
if (ModelState.IsValid)
{
model.user.ProcessState_id = Security.WAITING;
model.user.Rol_id = Security.ROL_PUBLIC;
model.user.CreationDate = DateTime.Now;
model.user.IP = Request.UserHostAddress;
model.user.Url = UserValidation.EncriptacionURL(model.user.Email);
if (image != null)
{
// product.ImageMimeType = image.ContentType;
model.user.Picture= new byte[image.ContentLength];
image.InputStream.Read(model.user.Picture, 0, image.ContentLength);
}
_db.Users.Add(model.user);
_db.SaveChanges();
return RedirectToAction("Create");
}
model.AvailableCountries = GetCountries();
return View(model);
}
catch (RetryLimitExceededException /* dex */)
{
}
return View(model);
}
So far so good.
For my Edit View, i need less properties from User class, so I have a new class with the properties I need. This class is called UserEditView.
public class UserEditView
{
public long User_id { get; set; }
[Required]
[StringLength(30, ErrorMessage = "LastName cannot be longer than 30 characters.")]
public string LastName { get; set; }
[Required]
[StringLength(30, ErrorMessage = "Name cannot be longer than 30 characters.")]
public string Name { get; set; }
[Required, Range(1, int.MaxValue, ErrorMessage = "El País es Obligatorio")]
public int Country_id { get; set; }
[Required]
[EmailAddress(ErrorMessage = "Invalid Email Address")]
public string Email { get; set; }
public Nullable<System.DateTime> UpDateTime { get; set; }
public byte[] Picture { get; set; }
public string CodArea { get; set; }
public string PhoneNumber { get; set; }
public virtual Countries Countries { get; set; }
}
I also create a new Model for Edit View, called UserEditViewModel and looks like this.
public class UserEditViewModel
{
public UserEditView user { get; set; }
public IList<SelectListItem> AvailableCountries { get; set; }
}
On my Edit method, I use Mapper to bind User entity with UserEditView
public ViewResult Edit(int User_id=3)
{
Users users = _db.Users
.FirstOrDefault(p => p.User_id == User_id);
var config = new MapperConfiguration(cfg =>
{
cfg.CreateMap<Users, UserEditView>();
});
IMapper mapper = config.CreateMapper();
UserEditView userEditView = mapper.Map<Users, UserEditView>(users);
var model = new UserEditViewModel
{
user = userEditView,
AvailableCountries = GetCountries(),
};
return View(model);
}
My problem arise when I want to Update the User table.
The Edit method gets UserEditViewModel instance.
public async Task<ActionResult> Edit(UserEditViewModel model, System.Web.HttpPostedFileBase image = null)
{
try
{
if (ModelState.IsValid)
{}
}
}
UserEditViewModel has an instance of UserEditView but I need an instance of Users in order to EF updates Users Table.
I need to Map again?
How can I get a Users Instance?
I add the following Class
public static class AutoMapperBootStrapper
{
public static void BootStrap()
{
var config = new MapperConfiguration(cfg =>
{
cfg.CreateMap<Users, UserEditView>();
cfg.CreateMap<UserEditView, Users>();
});
IMapper mapper = config.CreateMapper();
}
And I add in my Global.asax
public class MvcApplication : System.Web.HttpApplication
{
protected void Application_Start()
{
AutoMapperBootStrapper.BootStrap();
AreaRegistration.RegisterAllAreas();
RouteConfig.RegisterRoutes(RouteTable.Routes);
BundleConfig.RegisterBundles(BundleTable.Bundles);
}
}
then in the controller... i do
public ViewResult Edit(int User_id=3)
{
Users users = _db.Users.FirstOrDefault(p => p.User_id == User_id);
UserEditView userEditView = Mapper.Map<Users, UserEditView>(users);
}
But Mapper.Map have an error... it says Mapper is not instantiated.
the problem is because I defined more than one Mapper. If i define just one, it Works fine...
I need to Map again? How can I get a Users Instance?
You could get the User model from your database using the id and then map the properties that you need to be updated from the view model:
[HttpPost]
public ActionResult Edit(UserEditViewModel model, HttpPostedFileBase image = null)
{
if (!ModelState.IsValid)
{
// Validation failed => redisplay the Edit form so that the
// user can correct the errors
return View(model);
}
var user = _db.Users.FirstOrDefault(p => p.User_id == model.user.User_id);
if (user == null)
{
// no user with the specified id has been found in the database =>
// there's nothing to update
return NotFound();
}
// This will map only the properties of the user object that
// are part of the view model
Mapper.Map<Users, UserEditView>(model.user, user);
// at this stage you could manually update some properties that
// have not been mapped such as the uploaded image
// finally persist the changes to the database
_db.SaveChanges();
// redirect to some other action to show the updated users
return RedirectToAction("users");
}
Also the code you have shown in your question:
var config = new MapperConfiguration(cfg =>
{
cfg.CreateMap<Users, UserEditView>();
});
IMapper mapper = config.CreateMapper();
This is absolutely NOT something that you should be doing inside a controller action. AutoMapper mappings should be configured only once per application lifetime, ideally when your application starts, i.e. for a web application that would be Application_Start in Global.asax. In a controller action you should only use the already configured mappings. I strongly recommend you going through the AutoMapper's documentation for getting better understanding of how to use this framework.
Quote from the documentation:
Where do I configure AutoMapper?
If you're using the static Mapper method, configuration should only
happen once per AppDomain. That means the best place to put the
configuration code is in application startup, such as the Global.asax
file for ASP.NET applications. Typically, the configuration
bootstrapper class is in its own class, and this bootstrapper class is
called from the startup method. The bootstrapper class should call
Mapper.Initialize to configure the type maps.
I hate these kind of errors.
I have an ASP.NET Core MVC form. When I submit the form I get the dreaded "No parameterless constructor defined for this object." error.
I can't seem to hit anything in the debugger to help me find where I have went wrong.
My controller:
using Brand.Extensions;
using Brand.Models;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc;
namespace Brand.Controllers
{
public class ContentController : Controller
{
public IActionResult Index()
{
return View();
}
[HttpGet]
public IActionResult Upload()
{
return View();
}
[HttpPost]
[ValidateAntiForgeryToken]
public IActionResult Upload(UploadedContentModel uploadedContentModel, FormCollection formCollection, IFormFileCollection uploadedFiles)
{
ContentExtensions contentExtensions = new ContentExtensions();
FileExtensions fileExtensions = new FileExtensions();
FileModel file = new FileModel();
file = fileExtensions.GetFileModel(uploadedFiles["File"]);
uploadedContentModel.File = fileExtensions.UploadFile(file).ID;
FileModel thumbnail = new FileModel();
thumbnail = fileExtensions.GetFileModel(uploadedFiles["Thumbnail"]);
uploadedContentModel.Thumbnail = fileExtensions.UploadFile(thumbnail).ID;
uploadedContentModel.ID = contentExtensions.UploadContent(uploadedContentModel).ID;
return View();
}
}
}
My model:
public class UploadedContentModel
{
public Guid ID { get; set; }
public string Title { get; set; }
public string Description { get; set; }
public Guid File { get; set; }
public Guid Thumbnail { get; set; }
public string ContentType { get; set; }
public string Solutions { get; set; }
public string Industries { get; set; }
public string AdditionalTags { get; set; }
public DateTime Publish { get; set; }
public DateTime Expire { get; set; }
public string AuthorizedRoles { get; set; }
public string Author { get; set; }
public DateTime Created { get; set; }
}
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 can't understand what i'm doing wrong. Every time I'm getting this error:
The entity or complex type 'BusinessLogic.CompanyWithDivisionCount' cannot be constructed in a LINQ to Entities query.
I need to get info from 'Company' table and divisions count of each company from 'Division' table, and then make PagedList. Here is my 'Company' table:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.ComponentModel.DataAnnotations;
using BusinessLogic.Services;
using BusinessLogic.Models.ValidationAttributes;
namespace BusinessLogic.Models
{
public class Company
{
public Company()
{
Country = "US";
Status = true;
}
public int Id { get; set; }
[Required]
[UniqueCompanyName]
public string Name { get; set; }
public string Street { get; set; }
public string City { get; set; }
public string State { get; set; }
public int Zip { get; set; }
public string Country { get; set; }
public string ContactInfo { get; set; }
[Required]
public DateTime EffectiveDate { get; set; }
public DateTime TerminationDate { get; set; }
public bool Status { get; set; }
[Required]
public string URL { get; set; }
public string EAP { get; set; }
public string EAPCredentials { get; set; }
public string BrandingColors { get; set; }
public string Comments { get; set; }
}
}
Here is my domain model:
public class Company
{
public Company()
{
Country = "US";
Status = true;
}
public int Id { get; set; }
[Required]
[UniqueCompanyName]
public string Name { get; set; }
public string Street { get; set; }
public string City { get; set; }
public string State { get; set; }
public int Zip { get; set; }
public string Country { get; set; }
public string ContactInfo { get; set; }
[Required]
public DateTime EffectiveDate { get; set; }
public DateTime TerminationDate { get; set; }
public bool Status { get; set; }
[Required]
public string URL { get; set; }
public string EAP { get; set; }
public string EAPCredentials { get; set; }
public string BrandingColors { get; set; }
public string Comments { get; set; }
}
public class CompanyWithDivisionCount: Company // I'm using this
{
public int DivisionCount { get; set; }
}
Here is my controller:
public ActionResult CompaniesList(int? page)
{
var pageNumber = page ?? 1;
var companies = companyService.GetCompaniesWithDivisionsCount2();
var model = companies.ToPagedList(pageNumber, PageSize);
return View(model);
}
And here is my service part:
public IQueryable<CompanyWithDivisionCount> GetCompaniesWithDivisionsCount2()
{
return (from c in dataContext.Companies.AsQueryable()
select new CompanyWithDivisionCount
{
Id = c.Id,
Name = c.Name,
Status = c.Status,
EffectiveDate = c.EffectiveDate,
URL = c.URL,
EAP = c.EAP,
EAPCredentials = c.EAPCredentials,
Comments = c.Comments,
DivisionCount = (int)dataContext.Divisions.Where(b => b.CompanyName == c.Name).Count()
});
}
}
Thanks for help!!!
Creator of PagedList here. This has nothing to do with PagedList, but rather is an Entity Framework issue (I'm no expert on Entity Framework, so can't help you there). To confirm that this is true, write a unit test along the following lines:
[Test]
public void ShouldNotThrowAnException()
{
//arrange
var companies = companyService.GetCompaniesWithDivisionsCount2();
//act
var result = companies.ToList();
//assert
//if this line is reached, we win! no exception on call to .ToList()
}
I would consider changing you data model if possible so that instead of relating Companies to Divisions by name strings, instead use a properly maintained foreign key relationship between the two objects (Divisions should contain a CompanyID foreign key). This has a number of benefits (including performance and data integrity) and will almost certainly make your life easier moving forward if you need to make further changes to you app (or if any company ever decides that it may re-brand it's name).
If you create a proper foreign key relationship then your domain model could look like
public class Company
{
...
public virtual ICollection<Division> Divisions{ get; set; }
public int DivisionCount
{
get
{
return this.Divisions.Count()
}
}
...
}
I'm working on my first project using ASP MVC3. My application is code first using entity framework 4.1 and SQL server CE.
The application is a document library. The main model is Document which is a path to a pdf file and a bunch of metadata.
I have a "replace" function in my controller that takes one Document record, moves the file to an archive location, and updates the database with information about the replacement.
I am trying to store a list of strings with the document that represent filepaths to older versions of the same document.
No matter what I cannot get this list, called ArchivedFilePaths, to be anything but "null".
Model:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.ComponentModel;
using System.ComponentModel.DataAnnotations;
using System.Web.Mvc;
namespace DocLibrary.Models
{
public class Document
{
public int DocumentId { get; set; }
public int CategoryId { get; set; }
public string DocumentCode { get; set; }
public string Title { get; set; }
public string Description { get; set; }
public string FileUrl { get; set; }
public int FileSize { get; set; }
public string FileSizeString { get; set; }
public int Pages { get; set; }
public string Creator { get; set; }
public int Revision { get; set; }
public DateTime CreatedDate { get; set; }
public DateTime LastModifiedDate { get; set; }
public virtual Category Category { get; set; }
public List<String> ArchivedFilePaths { get; set; }
public SoftwareVersion SoftwareVersion { get; set; }
}
Controller:
public ActionResult Replace(HttpPostedFileBase file)
{
if (file != null && file.ContentLength > 0)
{
int id = Int32.Parse(Request["DocumentId"]);
Document doc = docsDB.Documents.Find(id);
//move the current version to the ~/Archive folder
string current_path = Path.Combine(Server.MapPath("~/Files"), Path.GetFileName(doc.FileUrl));
string archiveFileName = Path.GetFileNameWithoutExtension(doc.FileUrl) + "-" + doc.Revision.ToString() + ".pdf";
string destination_path = Path.Combine(Server.MapPath("~/Archive"), archiveFileName);
System.IO.File.Move(current_path, destination_path);
if (doc.ArchivedFilePaths == null)
{
doc.ArchivedFilePaths = new List<String>();
}
doc.ArchivedFilePaths.Add(destination_path);
//there are a bunch of statements that update the title, number of pages, etc. here, all of these work fine
try
{
docsDB.Entry(doc).State = EntityState.Modified;
docsDB.Logs.Add(new Log { LogDate = DateTime.Now, LogText = "Document replaced with a new version: " + doc.Title, });
docsDB.SaveChanges();
return RedirectToAction("Index");
}
catch (Exception ex)
{
Console.WriteLine(ex.ToString());
}
}
}
//if a file was not selected;
return RedirectToAction("Index");
}
The Index view displays all of the files and their properties, including ArchivedFilePaths. After replacing a document with a new file, all of the items in the Document model update properly except ArchivedFilePaths.
If I inspect the list in VisualStudio, it is not null after the doc.ArchivedFilePaths.Add statement. So I don't believe the list is ever saved in the database and I suspect there is something wrong with my model. If I change it to a single string I can update it just fine.
Does anyone have any insight? Thanks.