In my mvc application i have a base model as below
namespace ModulericaV1.Models
{
public class BaseModel
{
private ApplicationDbContext db = new ApplicationDbContext();
public DateTime? CrDate { get; set; }
[ForeignKey("CrUser")]
public ApplicationUser UserCr { get; set; }
public string CrUser { get; set; }
public DateTime? MdDate { get; set; }
[ForeignKey("MdUser")]
public ApplicationUser UserMd { get; set; }
public string MdUser { get; set; }
public bool IsDeleted { get; set; }
public ApplicationUser GetUserObject(string id)
{
var UserObject = db.Users.Find(id);
return UserObject;
}
public void LogBasic()
{
if (this.CrDate == null)
{
this.CrDate = System.DateTime.Now;
this.CrUser = HttpContext.Current.User.Identity.GetUserId();
}
else
{
this.MdDate = System.DateTime.Now;
this.MdUser = HttpContext.Current.User.Identity.GetUserId();
}
}
}
}
All my models inherits the :BaseModel. What i am trying to do, calling LogBasic() method for all Create and Update DB queries so i can log which user create new row or made changes.
Where is the place should i need to call LogBasic() method .
Probably something like this should work:
public class ApplicationDbContext : DbContext {
// ...
public override int SaveChanges () {
if(HasChanges()){
foreach(var entry in Entries<BaseModel>()){
((BaseModel)entry.Entity).LogBasic();
}
}
}
// ...
}
The best place to do this is in the ApplicationDbContext on the SaveChanges() method. You can iterate through all of the items that are pending updates and then if they are of a BaseModel, log them.
I don't have the exact syntax to hand, but this is the idea:
public class ApplicationDbContext : DbContext
{
//.....
public override int SaveChanges()
{
foreach(var entity in this.ChangeTracker.Entries)
{
var baseModel = entity as BaseModel;
if(baseModel!=null)
{
switch(entity.State)
{
case State.Modified;
// Do things with an updated version
case State.Deleted:
// Do things with the deleted version
case State.Added:
{
// Do things with the Added entity
baseModel.LogBasic();
break;
}
}
}
base.SaveChanges();
}
}
Related
I got dbset for table Functions in database and FunctionsContext: dbContext. I am implementing repository. In my interface I have only one function at the movement "GetFunctions". I got stuck in implementing class; method "GetFunctions" where I need to call FunctionsContext to get all list of available functions title from database and then send to controller class
I am using mvc5 asp.net and entity framework
dbContext
public class FunctionsContext : dbContext
{
public DbSet<App_Functions> Functions { get; set; }
}
model
[Table("Functions")]
public class App_Functions
{
[Key]
public int Function_ID { get; set; }
[StringLength(50)]
[Required]
public string Title { get; set; }
public int Hierarchy_level { get; set; }
}
Domain Class
public class Functions
{
public Functions()
{
}
public int Function_ID { get; set; }
public string Title { get; set; }
public int Hierarchy_level { get; set; }
}
IRepository
interface IFunctionRepository: IDisposable
{
IQueryable<Functions> GetFunctions { get; }
}
IRepository Implementation class
public class FunctionRepository : IFunctionRepository
{
private FunctionsContext fun_Context = new FunctionsContext();
public IQueryable<Functions>GetFunctions
{
?????????
}
}
what I want to implement in IQueryableGetFunctions
using (var db = new FunctionsContext())
{
var query = from b in db.Functions
orderby b.Function_ID
select b;
foreach (var item in query)
{
var a2 = item.Title;
}
}
I think the easiest way will be the following:
public IQueryable<Functions> GetFunctions()
{
return fun_Context.Functions.Select(x=>new Functions {
Function_ID = x.Function_ID,
Title = x.Title,
Hierarchy_level = x.Hierarchy_level
});
}
You have to add () after the method name, this declaration does not work 'public IQueryable GetFunctions'
IRepository Implementation class
public class FunctionRepository : IFunctionRepository
{
private FunctionsContext fun_Context = new FunctionsContext();
// For method declaration add the () after the method name
public IQueryable<Functions> GetFunctions()
{
return fun_Context.Functions;
}
}
I am trying to extend the UnitOfWork pattern that I used for a number of MVC 4 applications to MVC 5 whilst also using the new IdentityDbContext and things are not working out. The problem is its very hard to debug as no errors are being generated.
First, some code.
I have a context definition that looks as follows. I have added my own DbSets to the out of the box IdentityDbContext as it makes sense to keep everything in one place.
public class ApplicationDbContext : IdentityDbContext<ApplicationUser>
{
public DbSet<PALSOfficer> PALSOfficers { get; set; }
public DbSet<Client> Clients { get; set; }
public DbSet<GP> GPs { get; set; }
public DbSet<Surgery> Surgeries { get; set; }
public DbSet<Disability> Disabilities { get; set; }
public DbSet<Area> Areas { get; set; }
public DbSet<PALSReferral> PALSReferrals { get; set; }
public DbSet<Appointment> Appointments { get; set; }
public ApplicationDbContext()
: base("DefaultConnection")
{
}
}
I then have a UnitOfWork class that looks as follows:
public class UnitOfWork : IDisposable
{
private bool _disposed = false;
private ApplicationDbContext _context = new ApplicationDbContext();
public UserManager<ApplicationUser> _userManager { get; set; }
private PalsOfficerRepository _palsOfficerRepository;
private UserRepository _userRepository;
private GenericRepository<Area> _areaRepository;
public UserRepository UserRepository
{
get
{
if (this._userRepository == null)
{
this._userRepository = new UserRepository(_context);
}
return _userRepository;
}
}
public PalsOfficerRepository PalsOfficerRepository
{
get
{
if (this._palsOfficerRepository == null)
{
this._palsOfficerRepository = new PalsOfficerRepository(_context);
}
return _palsOfficerRepository;
}
}
public GenericRepository<Area> AreaRepository
{
get
{
if (this._areaRepository == null)
{
this._areaRepository = new GenericRepository<Area>(_context);
}
return _areaRepository;
}
}
public UserManager<ApplicationUser> UserManager
{
get
{
if (this._userManager == null)
{
this._userManager = new UserManager<ApplicationUser>(new UserStore<ApplicationUser>(_context));
}
return _userManager;
}
}
public void Save()
{
try
{
_context.SaveChanges();
}
catch (DbEntityValidationException dbEx)
{
foreach (var validationErrors in dbEx.EntityValidationErrors)
{
foreach (var validationError in validationErrors.ValidationErrors)
{
Trace.TraceInformation("Property: {0} Error: {1}", validationError.PropertyName, validationError.ErrorMessage);
}
}
throw;
}
}
protected virtual void Dispose(bool disposing)
{
if (disposing)
{
if (this._userManager != null)
{ this._userManager.Dispose(); }
_userManager.Dispose();
}
this._disposed = true;
}
public void Dispose()
{
Dispose(true);
GC.SuppressFinalize(this);
}
}
So far so good. I am using code first to generate my database with automatic migrations.
My problem is that if I try to query the database, odd things happen. Take this method for instance.
var results = new SearchResults<PALSOfficer>();
var officers = from o in Context.PALSOfficers
select o;
if (!string.IsNullOrEmpty(keyword))
{
officers = (from o in officers
where o.FirstName.Contains(keyword) || o.LastName.Contains(keyword)
select o);
}
officers = officers.OrderBy(p => p.LastName);
results.Total = officers.Count();
int offset = page * display;
results.ResultList = results.Total > offset ? officers.Skip(offset).Take(display) : officers;
//results.ResultList = Context.PALSOfficers;
return results;
This returns nothing even though the database contains data. Weirdly, the count will work. If I put a breakpoint in and mouseover the results I get the message that 'children could not be evaluated'
If I simply return Context.PALSOfficers I get the rows back. Any type of manipulation of that data though (sorting etc) seems to break the query entirely.
Here is the definition of PALSOfficer
public class ApplicationUser : IdentityUser
{
public string FirstName { get; set; }
public string LastName { get; set; }
public string Email { get; set; }
public DateTime Added { get; set; }
public DateTime Updated { get; set; }
}
public class PALSOfficer : ApplicationUser
{
public string InternalReference { get; set; }
public virtual ICollection<Area> Areas { get; set; }
}
I have a very simple example I am trying to set up with the following schema...
public class Foo
{
public int Id { get; set; }
public string Name { get; set; }
public string SomethingVeryBig { get; set; }
public List<Bar> Bars { get; set; }
}
public class Bar
{
public int Id { get; set; }
public int FooId { get; set; }
public string Name { get; set; }
}
What I am trying to test is using Breeze in a way disconnected from my data repository, so I am hand coding from the Fluent API my DBContext. Context code below, "FoosDb" is just an sdf file deployed with the project for the Breeze Metadata and is not a real database we are saving data into.
public class FoosDbContext : DbContext
{
public FoosDbContext() : base(nameOrConnectionString: "FoosDb")
{
Database.SetInitializer<FoosDbContext>(null);
}
public DbSet<Foo> Foos { get; set; }
public DbSet<Bar> Bars { get; set; }
public DbSet<Link> Links { get; set; }
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
modelBuilder.Entity<Foo>().HasKey(f => f.Id);
modelBuilder.Entity<Bar>().HasKey(b => b.Id);
modelBuilder.Entity<Foo>().HasMany(f => f.Bars).WithRequired().HasForeignKey(b => b.FooId);
}
}
public class FoosContextProvider : EFContextProvider<FoosDbContext>
{
public FoosContextProvider() : base() { }
protected override List<KeyMapping> SaveChangesCore(Dictionary<Type, List<EntityInfo>> saveMap)
{
return new List<KeyMapping>();
}
protected override bool BeforeSaveEntity(EntityInfo entityInfo)
{
return true;
}
protected override Dictionary<Type, List<EntityInfo>> BeforeSaveEntities(Dictionary<Type, List<EntityInfo>> saveMap)
{
// return a map of those entities we want saved.
return saveMap;
}
}
Everything works great and I am testing all CRUD operations via a project from the Hot Towel Template, but when I query Foos from my controller the json data looks perfect, but when it gets transferred to Breeze/Knockout Observables the data in each "Foo.Bars" list is wrong. It is taking Bar.Id = 1 and always putting that on Foo.Id = 1, Bar.Id = 2 and putting that on Foo.Id = 2, and so on. Even though in my example Bar.Id = 2 should be on Foo.Id = 1.
I have tried many different ways and looked at different posts, but still haven't come across a solution for this way of auditing. Below is my DBContext template file. I customised it by adding the OnContextCreated() partial method and assign the SavingChanges event to my OnSavingChanges event handler.
namespace ARSystem.Models
{
public partial class ARSEntities : ObjectContext
{
public ARSEntities()
: base("name=ARSEntities")
{
}
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
throw new UnintentionalCodeFirstException();
}
public string UserName { get; set; }
List<DBAudit> auditTrailList = new List<DBAudit>();
public enum AuditActions
{
I,
U,
D
}
partial void OnContextCreated()
{
this.SavingChanges += new EventHandler(OnSavingChanges);
}
public void OnSavingChanges(object sender, EventArgs e)
{
IEnumerable<ObjectStateEntry> changes = this.ObjectStateManager.GetObjectStateEntries(EntityState.Added | EntityState.Deleted | EntityState.Modified);
foreach (ObjectStateEntry stateEntryEntity in changes)
{
if (!stateEntryEntity.IsRelationship &&
stateEntryEntity.Entity != null &&
!(stateEntryEntity.Entity is DBAudit))
{//is a normal entry, not a relationship
DBAudit audit = this.AuditTrailFactory(stateEntryEntity, UserName);
auditTrailList.Add(audit);
}
}
if (auditTrailList.Count > 0)
{
foreach (var audit in auditTrailList)
{//add all audits
this.AddToDBAudit(audit);
}
}
}
private DBAudit AuditTrailFactory(ObjectStateEntry entry, string UserName)
{
DBAudit audit = new DBAudit();
audit.AuditId = Guid.NewGuid().ToString();
audit.RevisionStamp = DateTime.Now;
audit.TableName = entry.EntitySet.Name;
audit.UserName = UserName;
if (entry.State == EntityState.Added)
{//entry is Added
audit.NewData = GetEntryValueInString(entry, false);
audit.Actions = AuditActions.I.ToString();
}
else if (entry.State == EntityState.Deleted)
{//entry in deleted
audit.OldData = GetEntryValueInString(entry, true);
audit.Actions = AuditActions.D.ToString();
}
else
{//entry is modified
audit.OldData = GetEntryValueInString(entry, true);
audit.NewData = GetEntryValueInString(entry, false);
audit.Actions = AuditActions.U.ToString();
IEnumerable<string> modifiedProperties = entry.GetModifiedProperties();
//assing collection of mismatched Columns name as serialized string
audit.ChangedColumns = XMLSerializationHelper.XmlSerialize(modifiedProperties.ToArray());
}
return audit;
}
private string GetEntryValueInString(ObjectStateEntry entry, bool isOrginal)
{
if (entry.Entity is EntityObject)
{
object target = CloneEntity((EntityObject)entry.Entity);
foreach (string propName in entry.GetModifiedProperties())
{
object setterValue = null;
if (isOrginal)
{
//Get orginal value
setterValue = entry.OriginalValues[propName];
}
else
{
//Get orginal value
setterValue = entry.CurrentValues[propName];
}
//Find property to update
PropertyInfo propInfo = target.GetType().GetProperty(propName);
//update property with orgibal value
if (setterValue == DBNull.Value)
{//
setterValue = null;
}
propInfo.SetValue(target, setterValue, null);
}//end foreach
XmlSerializer formatter = new XmlSerializer(target.GetType());
XDocument document = new XDocument();
using (XmlWriter xmlWriter = document.CreateWriter())
{
formatter.Serialize(xmlWriter, target);
}
return document.Root.ToString();
}
return null;
}
public EntityObject CloneEntity(EntityObject obj)
{
DataContractSerializer dcSer = new DataContractSerializer(obj.GetType());
MemoryStream memoryStream = new MemoryStream();
dcSer.WriteObject(memoryStream, obj);
memoryStream.Position = 0;
EntityObject newObject = (EntityObject)dcSer.ReadObject(memoryStream);
return newObject;
}
public DbSet<Student> Students { get; set; }
public DbSet<User> Users { get; set; }
public DbSet<aspnet_Applications> aspnet_Applications { get; set; }
public DbSet<aspnet_Membership> aspnet_Membership { get; set; }
public DbSet<aspnet_Roles> aspnet_Roles { get; set; }
public DbSet<aspnet_SchemaVersions> aspnet_SchemaVersions { get; set; }
public DbSet<aspnet_Users> aspnet_Users { get; set; }
public DbSet<vw_aspnet_Applications> vw_aspnet_Applications { get; set; }
public DbSet<vw_aspnet_MembershipUsers> vw_aspnet_MembershipUsers { get; set; }
public DbSet<vw_aspnet_Roles> vw_aspnet_Roles { get; set; }
public DbSet<vw_aspnet_Users> vw_aspnet_Users { get; set; }
public DbSet<vw_aspnet_UsersInRoles> vw_aspnet_UsersInRoles { get; set; }
public DbSet<Cours> Courses { get; set; }
public DbSet<Enrollment> Enrollments { get; set; }
public DbSet<Modules> Modules { get; set; }
public DbSet<EnrollmentsByCourse> EnrollmentsByCourse { get; set; }
public DbSet<EnrollmentsByCourseAudit> EnrollmentsByCourseAudit { get; set; }
public DbSet<DBAudit> DBAudit { get; set; }
}
}
However, when I compile, i get the error message that:
Error 1 'ARSystem.Models.ARSEntities.OnModelCreating(System.Data.Entity.DbModelBuilder)': no suitable method found to override C:\Users\mngum\Documents\Visual Studio 2010\Projects\ARSystem\ARSystem\Models\ARSystem.Context.cs 35 33 ARSystem
I cannot see the OnContextCreated method in the DBContext metadata class but i can find it in the edmx designer. Please let me know how i can implement the OnContextCreated() method such that I can override the SavingChanges event for auditing purposes.
DbContext does not have an OnContextCreated event but that's not a problem because you don't need it to achieve the same. Instead with DbContext the SaveChanges method is overridable. So instead of your OnSavingChanges event handler you use:
public override int SaveChanges()
{
// custom code...
return base.SaveChanges();
}
This method will be called whenever you call ARSEntities.SaveChanges() and you can perform custom actions before you call the base.SaveChanges() of the base DbContext (ARSEntities must be derived from DbContext of course.)
You also can access the underlying ObjectContext from the DbContext:
public override int SaveChanges()
{
var objectContext = ((IObjectContextAdapter)this).ObjectContext;
// use methods and properties of ObjectContext now like
// objectContext.ObjectStateManager, etc.
// custom code...
return base.SaveChanges();
}
Here was a similar question and answer about change auditing with EF 4.1/DbContext:
Entity Framework 4.1 DbContext Override SaveChanges to Audit Property Change
I'm trying out Entity Framework Code first CTP4. Suppose I have:
public class Parent
{
public int Id { get; set; }
public string Name { get; set; }
}
public class Child
{
public int Id { get; set; }
public string Name { get; set; }
public Parent Mother { get; set; }
}
public class TestContext : DbContext
{
public DbSet<Parent> Parents { get; set; }
public DbSet<Child> Children { get; set; }
}
public class ChildEdit
{
public int Id { get; set; }
public string Name { get; set; }
public int MotherId { get; set; }
}
Mapper.CreateMap<Child, ChildEdit>();
Mapping to the Edit model is not a problem. On my screen I select the mother through some control (dropdownlist, autocompleter, etc) and the Id of the mother gets posted in back:
[HttpPost]
public ActionResult Edit(ChildEdit posted)
{
var repo = new TestContext();
var mapped = Mapper.Map<ChildEdit, Child>(posted); // <------- ???????
}
How should I solve the last mapping? I don't want to put Mother_Id in the Child object. For now I use this solution, but I hope it can be solved in Automapper.
Mapper.CreateMap<ChildEdit, Child>()
.ForMember(i => i.Mother, opt => opt.Ignore());
var mapped = Mapper.Map<ChildEdit, Child>(posted);
mapped.Mother = repo.Parents.Find(posted.MotherId);
EDIT
This works, but now I have to do that for each foreign key (BTW: context would be injected in final solution):
Mapper.CreateMap<ChildEdit, Child>();
.ForMember(i => i.Mother,
opt => opt.MapFrom(o =>
new TestContext().Parents.Find(o.MotherId)
)
);
What I'd really like would be:
Mapper.CreateMap<int, Parent>()
.ForMember(i => i,
opt => opt.MapFrom(o => new TestContext().Parents.Find(o))
);
Mapper.CreateMap<ChildEdit, Child>();
Is that possible with Automapper?
First, I'll assume that you have a repository interface like IRepository<T>
Afterwards create the following class:
public class EntityConverter<T> : ITypeConverter<int, T>
{
private readonly IRepository<T> _repository;
public EntityConverter(IRepository<T> repository)
{
_repository = repository;
}
public T Convert(ResolutionContext context)
{
return _repository.Find(System.Convert.ToInt32(context.SourceValue));
}
}
Basically this class will be used to do all the conversion between an int and a domain entity. It uses the "Id" of the entity to load it from the Repository. The IRepository will be injected into the converter using an IoC container, but more and that later.
Let's configure the AutoMapper mapping using:
Mapper.CreateMap<int, Mother>().ConvertUsing<EntityConverter<Mother>>();
I suggest creating this "generic" mapping instead so that if you have other references to "Mother" on other classes they're mapped automatically without extra-effort.
Regarding the Dependency Injection for the IRepository, if you're using Castle Windsor, the AutoMapper configuration should also have:
IWindsorContainer container = CreateContainer();
Mapper.Initialize(map => map.ConstructServicesUsing(container.Resolve));
I've used this approach and it works quite well.
Here's how I did it: (using ValueInjecter)
I made the requirements a little bigger just to show how it works
[TestFixture]
public class JohnLandheer
{
[Test]
public void Test()
{
var child = new Child
{
Id = 1,
Name = "John",
Mother = new Parent { Id = 3 },
Father = new Parent { Id = 9 },
Brother = new Child { Id = 5 },
Sister = new Child { Id = 7 }
};
var childEdit = new ChildEdit();
childEdit.InjectFrom(child)
.InjectFrom<EntityToInt>(child);
Assert.AreEqual(1, childEdit.Id);
Assert.AreEqual("John", childEdit.Name);
Assert.AreEqual(3, childEdit.MotherId);
Assert.AreEqual(9, childEdit.FatherId);
Assert.AreEqual(5, childEdit.BrotherId);
Assert.AreEqual(7, childEdit.SisterId);
Assert.AreEqual(0, childEdit.Sister2Id);
var c = new Child();
c.InjectFrom(childEdit)
.InjectFrom<IntToEntity>(childEdit);
Assert.AreEqual(1, c.Id);
Assert.AreEqual("John", c.Name);
Assert.AreEqual(3, c.Mother.Id);
Assert.AreEqual(9, c.Father.Id);
Assert.AreEqual(5, c.Brother.Id);
Assert.AreEqual(7, c.Sister.Id);
Assert.AreEqual(null, c.Sister2);
}
public class Entity
{
public int Id { get; set; }
}
public class Parent : Entity
{
public string Name { get; set; }
}
public class Child : Entity
{
public string Name { get; set; }
public Parent Mother { get; set; }
public Parent Father { get; set; }
public Child Brother { get; set; }
public Child Sister { get; set; }
public Child Sister2 { get; set; }
}
public class ChildEdit
{
public int Id { get; set; }
public string Name { get; set; }
public int MotherId { get; set; }
public int FatherId { get; set; }
public int BrotherId { get; set; }
public int SisterId { get; set; }
public int Sister2Id { get; set; }
}
public class EntityToInt : LoopValueInjection
{
protected override bool TypesMatch(Type sourceType, Type targetType)
{
return sourceType.IsSubclassOf(typeof(Entity)) && targetType == typeof(int);
}
protected override string TargetPropName(string sourcePropName)
{
return sourcePropName + "Id";
}
protected override bool AllowSetValue(object value)
{
return value != null;
}
protected override object SetValue(object sourcePropertyValue)
{
return (sourcePropertyValue as Entity).Id;
}
}
public class IntToEntity : LoopValueInjection
{
protected override bool TypesMatch(Type sourceType, Type targetType)
{
return sourceType == typeof(int) && targetType.IsSubclassOf(typeof(Entity));
}
protected override string TargetPropName(string sourcePropName)
{
return sourcePropName.RemoveSuffix("Id");
}
protected override bool AllowSetValue(object value)
{
return (int)value > 0;
}
protected override object SetValue(object sourcePropertyValue)
{
// you could as well do repoType = IoC.Resolve(typeof(IRepo<>).MakeGenericType(TargetPropType))
var repoType = typeof (Repo<>).MakeGenericType(TargetPropType);
var repo = Activator.CreateInstance(repoType);
return repoType.GetMethod("Get").Invoke(repo, new[] {sourcePropertyValue});
}
}
class Repo<T> : IRepo<T> where T : Entity, new()
{
public T Get(int id)
{
return new T{Id = id};
}
}
private interface IRepo<T>
{
T Get(int id);
}
}
It's possible to define the foreign key in EF this way as well:
[ForeignKey("MotherId")]
public virtual Parent Mother { get; set; }
public int MotherId { get; set; }
In this case, It's not necessary to do an extra query to find the Mother. Just Assign the ViewModel's MotherId to the Model's MotherId.