Important note: the column online in my MySQL table is of type enum and I'm not allowed to change the database
So here's my entity:
public enum onlineStatus { online = 1, offline = 0 };
public onlineStatus online { get; set; }
And in my db context:
public class UsersContext : DbContext
{
public DbSet<User> Users { get; set; }
public void AddUser(User user) // I'm using a custom membership provider that calls this method
{
try
{
user.online = 0; // this will give me the word "offline"
Users.Add(user);
SaveChanges();
}
catch (Exception ex)
{
throw ex;
}
}
}
Somehow user.online = 0; doesn't add to the database. And how do I make it so that it saves the value 0 instead of offline. Thanks in advance.
Related
I created scaffolded controller for CRUD project and there is a bug. When I Create new entity(record) for MySql DB it works as expected, one of those values is HiredDate. But when I go to Edit something for example FiredDate inside different view it overrides Previously created record for the HiredDate and sets it to 0001-01-01 .
Here is code for the Model:
[Column("City", TypeName = "varchar(100)")]
public string City { get; set; }
[Required]
[Column("Department", TypeName = "varchar(100)")]
public string Department { get; set; }
[Required]
[Column("HiredDate", TypeName = "date")]
[Display(Name = "Hired On")]
public DateTime HiredDate { get; set; }
[Column("FiredDate", TypeName = "date")]
[Display(Name = "Fired On")]
public DateTime? FiredDate { get; set; }
[Required]
[Column("ModifiedDate", TypeName = "datetime")]
[Display(Name = "Modified Date")]
public DateTime ModifiedDate { get; set; }
And Controller code for edit:
public async Task<IActionResult> Edit(int id, [Bind("ID,FirstName,LastName,Email,Phone,Age,City,Department,FiredDate")] Employee employee)
{
if (id != employee.ID)
{
return NotFound();
}
if (ModelState.IsValid)
{
try
{
_context.Update(employee);
await _context.SaveChangesAsync();
}
catch (DbUpdateConcurrencyException)
{
if (!EmployeeExists(employee.ID))
{
return NotFound();
}
else
{
throw;
}
}
return RedirectToAction(nameof(Index));
}
return View(employee);
}
And Controller code for create:
public async Task<IActionResult> Create([Bind("FirstName,LastName,Email,Phone,Age,City,Department,HiredDate")] Employee employee)
{
if (ModelState.IsValid)
{
_context.Add(employee);
await _context.SaveChangesAsync();
return RedirectToAction(nameof(Index));
}
return View(employee);
}
Result of the override looks something like this:
How can I fix it? I want HiredDate to stay unchanged(for example 2020-01-01) when I do Edit action and modify any other fields except HiredDate.
Never mind I found the problem, This BIND is really not user friendly, it forces developers to show all fields inside the view and if one is missing its going to override with null values the one thats missing.
The solution is inside Edit View Form. This Form should display all fields that your Model has.
I have using .net Core 3.1 scaffolded Identity and extended that with my own classes. But when I use the build in DeletePersonalData.OnPostAsync() it fails due to my relations from my other classes. I don't get how I make the delete to delete all the extended classes too.
Error message:
SqlException: The DELETE statement conflicted with the REFERENCE
constraint "FK_Workspaces_AspNetUsers_OwnerId". The conflict occurred
in database "myupload", table "dbo.Workspaces", column 'OwnerId'. The
statement has been terminated.
Microsoft.Data.SqlClient.SqlCommand+<>c.b__164_0(Task
result)
Extended Identity:
public class ApplicationDbContext : IdentityDbContext<IdentityUser>
{
public ApplicationDbContext(DbContextOptions<ApplicationDbContext> options)
: base(options)
{
}
public DbSet<MyFile> MyFiles { get; set; }
public DbSet<Workspace> Workspaces { get; set; }
public DbSet<WorkspacePermission> WorkspacePermissions { get; set; }
protected override void OnModelCreating(ModelBuilder builder)
{
base.OnModelCreating(builder);
// Customize the ASP.NET Identity model and override the defaults if needed.
// For example, you can rename the ASP.NET Identity table names and more.
// Add your customizations after calling base.OnModelCreating(builder);
builder.Entity<Workspace>()
.HasOne(p => p.Owner)
.WithMany()
.OnDelete(DeleteBehavior.Cascade);
builder.Entity<MyFile>()
.HasOne(p => p.Workspace)
.WithMany(b => b.MyFile);
}
}
And the delete method:
public async Task<IActionResult> OnPostAsync()
{
var user = await _userManager.GetUserAsync(User);
if (user == null)
{
return NotFound($"Unable to load user with ID '{_userManager.GetUserId(User)}'.");
}
RequirePassword = await _userManager.HasPasswordAsync(user);
if (RequirePassword)
{
if (!await _userManager.CheckPasswordAsync(user, Input.Password))
{
ModelState.AddModelError(string.Empty, "Incorrect password.");
return Page();
}
}
var result = await _userManager.DeleteAsync(user);
var userId = await _userManager.GetUserIdAsync(user);
if (!result.Succeeded)
{
throw new InvalidOperationException($"Unexpected error occurred deleting user with ID '{userId}'.");
}
await _signInManager.SignOutAsync();
_logger.LogInformation("User with ID '{UserId}' deleted themselves.", userId);
return Redirect("~/");
}
}
One of the Classes:
public class Workspace
{
// Base
public Guid WorkspaceID { get; set; }
public string Name { get; set; }
// Security
public virtual IdentityUser Owner { get; set; }
public string Password { get; set; }
// Files
public virtual ICollection<MyFile> MyFile { get; set; }
// Statistics
public Workspace()
{
}
}
Using Fluent API I managed to add the Cascade Delete to the IdentityUser with this Code in the OnModelCreating method.
builder.Entity<Workspace>()
.HasOne(p => p.Owner)
.WithMany()
.OnDelete(DeleteBehavior.Cascade);
builder.Entity<MyFile>()
.HasOne(p => p.Workspace)
.WithMany(b => b.MyFile);
I am working on updating to a more manageable repository pattern in my MVC 4 project that uses Entity Framework code first. I've integrated a generic base repository class that will do basic CRUD operations so I don't have to implement these in each repository I create. I have ran into an issue where my All method needs to filter there query by a deleted flag if the entity is a type of TrackableEntity. Since the Entity is generic in the base repository I am attempting to cast is to a type of TrackableEntity in the where which just results in the following error message.
The 'TypeAs' expression with an input of type 'NameSpace.Models.ClientFormField' and a check of type 'NameSpace.Models.TrackableEntity' is not supported. Only entity types and complex types are supported in LINQ to Entities queries.
This error makes complete since and I understand why the code I have is not working but I am trying to find a way to filter out deleted items without having to override this method in all of my repositories. The code I have for my All method is below.
public virtual IEnumerable<T> All()
{
if (typeof(T).IsSubclassOf(typeof(TrackableEntity)))
return dbSet.Where(e => !(e as TrackableEntity).IsDeleted).ToList();
return dbSet.ToList();
}
I know that I can do the following
public virtual IEnumerable<T> All(Expression<Func<T, bool>> predicate = null)
{
if (predicate != null)
return dbSet.Where(predicate).IsDeleted).ToList();
return dbSet.ToList();
}
And then add this to all of my repositories
public override IEnumerable<CaseType> All(Expression<Func<CaseType,bool>> predicate = null)
{
if (predicate == null)
predicate = e => !e.IsDeleted;
return base.All(predicate);
}
The problem I have with this is that I am duplicating code, this is basically a copy and paste into all of my repositories which defeats the purpose of changing to this new repository pattern. I made the switch to end duplicated code in my repositories.
Here is an example of one of my entities.
public class CaseType : TrackableEntity, IValidatableObject
{
public int Id { get; set; }
public string Name { get; set; }
public bool InUse { get; set; }
public bool IsValid { get { return !this.Validate(null).Any(); } }
public IEnumerable<ValidationResult> Validate(ValidationContext validationContext)
{
if (String.IsNullOrEmpty(Name))
yield return new ValidationResult("Case Type name cannot be blank", new[] { "Name" });
//Finish Validation Rules
}
}
And the TrackableEntity
public abstract class TrackableEntity
{
public bool Active { get; set; }
public bool IsDeleted { get; set; }
public virtual User CreatedBy { get; set; }
public virtual User ModifiedBy { get; set; }
public DateTime DateCreated { get; set; }
public DateTime DateModified { get; set; }
}
Any help on this would be much appreciated.
I finally got a solution working that I am happy with. I ended up making 2 generic repositories. One that is the base repository which deals with all of the calls to the database for my BaseEntity which all entities inherit from. Then I made my 2nd generic repo which is inherits BaesEntity and overrides a few methods to handle the needs of my TrackableEntities. In the end this does what I want by handling the filtering of soft deleted items from within the repo and also gives me more flexibility with the TrackableEntity.
BaseRepository -
public class BaseRepository<T> : IBaseRepository<T> where T : BaseEntity
{
private readonly IAppDb _db;
private readonly IDbSet<T> _dbSet;
public BaseRepository(IAppDb db)
{
_db = db;
_dbSet = Lwdb.Set<T>();
}
protected IAppDb Lwdb
{
get { return _db; }
}
#region IBaseRepository<T> Members
public virtual T GetById(int id)
{
return _dbSet.Find(id);
}
public virtual T Add(T entity)
{
_dbSet.Add(entity);
_db.Commit();
return entity;
}
public virtual bool Any(Expression<Func<T, bool>> expression)
{
return _dbSet.Any(expression);
}
public virtual void Delete(T entity)
{
_dbSet.Remove(entity);
_db.Commit();
}
public virtual IEnumerable<T> All()
{
return _dbSet.ToList();
}
public virtual T Update(T entity, bool attachOnly = false)
{
_dbSet.Attach(entity);
_db.SetModified(entity);
if (!attachOnly) _db.Commit();
return entity;
}
#endregion
protected User GetCurrentUser()
{
return
_db.Set<User>().Find(HttpContext.Current != null ? ((User) HttpContext.Current.Session["User"]).Id : 1);
}
BaseTrackableEntityRepository -
public class BaseTrackableEntityRepository<T> : BaseRepository<T>, IBaseTrackableEntityRepository<T>
where T : TrackableEntity
{
private readonly IAppDb _db;
private readonly IDbSet<T> _teDB;
public BaseTrackableEntityRepository(IAppDb db)
: base(db)
{
_db = db;
_teDB = _db.Set<T>();
}
#region IBaseTrackableEntityRepository<T> Members
public virtual T SetDeleteFlag(int id)
{
var entity = _teDB.Find(id);
if (entity == null) return null; //throw exception
entity.IsDeleted = true;
entity.DateModified = DateTime.Now;
entity.ModifiedBy = GetCurrentUser();
return Update(entity);
}
public override IEnumerable<T> All()
{
return _teDB.Where(e => !e.IsDeleted).ToList();
}
public override T Add(T entity)
{
var curUser = GetCurrentUser();
entity.CreatedBy = curUser;
entity.ModifiedBy = curUser;
entity.DateCreated = DateTime.Now;
entity.DateModified = DateTime.Now;
entity.Active = true;
entity.IsDeleted = false;
_teDB.Add(entity);
_db.Commit();
return entity;
}
public override T Update(T entity, bool attachOnly = false)
{
InsertTeData(ref entity);
entity.ModifiedBy = GetCurrentUser();
entity.DateModified = DateTime.Now;
_teDB.Attach(entity);
_db.SetModified(entity);
if (!attachOnly) _db.Commit();
return entity;
}
public virtual T SetStatus(int id, bool status)
{
var entity = _teDB.Find(id);
if (entity == null) return null;
entity.Active = status;
return Update(entity);
}
#endregion
private void InsertTeData(ref T entity)
{
if (entity == null || entity == null) return;
var dbEntity = GetById(entity.Id);
if (dbEntity == null) return;
_db.Detach(dbEntity);
entity.CreatedBy = dbEntity.CreatedBy;
entity.DateCreated = dbEntity.DateCreated;
entity.ModifiedBy = dbEntity.ModifiedBy;
entity.DateModified = dbEntity.DateModified;
}
When I call context.SaveChanges() to update a specific product, the update is not registered in the database. I do not get any runtime error either. All I notice is that my product catalog is not updated. I still see the same values. When I run the debugger I notice that the connection state of the database is closed.
This is the class implementing the context.SaveChanges()
namespace SportsStore.Domain.Concrete
{
public class EFProductRepository : IProductRepository
{
private EFDbContext context = new EFDbContext();
public IQueryable<Product> Products
{
get { return context.Products; }
}
public void SaveProduct(Product product)
{
if (product.ProductID == 0)
{
context.Products.Add(product);
}
context.SaveChanges();
}
}
}
namespace SportsStore.Domain.Concrete
{
public class EFDbContext : DbContext
{
public DbSet<Product> Products { get; set; }
}
}
namespace SportsStore.Domain.Entities
{
public class Product
{
[HiddenInput(DisplayValue=false)]
public int ProductID { get; set; }
public string Name { get; set; }
[DataType(DataType.MultilineText)]
public string Description { get; set; }
public string Category { get; set; }
public decimal Price { get; set; }
}
}
In the EFProductRepository class, prior to calling the context.SaveChanges() method in the SaveProduct method, you can use one of the following approaches to persist changes to the database.
public void SaveProduct(Product product)
{
if (product.ProductID == 0)
{
context.Products.Add(product);
}
//One approach to persist changes to database
//var productInDB = context.Products.Single(x => x.ProductID ==product.ProductID);
//context.Entry(productInDB).CurrentValues.SetValues(product);
//context.SaveChanges();
//Alternate Approach
if (product.ProductID != 0)
{
context.Entry(product).State = System.Data.EntityState.Modified;
}
context.SaveChanges();
}
I'm trying to persist a very simple username/password combo in WP7 7.1 beta 2 using LINQ to SQL. So far everything works as expected (using the repository/model classes below)
I can create a new database, insert a record and query for that row (and it returns the persisted user no problem). But the next time I spin up the emulator the db context returns false when I exec this expression "db.DatabaseExists()"
Is this normal behavior for the emulator or am I not telling LINQ to persist this between sessions?
Thank you in advance
repository class
public class UserRepository : IDisposable
{
private readonly UserDataContext context;
public UserDataContext Context
{
get { return context; }
}
public UserRepository()
{
context = new UserDataContext(UserDataContext.DBConnectionString);
CreateDatabase();
}
private void CreateDatabase()
{
if (context.DatabaseExists() == false)
{
context.CreateDatabase();
}
}
public User GetByID(int id)
{
return context.GetTable<User>().FirstOrDefault(e => e.UserId.Equals(id));
}
public User GetByUsername(string username)
{
return context.GetTable<User>().FirstOrDefault(e => e.UserName.Equals(username));
}
public void Save(User user)
{
if (user.UserId > 0)
{
context.GetTable<User>().Attach(user, true);
}
else
{
context.GetTable<User>().InsertOnSubmit(user);
}
context.SubmitChanges();
}
public List<User> GetAll()
{
return context.GetTable<User>().ToList();
}
public int Count()
{
return context.GetTable<User>().Count();
}
public void Dispose()
{
if (context != null)
{
context.Dispose();
}
}
}
model
[Table]
public class User
{
[Column(IsPrimaryKey = true, IsDbGenerated = true, DbType = "INT NOT NULL Identity", CanBeNull = false, AutoSync = AutoSync.OnInsert)]
public int UserId { get; set; }
[Column]
public string UserName { get; set; }
[Column]
public string Password { get; set; }
}
context
public class UserDataContext : DataContext
{
public static string DBConnectionString = "Data Source=isostore:/User.sdf";
public UserDataContext(string connectionString) : base(connectionString) { }
public Table<User> Users;
}
The Isolated Storage Explorer lets you create and restore snapshots of the Emulator's isolated storage. You might want to explore using this to save the state of your database prior to closing down the emulator, and then restoring it once you restart the emulator.
http://msdn.microsoft.com/en-us/library/hh286408(v=vs.92).aspx
This is expected behavior.
The database (and other data) is stored in Isolated Storage on the emulator. The emulator clears Isolated Storage on shutdown (see the first Note in MSDN here), so your database is deleted.