I'm using asp.net mvc with linq to sql repositories and the following code is throwing an mvc System.Data.Linq.DuplicateKeyException exception on this._table.Attach(entity)
My code is something like that:
public ActionResult Edit(int id)
{
return View(_controllerRepository.GetById(id));
}
public ActionResult Edit(Directivo entity)
{
try
{
_repository.Save(entity, this.UserName)
}
catch (Exception ex)
{
return View(ex);
}
}
And in the repository:
public virtual void Save(T entity, string userName)
{
if (0 == entity.Id)
{
entity.UsuarioIntroduccion = userName;
entity.FechaIntroduccion = DateTime.Now;
entity.UsuarioModificacion = null;
entity.FechaModificacion = null;
this._table.InsertOnSubmit(entity);
}
else
{
entity.UsuarioModificacion = userName;
entity.FechaModificacion = DateTime.Now;
this._table.Attach(entity);
this._table.Context.Refresh(RefreshMode.KeepCurrentValues, entity);
}
try
{
this._dataContext.SubmitChanges();
}
catch (SqlException ex)
{
throw new DataContextException(ex);
}
}
Note that the Id isn't 0.
Its really weird because it happens only with this class, i have a couple more that are working well.
The table is that:
CREATE TABLE [Directivo](
[Id] [int] IDENTITY(1,1) NOT NULL,
[Nombre] [varchar](45) NOT NULL,
[Apellidos] [varchar](60) NOT NULL,
[FechaNacimiento] [datetime] NULL,
[CargoDirectivoId] [int] NOT NULL,
[PathImagen] [varchar](250) NULL,
FechaIntroduccion datetime not null,
UsuarioIntroduccion varchar(45) not null,
FechaModificacion datetime,
UsuarioModificacion varchar(45),
PRIMARY KEY (Id),
FOREIGN KEY (CargoDirectivoId)
REFERENCES CargoDirectivo(Id)
ON DELETE NO ACTION
ON UPDATE NO ACTION
)
And the class is the autogenerated by linq and a partial class that makes it inherit an interface, and sets the buddy class for metadata to use xVal
Do you have any clues about what could be happening?
Thanks in advance!
I think the problem is in this._table.Attach(entity); move this line up before setting the new values as follows
public virtual void Save(T entity, string userName)
{
if (0 == entity.Id)
{
entity.UsuarioIntroduccion = userName;
entity.FechaIntroduccion = DateTime.Now;
entity.UsuarioModificacion = null;
entity.FechaModificacion = null;
this._table.InsertOnSubmit(entity);
}
else
{
this._table.Attach(entity);
entity.UsuarioModificacion = userName;
entity.FechaModificacion = DateTime.Now;
this._table.Context.Refresh(RefreshMode.KeepCurrentValues, entity);
}
try
{
this._dataContext.SubmitChanges();
}
catch (SqlException ex)
{
throw new DataContextException(ex);
}
}
and this may help you http://www.richardbushnell.net/2008/02/18/how-to-update-data-with-linq-to-sql/
Related
I have an application that you can create a user with a name and email. When the user is created, they are added to the database and they are assigned an ID (primary key).
When I create a user, the function RedirectToAction won't redirect me to the new user's homepage but still redirects me to the user that had created the user although I'm telling the controller to redirect me to the newly created ID's user homepage.
return RedirectToAction("GetUser", "User", new { id = userAdd.ID })
The user account is being created as I can see it in the database and a list of users so the AddUser function works, it just seems to be this line of code that is not working.
I am initializing the application into the first ID (1) user's profile that can then create another user.
Can someone tell me what has gone wrong here?
Routeconfig.cs file to point to the first users ID as initialisation:
routes.MapRoute(
name: "User",
url: "{controller}/{action}/{id}",
defaults: new { controller = "User", action = "GetUser", ID = 1 }
);
Controller that adds user:
public ActionResult AddUser(UserAdd userAdd)
{
try
{
// TODO: Add insert logic here
userService.AddUser(userAdd);
return RedirectToAction("GetUser", "User", new { id = userAdd.ID });
}
catch
{
return View();
}
}
My user DAO and IDAO are following.
DAO :
public void AddUser(User user, TheContext context)
{
context.Users.Add(user);
context.SaveChanges();
}
IDAO:
void AddUser(User user, TheContext context);
My user service layer code is:
Service:
public void AddUser(UserAdd userAdd)
{
User newUser = new User()
{
Name = userAdd.Name,
Email = userAdd.Email
};
using (var context = new TheContext())
{
userDAO.AddUser(newUser, context);
}
}
IService:
void AddUser(UserAdd userAdd);
GetUser action:
DAO:
public User GetUser(int id, TheContext context)
{
context.Users.Include(h => h.UserDetails).ToList();
return context.Users.Find(id);
}
IDAO:
User GetUser(int id, TheContext context);
Service:
public User GetUser(int id)
{
using (var context = new TheContext())
{
return userDAO.GetUser(id, context);
}
}
IService:
User GetUser(int id);
Controller:
public ActionResult GetUser(int id)
{
return View(userService.GetUser(id));
}
GetUser.cshtml view:
#model Tester.Data.Models.Domain.User
#{
ViewBag.Title = "GetUser";
}
<h2>My Test App</h2>
<div>
<p>#Html.ActionLink("Register", "AddUser", "User")</p>
</div>
<h2>Details for #Model.Name</h2>
#if (Model.UserDetails.Count != 0)
{
#Html.Partial("~/Views/User/GetUserDetails.cshtml", Model.UserDetails);
}
Initaliser class:
protected override void Seed(TheContext context)
{
User user1 = new User();
user1.ID = "1";
user1.Name = "test";
user1.Email = "test#test.com";
context.Users.Add(user1);
UserDetail userDetail1 = new UserDetail();
userDetail1.Name = "test";
userDetail1.Email = "test.com";
context.UserDetails.Add(userDetail1);
context.SaveChanges();
I could however use:
TheContext.Data.Models.Domain.User user = new TheContext.Data.Models.Domain.User();
user.Name = "test";
user.Email = "test#test.com";
TheContext.Data.Models.Domain.UserDetail userDetail = new TheContext.Data.Models.Domain.UserDetail();
userDetail.Name = "test";
userDetail.Email = "test#test.com";
T-SQL for Users:
CREATE TABLE [dbo].[Users] (
[ID] INT IDENTITY (1, 1) NOT NULL,
[Name] NVARCHAR (MAX) NULL,
[Email] NVARCHAR (MAX) NULL,
CONSTRAINT [PK_dbo.Users] PRIMARY KEY CLUSTERED ([ID] ASC)
);
T-SQL for UserDetails:
CREATE TABLE [dbo].[UserDetails] (
[ID] INT IDENTITY (1, 1) NOT NULL,
[Name] NVARCHAR (MAX) NULL,
[Email] NVARCHAR (MAX) NULL,
[User_ID] INT NULL,
CONSTRAINT [PK_dbo.UserDetails] PRIMARY KEY CLUSTERED ([ID] ASC),
CONSTRAINT [FK_dbo.UserDetails_dbo.Users_User_ID] FOREIGN KEY ([User_ID]) REFERENCES [dbo].[Users] ([ID])
);
User class:
public class User
{
public int ID { get; set; }
public string Name { get; set; }
public string Email { get; set; }
public virtual ICollection<UserDetail> UserDetails { get; set; }
}
UserDetails class:
public class UserDetail
{
public int ID { get; set; }
public string Name { get; set; }
public string Email { get; set; }
}
Instead of using an ID from posted view model, get ID of newly created record from service and use it.
IService:
int AddUser(UserAdd userAdd);
Service:
public int AddUser(UserAdd userAdd)
{
var userId = 0;
User newUser = new User()
{
Name = userAdd.Name,
Email = userAdd.Email
};
using (var context = new TheContext())
{
userDAO.AddUser(newUser, context);
// assing Id here after add
userId = newUser.ID;
}
return userId;
}
Action method:
public ActionResult AddUser(UserAdd userAdd)
{
try
{
// capture Id from service
var newId = userService.AddUser(userAdd);
return RedirectToAction("GetUser", "User", new { id = newId });
}
catch
{
return View(userAdd); // return view with posted model so you don't loose data
}
}
EDITED:
I have Created CRUD Functions for each Modals and now i am trying to get recent Inserted Id and use it in different view.
Here is what I have tried so far
I have created 2 classes(Layer based) for CRUD function for each ContextEntities db to practice pure OOP recursive approach and following is the code.
1. Access Layer
ViolatorDB
public class ViolatorDB
{
private TPCAEntities db;
public ViolatorDB()
{
db = new TPCAEntities();
}
public IEnumerable<tbl_Violator> GetALL()
{
return db.tbl_Violator.ToList();
}
public tbl_Violator GetByID(int id)
{
return db.tbl_Violator.Find(id);
}
public void Insert(tbl_Violator Violator)
{
db.tbl_Violator.Add(Violator);
Save();
}
public void Delete(int id)
{
tbl_Violator Violator = db.tbl_Violator.Find(id);
db.tbl_Violator.Remove(Violator);
Save();
}
public void Update(tbl_Violator Violator)
{
db.Entry(Violator).State = EntityState.Modified;
Save();
}
public void Save()
{
db.SaveChanges();
}
}
2. Logic Layer
ViolatorBs
public class ViolatorBs
{
private ViolatorDB objDb;
public ViolatorBs()
{
objDb = new ViolatorDB();
}
public IEnumerable<tbl_Violator> GetALL()
{
return objDb.GetALL();
}
public tbl_Violator GetByID(int id)
{
return objDb.GetByID(id);
}
public void Insert(tbl_Violator Violator)
{
objDb.Insert(Violator);
}
public void Delete(int id)
{
objDb.Delete(id);
}
public void Update(tbl_Violator Vioaltor)
{
objDb.Update(Vioaltor);
}
}
And Finally using Logic Layer functions in presentation Layer.Here insertion is performed as:
public class CreateViolatorController : Controller
{
public TPCAEntities db = new TPCAEntities();
private ViolatorBs objBs;
public CreateViolatorController()
{
objBs = new ViolatorBs();
}
public ActionResult Index()
{
var voilator = new tbl_Violator();
voilator=db.tbl_Violator.Add(voilator);
ViewBag.id = voilator.VID;
return View();
}
[HttpPost]
public ActionResult Create(tbl_Violator Violator)
{
try
{
if (ModelState.IsValid)
{
objBs.Insert(Violator);
TempData["Msg"] = "Violator Created successfully";
return RedirectToAction("Index");
}
else
{
return View("Index");
}
}
catch (Exception ex)
{
TempData["Msg"] = "Failed..." + ex.Message + " " + ex.ToString();
return RedirectToAction("Index");
}
}
}
Now here is the main part how do i get perticuller inserted id in another controller named Dues while performing insertion ?
In sqlqery I would have used ##IDENTITY but in Entity Framework I'm not sure.
I'm new to mvc framework any suggestion or help is appreciated Thanks in Advance.
Once you save your db context the id is populated back to your entity by EF automatically.
for example.
using(var context = new DbContext())
{
var employee = new Employee(); //this has an id property
context.Employees.Add(employee);
context.SaveChanges();
var id = employee.id; // you will find the id here populated by EF
}
You dont need to add and save your table as you have done this already in your voilatorDB class just fetch the last id like following
var id = yourTableName.Id;
db.yourTableName.find(id);
Or you can simply write one line code to achive that using VoilatorBs class function
GetbyID(id);
I am using Entity Framework 6 and I have a repository looking like the following with the Add and Update methods removed to make it shorter:
public class GenericRepository<T> : IRepository<T> where T : class
{
public GenericRepository(DbContext dbContext)
{
if (dbContext == null)
throw new ArgumentNullException("An instance of DbContext is required to use this repository", "context");
DbContext = dbContext;
DbSet = DbContext.Set<T>();
}
protected DbContext DbContext { get; set; }
protected DbSet<T> DbSet { get; set; }
public virtual IQueryable<T> Find(Expression<Func<T, bool>> predicate)
{
return DbSet.Where<T>(predicate);
}
public virtual IQueryable<T> GetAll()
{
return DbSet;
}
public virtual T GetById(int id)
{
//return DbSet.FirstOrDefault(PredicateBuilder.GetByIdPredicate<T>(id));
return DbSet.Find(id);
}
public virtual void Delete(T entity)
{
DbEntityEntry dbEntityEntry = DbContext.Entry(entity);
if (dbEntityEntry.State != EntityState.Deleted)
{
dbEntityEntry.State = EntityState.Deleted;
}
else
{
DbSet.Attach(entity);
DbSet.Remove(entity);
}
}
public virtual void Delete(int id)
{
var entity = GetById(id);
if (entity == null) return; // not found; assume already deleted.
Delete(entity);
}
}
In my controller I call the repository like this:
public HttpResponseMessage DeleteTest(int id)
{
Test test = _uow.Tests.GetById(id);
if (test == null)
{
return Request.CreateResponse(HttpStatusCode.NotFound);
}
try
{
_uow.Tests.Delete(test);
_uow.Commit();
return Request.CreateResponse(HttpStatusCode.OK);
}
catch (Exception ex)
{
return Request.CreateErrorResponse(HttpStatusCode.BadRequest, ex);
}
}
This works for a single test but how can I delete for example all tests that have an examId column value of 1 being
that examId is one of the columns in the Test table.
You can create another delete method in your generic repository class, see below:
public virtual void Delete(Expression<Func<T, bool>> predicate)
{
IQueryable<T> query = DbSet.Where(predicate).AsQueryable();
foreach (T obj in query)
{
DbSet.Remove(obj);
}
}
Then you can use it like below, it will delete all records which Id equalsid.
_uow.Test.Delete(n => n.Id = id)
I'm not sure if EF is able to handle multiple delete now given a certain value, but the last time I did this I had to resort to a loop.
public HttpResponseMessage DeleteTest(int id)
{
var testList = _uow.Tests.GetAll().Where(o => o.Id == id);
if (testList.Count() == 0)
{
return Request.CreateResponse(HttpStatusCode.NotFound);
}
try
{
foreach (var test in testList)
{
_uow.Tests.Delete(test);
}
_uow.Commit();
return Request.CreateResponse(HttpStatusCode.OK);
}
catch (Exception ex)
{
return Request.CreateErrorResponse(HttpStatusCode.BadRequest, ex);
}
}
If the "test" table is a foreign table linked to a primary table on the "ID" column, you may want to consider doing a cascading delete in this case.
You can use RemoveRange()
public virtual void Delete(Expression<Func<T,bool>> predicate)
{
var query = Context.Set<T>().Where(predicate);
Context.Set<T>().RemoveRange(query);
}
I am trying to create a CMS using the MVC framework.
All was going well, I created my tables to store my ViewData, ViewTitle and virtualPath.
I use VirthPathProvider and it looks like this:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Web.Hosting;
using CMS;
using System.Collections.ObjectModel;
using CMS.Components;
using System.Web;
namespace CMS.Providers
{
public class PageVirtualPathProvider : VirtualPathProvider
{
private Directory current { get; set; }
private Collection<Directory> directories { get; set; }
private Collection<BasePage> pages { get; set; }
public override bool FileExists(string virtualPath)
{
string Path = (VirtualPathUtility.GetDirectory(virtualPath) != "~/") ? VirtualPathUtility.RemoveTrailingSlash(VirtualPathUtility.GetDirectory(virtualPath)) : VirtualPathUtility.GetDirectory(virtualPath);
if (IsVirtualPath(Path))
{
BasePage oPage = FindPage(virtualPath);
if (oPage != null)
return true;
}
bool bExists = base.FileExists(virtualPath);
return bExists;
}
public override VirtualFile GetFile(string virtualPath)
{
string Path = (VirtualPathUtility.GetDirectory(virtualPath) != "~/") ? VirtualPathUtility.RemoveTrailingSlash(VirtualPathUtility.GetDirectory(virtualPath)) : VirtualPathUtility.GetDirectory(virtualPath);
if (IsVirtualPath(Path))
{
BasePage oPage = FindPage(virtualPath);
if (oPage != null)
return new PageVirtualFile(virtualPath, oPage.ViewData.ToArray());
}
return base.GetFile(virtualPath);
}
public override bool DirectoryExists(string virtualDir)
{
if (IsVirtualPath(virtualDir))
{
if (current != null)
{
if (current.Path.ToLower() != virtualDir.ToLower())
current = new Directory(virtualDir, "53AF0033-4011-4C8F-A14D-7CE9301E264D");
}
else
{
current = new Directory(virtualDir, "53AF0033-4011-4C8F-A14D-7CE9301E264D");
}
if (current != null)
return true;
}
return base.DirectoryExists(virtualDir);
}
public override VirtualDirectory GetDirectory(string virtualDir)
{
if (IsVirtualPath(virtualDir))
{
if (current != null)
{
if (current.Path.ToLower() != virtualDir.ToLower())
current = new Directory(virtualDir, "53AF0033-4011-4C8F-A14D-7CE9301E264D");
}
else
{
current = new Directory(virtualDir, "53AF0033-4011-4C8F-A14D-7CE9301E264D");
}
if (current != null)
return new CmsVirtualDirectory(virtualDir, "53AF0033-4011-4C8F-A14D-7CE9301E264D");
}
return base.GetDirectory(virtualDir);
}
public override System.Web.Caching.CacheDependency GetCacheDependency(string virtualPath, System.Collections.IEnumerable virtualPathDependencies, DateTime utcStart)
{
string Path = (VirtualPathUtility.GetDirectory(virtualPath) != "~/") ? VirtualPathUtility.RemoveTrailingSlash(VirtualPathUtility.GetDirectory(virtualPath)) : VirtualPathUtility.GetDirectory(virtualPath);
if (IsVirtualPath(Path))
return null;
return base.GetCacheDependency(virtualPath, virtualPathDependencies, utcStart);
}
public override string GetFileHash(string virtualPath, System.Collections.IEnumerable virtualPathDependencies)
{
string Path = (VirtualPathUtility.GetDirectory(virtualPath) != "~/") ? VirtualPathUtility.RemoveTrailingSlash(VirtualPathUtility.GetDirectory(virtualPath)) : VirtualPathUtility.GetDirectory(virtualPath);
if (IsVirtualPath(Path))
return Guid.NewGuid().ToString();
return base.GetFileHash(virtualPath, virtualPathDependencies);
}
private BasePage FindPage(string virtualPath)
{
string VirtualName = VirtualPathUtility.GetFileName(virtualPath).ToLower();
if (pages == null)
{
pages = PageManager.getAllPages("53AF0033-4011-4C8F-A14D-7CE9301E264D", false);
}
BasePage oPage = pages.SingleOrDefault(page => page.Path.ToLower() == VirtualName);
return oPage;
}
private bool IsVirtualPath(string virtualPath)
{
string Path = VirtualPathUtility.ToAppRelative(virtualPath);
if (directories == null)
{
directories = DirectoryManager.GetAllDirectories("53AF0033-4011-4C8F-A14D-7CE9301E264D");
}
Directory oDir = directories.SingleOrDefault(dir => dir.Path.ToLower() == Path.ToLower());
if (oDir == null || virtualPath == "~/") return false; // If we don't have directory, return false
return true; // Hit only if we are null
}
}
}
Now my problem is this:
Getting the pages is fine, when they are virtual it returns null as Cache and the FileHash is always a different string, so GetFile is called.
My file is returned, but it never finds the Layout.
I believe this is because there is no _ViewStart.cshtml because there is no views directory....So how can I force it to use a Layout?
I have tried so many things, like getting my virtual pages to inherit from #inherits System.Web.Mvc.WebViewPage, etc, but I still get the same problem....
When I navigate to a virtual page, I get this error:
Unable to cast object of type 'ASP._Page_Guidelines_index_cshtml' to type 'System.Web.IHttpHandler'.
Please help me; I have been stuck on this for 3 weeks....
If anyone else is having this issue. I did solve it. Here is my complete code :)
First, create our RouteHandler
public class CmsRouteHandler : IRouteHandler
{
public IHttpHandler GetHttpHandler(RequestContext requestContext)
{
return new CmsHandler(requestContext); // Return the requestContext and handle as normal
}
}
Then we need to create our MvcHanlder
class CmsHandler : MvcHandler, IRequiresSessionState
{
public CmsHandler(RequestContext requestContext)
: base(requestContext)
{
}
protected override IAsyncResult BeginProcessRequest(HttpContext httpContext, AsyncCallback callback, object state)
{
return base.BeginProcessRequest(httpContext, callback, state);
}
protected override IAsyncResult BeginProcessRequest(HttpContextBase httpContext, AsyncCallback callback, object state)
{
try
{
// If we are using a company Url, then the session should not be null
if (CompanyProvider.CurrentCompanyId() != null)
{
var vpp = new CmsVirtualPathProvider(); // Create an instance of our VirtualPathProvider class
var requestContext = ((MvcHandler)httpContext.Handler).RequestContext; // Get our request context
string path = requestContext.HttpContext.Request.Url.AbsolutePath; // Get our requested path
// If we have a path
if (path != null)
{
Collection<IPage> pages = vpp.pages; // Get all the published pages for this company
// If we have pages
if (pages != null)
{
IPage oPage = pages.SingleOrDefault(page => page.Path.ToLower() == path.ToLower()); // Select the page matching our requested path (if any)
if (oPage != null) // If we find the page
{
requestContext.RouteData.Values["controller"] = "_Cms"; // Set the controller
requestContext.RouteData.Values["action"] = "Index"; // And the action
}
}
}
}
return base.BeginProcessRequest(httpContext, callback, state);
}
catch (Exception ex)
{
log4net.ILog _log = log4net.LogManager.GetLogger(this.GetType());
_log.Fatal(ex);
throw new Exception(ex.Message);
}
}
protected override void ProcessRequest(HttpContext httpContext)
{
base.ProcessRequest(httpContext);
}
}
You can see in my application I have a companyId that is because I am using subdomains for multiple companies, but in a normal case that would not be needed.
And the full VirtualPathProvider looks like this:
public class CmsVirtualPathProvider : VirtualPathProvider
{
public Collection<IPage> pages
{
get
{
if (HttpContext.Current.Session != null)
{
return PageProvider.GetPublishedPages(CompanyProvider.CurrentCompanyId(), true);
}
else
return null;
}
}
public override bool FileExists(string virtualPath)
{
if (IsVirtualPath(virtualPath))
{
if (FindPage(virtualPath) != null)
{
PageVirtualFile file = (PageVirtualFile)GetFile(virtualPath);
return file.Exists;
}
}
return Previous.FileExists(virtualPath);
}
public override VirtualFile GetFile(string virtualPath)
{
if (IsVirtualPath(virtualPath))
{
IPage oPage = FindPage(virtualPath);
if (oPage != null)
return new PageVirtualFile(virtualPath, oPage.ViewData);
}
return Previous.GetFile(virtualPath);
}
public override System.Web.Caching.CacheDependency GetCacheDependency(string virtualPath, System.Collections.IEnumerable virtualPathDependencies, DateTime utcStart)
{
if (IsVirtualPath(virtualPath))
return null;
return Previous.GetCacheDependency(virtualPath, virtualPathDependencies, utcStart);
}
public override string GetFileHash(string virtualPath, System.Collections.IEnumerable virtualPathDependencies)
{
if (IsVirtualPath(virtualPath))
return Guid.NewGuid().ToString();
return Previous.GetFileHash(virtualPath, virtualPathDependencies);
}
private IPage FindPage(string virtualPath)
{
string VirtualName = VirtualPathUtility.GetFileName(virtualPath).ToLower();
if (pages != null)
{
IPage oPage = pages.SingleOrDefault(page => page.FileName == VirtualName);
return oPage;
}
else
return null;
}
private bool IsVirtualPath(string virtualPath)
{
string Path = (VirtualPathUtility.GetDirectory(virtualPath) != "~/") ? VirtualPathUtility.RemoveTrailingSlash(VirtualPathUtility.GetDirectory(virtualPath)) : VirtualPathUtility.GetDirectory(virtualPath);
if (Path.StartsWith("/Views/_Cms", StringComparison.InvariantCultureIgnoreCase))
return true;
else
return false;
}
}
Now you just need a controller to handle all the page requests. Mine looks like this:
public class _CmsController : Controller
{
private Collection<IPage> pages { get; set; }
public ActionResult Index()
{
string uri = Request.Url.AbsolutePath; // Get our Requested Url
string queryString = Request.Url.Query;
Profile currentUser = ProfileProvider.CurrentUser(); // Get our current user if we have one
if (pages == null) // If pages is null
pages = PageProvider.GetPublishedPages(CompanyProvider.CurrentCompanyId(), true); // Get our published pages
IPage page = pages.SingleOrDefault(p => p.Path.ToLower() == uri.ToLower()); // Get the current page
if (page == null) // If our page is null
throw new HttpException(404, "Are you sure this page exists?"); // Throw the 404 error
if (page.Restricted && currentUser == null) // If our page is restricted and we are not logged in
return Redirect("~/Account/LogOn?ReturnUrl=" + page.Path + queryString); // Redirect to the login page
if (currentUser != null)
{
IPage oForbidden = currentUser.GetForbiddenPages().SingleOrDefault(p => p.Id == page.Id); // Finds the page in our forbidden pages, if it exists
if (oForbidden != null) // If we find the forbidden page
throw new HttpException(401, "You do not have permission to view this page."); // Throw 401 error
AuditProvider.Create(currentUser, page, Event.View); // Create our audit trail if we get this far
}
return View(Path.GetFileNameWithoutExtension(page.Id.ToString())); // Show the page
}
}
And just because I am feeling nice, here is my table structure
CREATE TABLE [dbo].[cms_Pages](
[Id] [int] IDENTITY(1,1) NOT NULL,
[DateCreated] [datetime] NOT NULL,
[UserId] [uniqueidentifier] NOT NULL,
[Author] [nvarchar](256) NOT NULL,
[DateModified] [datetime] NULL,
[ModifiedById] [uniqueidentifier] NULL,
[ModifiedBy] [nvarchar](256) NULL,
[CompanyId] [uniqueidentifier] NOT NULL,
[Name] [varchar](100) NOT NULL,
[Description] [text] NULL,
[FileName] [varchar](255) NULL,
[Path] [varchar](255) NULL,
[Link] [varchar](255) NULL,
[Published] [bit] NOT NULL,
[TypeId] [int] NOT NULL,
[Order] [int] NOT NULL,
[Lineage] [nvarchar](255) NOT NULL,
[ParentId] [int] NULL,
[ViewData] [varbinary](max) NULL,
[ViewTitle] [varchar](255) NULL,
[Restricted] [bit] NOT NULL,
CONSTRAINT [PK_cg_Pages] PRIMARY KEY CLUSTERED
(
[Id] ASC
)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY]
) ON [PRIMARY] TEXTIMAGE_ON [PRIMARY]
GO
obviously again, you don't need the companyId :)
Here is a peek at the data contained inside the table
I transformed the html for a page into bytes using this line of code:
ASCIIEncoding encoding = new ASCIIEncoding();
var decodedContent = HttpUtility.UrlDecode(Content, Encoding.Default);
Byte[] bytes = encoding.GetBytes(decodedContent);
you only need the UrlDecode if you are escaping your HTML before submitting it to your save function. If you are not then you can just use:
ASCIIEncoding encoding = new ASCIIEncoding();
Byte[] bytes = encoding.GetBytes(Content);
I really hope this helps someone out. I was in a right mess trying to work this out :)
cheers,
r3plica
I have a property on my Model Class that performs a type conversion as follows:
Model:
public class TimeClass
{
private int timeInSeconds;
[Required]
public int Id {get;set;}
[Required]
public string Timer
{
get {
TimeSpan ts = new TimeSpan(0,0,(int)timeInSeconds);
return ts.ToString(#"mm\:ss",CultureInfo.InvariantCulture);
}
set {
try {
var ts = TimeSpan.ParseExact(value, #"mm\:ss", CultureInfo.InvariantCulture);
timeInSeconds= Convert.ToInt32(ts.TotalSeconds);
}
catch {
//Is it possible to add a validation error to the modelstate here
}
}
}
}
Controller:
[HttpPost]
public ActionResult Create(FormCollection collection)
{
string[] whitelist = new[] {"Id", "Timer" };
if (TryUpdateModel(quiz, whitelist))
{
//Save to Repo
return RedirectToAction("Edit", new { Id = Id });
}
return View(tc);
}
What is the appropriate pattern to add an appropriate ModelError if the TimeSpan.Parse throws an exception?
Currently it will give the generic error "The value "xxx" is invalid". How can I customize this to return a specific error?
Rather than putting the try catch within your model, you could move the error handling to your controller code. For example if the tryupdatemodel throws an exception you could add a custom model error:
TimeClass model = new TimeClass();
string[] whitelist = new[] {"Id", "Timer" };
try
{
UpdateModel(model);
//Save to Repo
return RedirectToAction("Edit", new { Id = Id });
}
catch
{
// Generate error
ModelState.AddModelError(string.Empty, "Model Error Here");
return View(tc);
}