I have these classes:
public class Items
{
[Key]
public Guid Id { get; set; }
public string ItemCode { get; set; }
public decimal SalesPriceExcl { get; set; }
public decimal SalesPriceIncl { get; set; }
public virtual ICollection<ItemPrice> SalesPrices { get; set; }
public Items()
{
SalesPrices = new HashSet<App4Sales_ItemPrice>();
}
}
public class ItemPrice
{
[Key, Column(Order = 0), ForeignKey("Items")]
public Guid Id { get; set; }
public virtual Items Items { get; set; }
[Key, Column(Order=1)]
public Guid PriceList { get; set; }
public decimal PriceExcl { get; set; }
public decimal PriceIncl { get; set; }
public decimal VatPercentage { get; set; }
}
I want to query the Items and automatically get the ItemPrice collection.
I've created an OData V3 controller:
// GET: odata/Items
//[Queryable]
public IQueryable<Items> GetItems(ODataQueryOptions opts)
{
SelectExpandQueryOption expandOpts = new SelectExpandQueryOption(null, "SalesPrices", opts.Context);
Request.SetSelectExpandClause(expandOpts.SelectExpandClause);
return expandOpts.ApplyTo(db.Items.AsQueryable(), new ODataQuerySettings()) as IQueryable<Items>;
}
But I get the error:
"Cannot serialize null feed"
Yes, some Items have no ItemPrice list.
Can I get past this error, or can I do something different?
Kind regards
Jeroen
I found the underlying error is:
Unable to cast object of type
'System.Data.Entity.Infrastructure.DbQuery1[System.Web.Http.OData.Query.Expressions.SelectExpandBinder+SelectAllAndExpand1[.Models.Items]]'
to type '.Models.Items'.
I've solved it after I came across this post: http://www.jauernig-it.de/intercepting-and-post-processing-odata-queries-on-the-server/
This is my controller now:
SelectExpandQueryOption expandOpts = new SelectExpandQueryOption(null, "SalesPrices", opts.Context);
Request.SetSelectExpandClause(expandOpts.SelectExpandClause);
var result = expandOpts.ApplyTo(db.Items.AsQueryable(), new ODataQuerySettings());
var resultList = new List<Items>();
foreach (var item in result)
{
if (item is Items)
{
resultList.Add((Items)item);
}
else if (item.GetType().Name == "SelectAllAndExpand`1")
{
var entityProperty = item.GetType().GetProperty("Instance");
resultList.Add((Items)entityProperty.GetValue(item));
}
}
return resultList.AsQueryable();
Jeroen
GetItems([FromODataUri] ODataQueryOptions queryOptions)
expanding on Jeroen's post. Anytime a select or expand is involved, OData wraps the results in a SelectAll or SelectSome object; so, we need to unwrap the values rather than do an direct cast.
public static class ODataQueryOptionsExtensions
{
public static IEnumerable<T> ApplyODataOptions<T>(this IQueryable<T> query, ODataQueryOptions options) where T : class, new()
{
if (options == null)
{
return query;
}
var queryable = options.ApplyTo(query);
if (queryable is IQueryable<T> queriableEntity)
{
return queriableEntity.AsEnumerable();
}
return UnwrapAll<T>(queryable).ToList();
}
public static IEnumerable<T> UnwrapAll<T>(this IQueryable queryable) where T : class, new()
{
foreach (var item in queryable)
{
yield return Unwrap<T>(item);
}
}
public static T Unwrap<T>(object item) where T : class, new()
{
var instanceProp = item.GetType().GetProperty("Instance");
var value = (T)instanceProp.GetValue(item);
if (value != null)
{
return value;
}
value = new T();
var containerProp = item.GetType().GetProperty("Container");
var container = containerProp.GetValue(item);
if (container == null)
{
return (T)null;
}
var containerType = container.GetType();
var containerItem = container;
var allNull = true;
for (var i = 0; containerItem != null; i++)
{
var containerItemType = containerItem.GetType();
var containerItemValue = containerItemType.GetProperty("Value").GetValue(containerItem);
if (containerItemValue == null)
{
containerItem = containerType.GetProperty($"Next{i}")?.GetValue(container);
continue;
}
var containerItemName = containerItemType.GetProperty("Name").GetValue(containerItem) as string;
var expandedProp = typeof(T).GetProperty(containerItemName);
if (expandedProp.SetMethod == null)
{
containerItem = containerType.GetProperty($"Next{i}")?.GetValue(container);
continue;
}
if (containerItemValue.GetType() != typeof(string) && containerItemValue is IEnumerable containerValues)
{
var listType = typeof(List<>).MakeGenericType(expandedProp.PropertyType.GenericTypeArguments[0]);
var expandedList = (IList)Activator.CreateInstance(listType);
foreach (var expandedItem in containerValues)
{
var expandedInstanceProp = expandedItem.GetType().GetProperty("Instance");
var expandedValue = expandedInstanceProp.GetValue(expandedItem);
expandedList.Add(expandedValue);
}
expandedProp.SetValue(value, expandedList);
allNull = false;
}
else
{
var expandedInstanceProp = containerItemValue.GetType().GetProperty("Instance");
if (expandedInstanceProp == null)
{
expandedProp.SetValue(value, containerItemValue);
allNull = false;
}
else
{
var expandedValue = expandedInstanceProp.GetValue(containerItemValue);
if (expandedValue != null)
{
expandedProp.SetValue(value, expandedValue);
allNull = false;
}
else
{
var t = containerItemValue.GetType().GenericTypeArguments[0];
var wrapInfo = typeof(ODataQueryOptionsExtensions).GetMethod(nameof(Unwrap));
var wrapT = wrapInfo.MakeGenericMethod(t);
expandedValue = wrapT.Invoke(null, new[] { containerItemValue });
if (expandedValue != null)
{
expandedProp.SetValue(value, expandedValue);
allNull = false;
}
}
}
}
containerItem = containerType.GetProperty($"Next{i}")?.GetValue(container);
}
if (allNull)
{
return (T)null;
}
return value;
}
}
Related
how i can add a tag link to every node(root or children) in JTree, I Fetch data from database with EFCore
and i want to every node have link like this:
<a class="btn btn-primary" asp-action="TaskTypeDetail" asp-controller="Admin" asp-route-TaskTypeNumber=#item.TaskTypeNumber>details</a>
mycontroller like this :
public class TreeviewController : Controller
{
private readonly RoleManager<IdentityRole> roleManager;
private readonly UserManager<ApplicationUser> userManager;
private readonly ApplicationDbContext applicationDbContext;
public TreeviewController(RoleManager<IdentityRole> roleManager, UserManager<ApplicationUser> userManager, ApplicationDbContext applicationDbContext)
{
this.roleManager = roleManager;
this.userManager = userManager;
this.applicationDbContext = applicationDbContext;
}
public IActionResult Index()
{
return View();
}
public JsonResult GetRoot()
{
List<JsTreeModel> items = GetTree();
return new JsonResult ( items );
}
public JsonResult GetChildren(string id)
{
List<JsTreeModel> items = GetTree(id);
return new JsonResult ( items );
}
public List<JsTreeModel> GetTree()
{
bool checkchildren;
var items = new List<JsTreeModel>();
foreach (var role in roleManager.Roles)
{
foreach (var user in userManager.Users)
{
checkchildren = false;
var checkUserInRole = userManager.IsInRoleAsync(user, role.Name).Result;
if (checkUserInRole)
{
var checkEmployeeForUser = applicationDbContext.EmployeeInRoles.Where(s => s.RoleId == role.Id).ToList();
if (checkEmployeeForUser.Count > 0)
{
checkchildren = true;
}
items.Add(new JsTreeModel { id = user.Id.ToString(), parent = "#", text = user.Name+" "+user.Family+" ریشه " , children = checkchildren,a_attr=""});
}
}
}
// set items in here
return items;
}
public List<JsTreeModel> GetTree(string id)
{
var items = new List<JsTreeModel>();
// set items in here
//Loop and add the Child Nodes.
bool checkchildren;
string Role="";
foreach (var subType in applicationDbContext.EmployeeInRoles)
{
checkchildren = false;
string Parentid = "";
foreach (var findParent in userManager.Users)
{
var roleid = roleManager.FindByIdAsync(subType.RoleId);
var checkParent = userManager.IsInRoleAsync(findParent, roleid.Result.Name).Result;
if (checkParent) {
Parentid = findParent.Id;
}
}
var user = userManager.Users.SingleOrDefault(s => s.Id == subType.UserId);
foreach(var role in roleManager.Roles)
{
var checkUserInRole = userManager.IsInRoleAsync(user, role.Name).Result;
if (checkUserInRole)
{
Role = role.Id;
break;
}
}
var checkEmployeeForUser = applicationDbContext.EmployeeInRoles.Where(s => s.RoleId == Role).ToList();
if (checkEmployeeForUser.Count > 0)
{
checkchildren = true;
}
items.Add(new JsTreeModel { id = subType.Id.ToString(), parent = Parentid, text = user.Name + " " + user.Family, children=checkchildren});
}
return items;
}
}
JsTreeModel like this:
public class JsTreeModel
{
public string id { get; set; }
public string parent { get; set; }
public string text { get; set; }
public string a_attr { get; set; }
public bool children { get; set; } // if node has sub-nodes set true or not set false
}
and Index.cshtml
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/1.12.1/jquery.min.js"></script>
<div id='treeview'></div>
<script>
$('#treeview').jstree({
"plugins": ["search","contextmenu"],
'core': {
'data': {
'url': function (node) {
return node.id === '#' ? "/Treeview/GetRoot" : "/Treeview/GetChildren/" + node.id;
},
'data': function (node) {
return {'id': node.id };
}
}
}
});
$('#treeview').on('changed.jstree', function (e, data) {
console.log("=> selected node: " + data.node.id);
});
but every node that show me, when I click on it, doesn't have link and # instead of link
can any one help me ?
I have this ChainableSelect component which is select and based on that it generates another ChainableSelect and on and on.
I noticed that if i changed the selection like around 7~10 times
memory consumption starts to rise without stopping causing my laptop to freeze
To Reproduce
Steps to reproduce the behavior:
Using this version of ASP.NET Core '3.0' release
Run this code
public class ChainableSelect<TEntity> : InputableBaseComponent where TEntity : DbModel
{
[Parameter]
public IChainableSelectDataFetcher<TEntity> DataFetcher { get; set; }
[Parameter]
public string FilterText { get; set; } = "";
[Parameter]
public bool IsBusy { get; set; }
[Parameter]
public bool FilterChanged { get; set; } = true;
[Parameter]
public string ParentPropertyName { get; set; }
[Parameter]
public TEntity ParentEntity { get; set; }
TEntity _localParentEntity;
IViewProperty _localViewProperty;
ReadOnlyObservableCollection<TEntity> _entities { get; set; }
bool _updateEntities = true;
protected override void OnInitialized()
{
base.OnInitialized();
_localViewProperty = ViewPropertyFactory.CreateEmptyProperty("", null);
}
protected override async Task OnParametersSetAsync()
{
await base.OnParametersSetAsync();
if (DataFetcher != null)
{
if (_localParentEntity != ParentEntity || ParentEntity == null || FilterChanged)
{
_entities = await DataFetcher.GetDescendents(ParentEntity, ParentPropertyName);
_updateEntities = false;
}
_localViewProperty.PropertyValue = null;
FilterChanged = true;
StateHasChanged();
}
_localParentEntity = ParentEntity;
}
protected override void BuildRenderTree(RenderTreeBuilder builder)
{
if (_entities == null)
return;
if (_entities.Count == 0)
return;
var seq = 0;
base.BuildRenderTree(builder);
builder.OpenElement(seq, "input");
builder.AddAttribute(seq, "class", "form-control form-control-sm dropdown-input bg-white");
builder.AddAttribute(seq, "value", _localViewProperty.PropertyValue ?? "--SELECT--");
builder.AddAttribute(seq, "readonly", "readonly");
builder.AddAttribute(seq, "onfocus", "document.getElementById('" + Id + "').style.zIndex=1000");
builder.CloseElement();
builder.OpenElement(++seq, "div");
builder.AddAttribute(seq, "class", "dropdown-div border");
builder.AddAttribute(seq, "onmouseover", "document.getElementById('" + Id + "').style.zIndex=1000");
builder.AddAttribute(seq, "onmouseout", "document.getElementById('" + Id + "').style.zIndex=1");
builder.OpenElement(++seq, "div");
builder.AddAttribute(seq, "class", "p-1 m-1 bg-white");
builder.OpenElement(++seq, "input");
builder.AddAttribute(seq, "class", "form-control form-control-sm");
builder.AddAttribute(seq, "oninput", new Action<ChangeEventArgs>(OnFilterChanged));
builder.CloseElement();
builder.OpenElement(++seq, "hr");
builder.CloseElement();
builder.CloseElement();
builder.OpenElement(++seq, "a");
builder.AddAttribute(seq, "onclick", new Action(() => OnSelect(null)));
builder.AddContent(seq, "None");
builder.CloseElement();
if (_entities != null)
foreach (var _entity in _entities)
{
if (!_entity.ToString().Contains(FilterText, StringComparison.InvariantCultureIgnoreCase))
continue;
builder.OpenElement(++seq, "a");
builder.AddAttribute(seq, "onclick", new Action(() => OnSelect(_entity)));
if (_entity == _localViewProperty.PropertyValue)
builder.AddAttribute(seq, "class", "selected");
builder.AddContent(seq, _entity.ToString());
builder.CloseElement();
}
builder.CloseElement();
builder.CloseElement();
FilterChanged = false;
if (_localViewProperty.PropertyValue != null && _localViewProperty.PropertyValue != ParentEntity)
{
builder.OpenComponent<ChainableSelect<TEntity>>(++seq);
builder.AddAttribute(seq, "Parameters", Parameters);
builder.AddAttribute(seq, "ParentEntity", ViewProperty.PropertyValue);
builder.AddAttribute(seq, "ParentPropertyName", ParentPropertyName);
builder.CloseComponent();
}
}
private async void OnSelect(TEntity entity)
{
ViewProperty.PropertyValue = entity;
_localViewProperty.PropertyValue = entity;
FilterChanged = true;
_updateEntities = false;
StateHasChanged();
}
protected override bool ShouldRender()
{
return !IsBusy && FilterChanged;
}
CancellationTokenSource _cancellationTokenSource = new CancellationTokenSource();
private async void OnFilterChanged(ChangeEventArgs e)
{
_cancellationTokenSource.Cancel();
_cancellationTokenSource = new CancellationTokenSource();
var _cancelationToken = _cancellationTokenSource.Token;
IsBusy = true;
await Task.Delay(200, _cancelationToken).ContinueWith(t =>
{
if (!_cancelationToken.IsCancellationRequested)
{
FilterText = e.Value.ToString();
FilterChanged = true;
}
IsBusy = false;
InvokeAsync(StateHasChanged);
});
}
}
ChainableDataFetcher
public class ChainableEntityDataFetcher<TEntity> : IChainableSelectDataFetcher<TEntity> where TEntity : DbModel
{
public Func<TEntity, string, Task<ReadOnlyObservableCollection<TEntity>>> GetDescendents { get; set; }
private readonly IDataAccessLayer<TEntity> _dataAccessLayer;
private PropertyInfo _parentProperty;
public ChainableEntityDataFetcher(IDataAccessLayer<TEntity> dataAccessLayer)
{
_dataAccessLayer = dataAccessLayer;
GetDescendents = DataFetcherAsync;
}
public void Dispose()
{
_dataAccessLayer.Dispose();
GetDescendents = null;
_parentProperty = null;
}
async Task<ReadOnlyObservableCollection<TEntity>> DataFetcherAsync(TEntity parentEntity,string parentPropertyName)
{
var _comparableValue = parentEntity == null ? " is NULL" : " = " + parentEntity.DbModelId.ToString();
var _dataList = _dataAccessLayer.GetEntities(new Dapper.CommandDefinition(String.Format("select * from {0} where {1}{2}",
_dataAccessLayer.TableName,
parentPropertyName,
_comparableValue)));
ObservableCollection<TEntity> _entities = new ObservableCollection<TEntity>(_dataList);
ReadOnlyObservableCollection<TEntity> _readOnlyEntites = new ReadOnlyObservableCollection<TEntity>(_entities);
return _readOnlyEntites;
}
}
DataAccessLayer
public class DataAccessLayer<TEntity> : IDataAccessLayer<TEntity> where TEntity : DbModel
{
public IDbConnectionBuilder DbConnectionBuilder { get; }
private readonly string _tableName;
...
public ICollection<TEntity> GetEntities(CommandDefinition command)
{
using (var _connection = DbConnectionBuilder.GetDbConnection())
{
_connection.Open();
return _connection.Query<TEntity>(command).ToList();
}
}
...
Screenshots
I found the problem..
It is with the async StateHasChanged
Just don’t use StateHasChanges in an async operation
I'm trying to figure out how to pull values from a SQL database and display this in a razor view.
I have the following class using Entity Framework (I believe)
public class EventLog
{
[Key]
public int Id { get; set; }
public int EventId { get; set; }
public int MaxDelegates { get; set; }
public string Code { get; set; }
public DateTime End { get; set; }
public string Title { get; set; }
}
And I want to map title to DBTitle in the following model:
public class CourseDetailVM : CourseDetailSummaryVM
{
public EventLog DBTitle { get; set; }
}
I then want to see this in the following view:
#using TSW.Web.Helpers
#model TSW.Web.ViewModels.CourseDetailVM
#{
Layout = "~/Views/_Master.cshtml";
}
#Model.DBTitle.Title;
I have the following controller already in place (sorry for the length I plan to reduce this down):
public class CourseDetailController : BaseRenderController<CourseDetailPageDT>
{
private readonly ISitePageFactory _pageFactory = null;
private readonly IEventService _eventService = null;
public CourseDetailController(IEventService eventService, ISitePageFactory pageFactory)
{
_pageFactory = pageFactory;
_eventService = eventService;
}
public async Task<ActionResult> CourseDetail()
{
var homepage = _pageFactory.GetCurrentHomepage();
var model = Mapper.Map<CourseDetailVM>(CurrentContent);
model.Email = homepage.ContactEmail;
model.PhoneNumber = homepage.HeaderPhoneNumber;
model.InnerPageHeader.ShowHeading = true;
model.InnerPageHeader.Title = model.PageTitle;
if (model.Categories.Count == 1)
{
var categoryTagId = model.Categories.First().Id;
var contentTypeAlias = DocumentTypeHelper.GetDocumentTypeAlias<CourseListingPageDT>();
var courseCategoryPage = Umbraco.TypedContentAtXPath($"//{contentTypeAlias}")
.FirstOrDefault(x => x.GetPropertyValue<int>(Constants.DocumentTypes.CourseListingPage.Category) == categoryTagId);
if (courseCategoryPage != null)
{
model.InnerPageHeader.BackLink = Mapper.Map<LinkItem>(courseCategoryPage.Id);
}
}
try
{
model.Events = await _eventService.GetEventsForCourse(CurrentContent.AdministrateId);
}
catch (Exception ex)
{
model.Events = new StaticPagedList<Event>(Enumerable.Empty<Event>(), 1, 1, 0);
Elmah.ErrorSignal.FromCurrentContext().Raise(ex);
}
if (CurrentContent.Graphic != 0)
{
model.InnerPageHeader.Graphic = Mapper.Map<CtaItem>(CurrentContent.Graphic);
}
return View(model);
}
}
I've tried every suggestion I can google to add the mapping in the controlling but can't get my head around this simple function of pulling the value from a SQL database into the razor view.
Could anyone help me out?
I am trying to make a custom file validation for my project which is based on .net core 2.
I want to validate file size and also file extension in order to prevent users from uploading large files and for example .png files.
I have searched a lot but I could not find anything that works.
Here is my file validation class :
public class FileTypeAttribute : ValidationAttribute, IClientModelValidator
{
private const int MaxSize = 1048576;
private const string _DefaultErrorMessage = "Only the following file types are allowed: {0}";
private IEnumerable<string> _ValidTypes { get; set; }
public string ValidTypes { get; set; }
public string ErrorMessageExtension { get; set; }
public string ErrorMessageSize { get; set; }
public FileTypeAttribute(string errorExtension, string errorSize, string vt)
{
ErrorMessageExtension = errorExtension;
ErrorMessageSize = errorSize;
_ValidTypes = vt.Split(',');
}
protected override ValidationResult IsValid(object value, ValidationContext validationContext)
{
IFormFile file = value as IFormFile;
if (file != null)
{
if (!_ValidTypes.Any(e => file.FileName.EndsWith(e)))
{
return new ValidationResult(ErrorMessageExtension);
}
if (file.Length > MaxSize)
{
return new ValidationResult(ErrorMessageSize);
}
}
return ValidationResult.Success;
}
public void AddValidation(ClientModelValidationContext context)
{
MergeAttribute(context.Attributes, "data-val", "true");
var errorMessage = FormatErrorMessage(context.ModelMetadata.GetDisplayName());
MergeAttribute(context.Attributes, "data-val-fileextensions", ErrorMessageExtension);
MergeAttribute(context.Attributes, "data-val-maxfilesize", ErrorMessageSize);
}
private bool MergeAttribute(
IDictionary<string, string> attributes, string key, string value)
{
if (attributes.ContainsKey(key))
{
return false;
}
attributes.Add(key, value);
return true;
}
}
and here is the javascript code in my view:
$.validator.addMethod("FileType",
function (value, element, param) {
for (var i = 0; i < element.files.length; i++) {
var extension = getFileExtension(element.files[0].name);
if ($.inArray(extension, param.validtypes) === -1) {
return false;
}
}
return true;
});
$.validator.unobtrusive.adapters.add('FileType', ['validtypes'], function (options) {
console.log("value:");
options.rules.cannotbered = {};
options.messages["FileType"] = options.message;
});
function getFileExtension(fileName) {
if (/[.]/.exec(fileName)) {
return /[^.]+$/.exec(fileName)[0].toLowerCase();
}
return null;
}
and here is the entity class code in my project:
public class MyEntityClass
{
public int MyEntityClassId { get; set; }
[FileType("invalid format", "invalid size", "jpg,png,gif")]
public IFormFile Photo { get; set; }
}
Can anyone help me to know where the problem is?
Thanks in advance.
When we are used code first or entity framework then there is easiest way to audit trail the actions like add , update and delete.
Create a class for capture the changes or track the changes when entity added, modifies or deleted.
using System;
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations.Schema;
using System.Data.Entity;
using System.Data.Entity.Infrastructure;
using System.Linq;
using System.Text;
using System.Web;
namespace MVC_AuditTrail.Models
{
public class AuditTrailFactory
{
private DbContext context;
public AuditTrailFactory(DbContext context)
{
this.context = context;
}
public Audit GetAudit(DbEntityEntry entry)
{
Audit audit = new Audit();
// var user = (User)HttpContext.Current.Session[":user"];
audit.UserId = "swapnil";// user.UserName;
audit.TableName = GetTableName(entry);
audit.UpdateDate = DateTime.Now;
audit.TableIdValue = GetKeyValue(entry);
//entry is Added
if (entry.State == EntityState.Added)
{
var newValues = new StringBuilder();
SetAddedProperties(entry, newValues);
audit.NewData = newValues.ToString();
audit.Actions = AuditActions.I.ToString();
}
//entry in deleted
else if (entry.State == EntityState.Deleted)
{
var oldValues = new StringBuilder();
SetDeletedProperties(entry, oldValues);
audit.OldData = oldValues.ToString();
audit.Actions = AuditActions.D.ToString();
}
//entry is modified
else if (entry.State == EntityState.Modified)
{
var oldValues = new StringBuilder();
var newValues = new StringBuilder();
SetModifiedProperties(entry, oldValues, newValues);
audit.OldData = oldValues.ToString();
audit.NewData = newValues.ToString();
audit.Actions = AuditActions.U.ToString();
}
return audit;
}
private void SetAddedProperties(DbEntityEntry entry, StringBuilder newData)
{
foreach (var propertyName in entry.CurrentValues.PropertyNames)
{
var newVal = entry.CurrentValues[propertyName];
if (newVal != null)
{
newData.AppendFormat("{0}={1} || ", propertyName, newVal);
}
}
if (newData.Length > 0)
newData = newData.Remove(newData.Length - 3, 3);
}
private void SetDeletedProperties(DbEntityEntry entry, StringBuilder oldData)
{
DbPropertyValues dbValues = entry.GetDatabaseValues();
foreach (var propertyName in dbValues.PropertyNames)
{
var oldVal = dbValues[propertyName];
if (oldVal != null)
{
oldData.AppendFormat("{0}={1} || ", propertyName, oldVal);
}
}
if (oldData.Length > 0)
oldData = oldData.Remove(oldData.Length - 3, 3);
}
private void SetModifiedProperties(DbEntityEntry entry, StringBuilder oldData, StringBuilder newData)
{
DbPropertyValues dbValues = entry.GetDatabaseValues();
foreach (var propertyName in entry.OriginalValues.PropertyNames)
{
var oldVal = dbValues[propertyName];
var newVal = entry.CurrentValues[propertyName];
if (oldVal != null && newVal != null && !Equals(oldVal, newVal))
{
newData.AppendFormat("{0}={1} || ", propertyName, newVal);
oldData.AppendFormat("{0}={1} || ", propertyName, oldVal);
}
}
if (oldData.Length > 0)
oldData = oldData.Remove(oldData.Length - 3, 3);
if (newData.Length > 0)
newData = newData.Remove(newData.Length - 3, 3);
}
public long? GetKeyValue(DbEntityEntry entry)
{
var objectStateEntry = ((IObjectContextAdapter)context).ObjectContext.ObjectStateManager.GetObjectStateEntry(entry.Entity);
long id = 0;
if (objectStateEntry.EntityKey.EntityKeyValues != null)
id = Convert.ToInt64(objectStateEntry.EntityKey.EntityKeyValues[0].Value);
return id;
}
private string GetTableName(DbEntityEntry dbEntry)
{
TableAttribute tableAttr = dbEntry.Entity.GetType().GetCustomAttributes(typeof(TableAttribute), false).SingleOrDefault() as TableAttribute;
string tableName = tableAttr != null ? tableAttr.Name : dbEntry.Entity.GetType().Name;
return tableName;
}
}
public enum AuditActions
{
I,
U,
D
}
}
Then create audit table entity and context class.
And Override savechanges method in this method get audit changes and save before base entity saved.
using System;
using System.Collections.Generic;
using System.Data.Entity;
using System.Data.Entity.Infrastructure;
using System.Linq;
using System.Web;
namespace MVC_AuditTrail.Models
{
public class Student
{
public int StudentID { get; set; }
public string Name { get; set; }
public string mobile { get; set; }
}
public class Audit
{
public long Id { get; set; }
public string TableName { get; set; }
public string UserId { get; set; }
public string Actions { get; set; }
public string OldData { get; set; }
public string NewData { get; set; }
public Nullable<long> TableIdValue { get; set; }
public Nullable<System.DateTime> UpdateDate { get; set; }
}
public class StdContext : DbContext
{
private AuditTrailFactory auditFactory;
private List<Audit> auditList = new List<Audit>();
private List<DbEntityEntry> objectList = new List<DbEntityEntry>();
public StdContext() : base("stdConnection")
{
Database.SetInitializer<StdContext>(new CreateDatabaseIfNotExists<StdContext>());
}
public DbSet<Student> Student { get; set; }
public DbSet<Audit> Audit { get; set; }
public override int SaveChanges()
{
auditList.Clear();
objectList.Clear();
auditFactory = new AuditTrailFactory(this);
var entityList = ChangeTracker.Entries().Where(p => p.State == EntityState.Added || p.State == EntityState.Deleted || p.State == EntityState.Modified);
foreach (var entity in entityList)
{
Audit audit = auditFactory.GetAudit(entity);
bool isValid = true;
if (entity.State == EntityState.Modified && string.IsNullOrWhiteSpace(audit.NewData) && string.IsNullOrWhiteSpace(audit.OldData))
{
isValid = false;
}
if (isValid)
{
auditList.Add(audit);
objectList.Add(entity);
}
}
var retVal = base.SaveChanges();
if (auditList.Count > 0)
{
int i = 0;
foreach (var audit in auditList)
{
if (audit.Actions == AuditActions.I.ToString())
audit.TableIdValue = auditFactory.GetKeyValue(objectList[i]);
this.Audit.Add(audit);
i++;
}
base.SaveChanges();
}
return retVal;
}
}
}