I have a audit method that logs changes to my datatabase.
The method looks (abit)simplified like this
private List<Case_History> GetRecords(DbEntityEntry dbEntry, ApplicationUser user, int actionID)
{
List<Case_History> result = new List<Case_History>();
DateTime changeTime = DateTime.Now;
// Finds the table
TableAttribute tableAttr = dbEntry.Entity.GetType().GetCustomAttributes(typeof(TableAttribute), false).SingleOrDefault() as TableAttribute;
// Finds the table name
string tableName = tableAttr != null ? tableAttr.Name : dbEntry.Entity.GetType().BaseType.Name;
// Finds primary key
string keyName = dbEntry.Entity.GetType().GetProperties().Single(p => p.GetCustomAttributes(typeof(KeyAttribute), false).Count() > 0).Name;
else if (dbEntry.State == EntityState.Modified)
{
List<string> values = new List<string>();
foreach (string propertyName in dbEntry.OriginalValues.PropertyNames)
{
if (!object.Equals(dbEntry.OriginalValues.GetValue<object>(propertyName), dbEntry.CurrentValues.GetValue<object>(propertyName)) && propertyName != "Modified_date")
{
//DEBUGGING VARIABLE
var originalValue = dbEntry.OriginalValues.GetValue<object>(propertyName);
//DEBUGGING VARIABLE
var newValue = dbEntry.CurrentValues.GetValue<object>(propertyName);
//Here is the part where i want to get the column display name
// This code is not working
PropertyInfo prop = typeof(this).GetProperty(propertyName);
var att = (DisplayAttribute)prop.GetCustomAttributes(typeof(DisplayAttribute);
if (att != null)
{
//found Display name
}
//If the record is different, record the change
if (dbEntry.OriginalValues.GetValue<object>(propertyName) != null && dbEntry.CurrentValues.GetValue<object>(propertyName) != null)
{
values.Add(propertyName + ": " + dbEntry.OriginalValues.GetValue<object>(propertyName).ToString() + " -> " + dbEntry.CurrentValues.GetValue<object>(propertyName).ToString());
}
}
}
}
I have found the variable in the locals in debugging session in the metadata propterties of the field. But only in the "this" variable. And this have to be dynamic for each different DBentries.
Just replace
PropertyInfo prop = typeof(this).GetProperty(propertyName);
by
PropertyInfo prop = dbEntry.Entity.GetType().GetProperty(propertyName);
Indeed, "this" is the current class, it contains no properties from the entity you want to log
Related
I have to check that the item has the field "In training", if that field exists and is different from 1, I have to set it to 1.
var field = "In Allenamento";
var value = "0";
var edited = (field = "1");
if (field != null && field == value)
{
field = "1";
}
using (new Sitecore.SecurityModel.SecurityDisabler())
{
item.Editing.BeginEdit();
item.Fields[field].Value = field;
item.Editing.EndEdit();
log.AppendLine(item.ID + "edited");
}
}
I am aware that I have written nonsense, so I ask for support
Take the field by its name ("In Allenamento" or "In Training")
If field is null it means that it does not exist on that item
Check if its value is not "1"
Start editing
Update value
End editing
using (new Sitecore.SecurityModel.SecurityDisabler())
{
var inTrainingField = item.Fields["In Allenamento"];
if (inTrainingField != null && inTrainingField.Value != "1")
{
item.Editing.BeginEdit();
item.Fields["In Allenamento"].Value = "1";
item.Editing.EndEdit();
log.AppendLine(item.ID + "edited");
}
}
I have a issue in EF7/asp.Net Core application. In my context I create a method Save:
public int Save()
{
ChangeTracker.DetectChanges();
var modifiedEntities = ChangeTracker.Entries()
.Where(p => p.State == EntityState.Modified || p.State == EntityState.Added || p.State == EntityState.Deleted || p.State == EntityState.Modified || p.State == EntityState.Detached).ToList();
var now = DateTime.UtcNow;
foreach (var change in modifiedEntities)
{
var entityName = change.Entity.GetType().Name;
var primaryKeyValue = GetPrimaryKeyValue(change.Entity);
foreach (var prop in change.Entity.GetType().GetTypeInfo().DeclaredProperties)
{
if (!prop.GetGetMethod().IsVirtual)
{
var currentValue = change.Property(prop.Name).CurrentValue;
var originalValue = change.Property(prop.Name).OriginalValue;
if (originalValue.ToString() != currentValue.ToString())
{
var changeLoged = new ChangeLog
{
PropertyName = prop.Name,
EntityName = entityName,
PrimaryKeyValue = primaryKeyValue,
DateChange = now,
OldValue = originalValue.ToString(),
NewValue = currentValue.ToString(),
ChangedBy = "test"
};
ChangeLog.Add(changeLoged);
}
}
}
}
return base.SaveChanges();
}
and method GetPrimaryKeyValue:
protected virtual int GetPrimaryKeyValue<T>(T entity)
{
var test = entity;
var test2 = test.GetType();
var keyName = this.Model.FindEntityType(test2).FindPrimaryKey().Properties
.Select(x => x.Name).Single();
var result = (int)entity.GetType().GetProperty(keyName).GetValue(entity, null);
if (result < 0)
return -1;
return result;
}
Unfortunatlly the change.Property(prop.Name).CurrentValue always equals OriginalValue, so the if
originalValue.ToString() != currentValue.ToString()
always return false.
Replace:
var originalValue = change.Property(prop.Name).OriginalValue;
to:
var originalValue = change.GetDatabaseValues().GetValue<object>(prop.Name);
This will not exactly answer your question since I cannot reproduce this issue but this may help you.
In EF Core, the PropertyEntry class has now an IsModified property which let you know if the value has been modified or not.
You should use it instead:
if (change.Property(prop.Name).IsModified)
{
var changeLoged = new ChangeLog
{
PropertyName = prop.Name,
EntityName = entityName,
PrimaryKeyValue = primaryKeyValue,
DateChange = now,
OldValue = originalValue.ToString(),
NewValue = currentValue.ToString(),
ChangedBy = "test"
};
ChangeLog.Add(changeLoged);
}
Disclaimer: I'm the owner of the project Entity Framework Plus
The Library has an Audit Feature (Supporting EF Core) which you may use or get inspired by to create your auditing (The code is Open Source).
Documentation: EF+ Audit
Based on the answers above I refactored my SetChanges method to this, and its working fine now! (EF 6 and .NET 6)
private void SetChanges()
{
TableName = Entry.Metadata.GetTableName();
var entsInDB = Entry.GetDatabaseValues();
foreach (PropertyEntry property in Entry.Properties)
{
if (property != null)
{
string propertyName = property.Metadata.Name;
if (property.Metadata.IsPrimaryKey())
{
KeyValues[propertyName] = property.CurrentValue ?? "";
continue;
}
switch (Entry.State)
{
case EntityState.Added:
NewValues[propertyName] = property.CurrentValue ?? "";
AuditType = AuditType.Create;
break;
case EntityState.Deleted:
OldValues[propertyName] = property.OriginalValue ?? "";
AuditType = AuditType.Delete;
break;
case EntityState.Modified:
if (property.IsModified)
{
var originalValue = entsInDB?.GetValue<object>(property.Metadata.Name);
if (originalValue?.ToString() != property.CurrentValue?.ToString())
{
ChangedColumns.Add(propertyName);
OldValues[propertyName] = originalValue?.ToString() ?? "";
NewValues[propertyName] = property.CurrentValue ?? "";
AuditType = AuditType.Update;
}
}
break;
}
}
}
}
Logging it to the database audit table:
I'm working on a ASP.net application which allows users to select an SSRS report from a dropdown list, fill in report parameters and then view the report in ReportViewer on the next page. Some of the parameters have valid values, which I get from the RDLC file using code below:
private ValidValue[] GetParameterValidValues(string parameterName, string reportDirectory)
{
ServerReport serverReport = new ServerReport();
string reportServerUrl = Application["ReportServerUrl"] as string;
serverReport.ReportPath = reportDirectory + lbReports.SelectedItem.Value;
serverReport.ReportServerUrl = new Uri(reportServerUrl);
ReportParameterInfo reportParameter = serverReport.GetParameters()[parameterName];
ValidValue[] validValues = reportParameter.ValidValues.ToArray();
return validValues;
}
These values are then added to dynamically created dropdown lists on the page.
The problem is that in some cases parameter A can filter valid values of parameter B. This functionality can be observer when selecting parameters in the ReportViewer control.
My question is, how can I implement this functionality in my code?
You need to populate parameter dependency values each time a value is selected that another param is dependent on. The best solution I have found is using a hash table as per this article:
http://www.codeproject.com/Articles/9161/SQL-Reporting-Services-Viewer-Page-Using-SOAP-API
Where you make a call to GetItemParameters, loop through each to check if they have dependencies. each dependency is then added to the hashtable for setting values later...
private ParameterValue[] _ParamDependenciesValues = new ParameterValue[0];
protected ItemParameter[] GetReportParameterDependencies()
{
ItemParameter[] Parameters = _Rs.GetItemParameters(_ThisReportPath, null, true, _ParamDependenciesValues, null);
if (Parameters.Length > 0)
{
foreach (ItemParameter p in Parameters)
{
if (p.Dependencies != null)
{
foreach (var d in p.Dependencies)
{
if (!_Dependencies.Contains(d))
{
_Dependencies.Add(d, null);
}
}
}
}
}
return Parameters;
}
Then, each time a parameter value is selected, you will need to set the parameter value in the array and call again the GetItemParameters with dependancy values set
protected void SetParamValues(ItemParameter[] Params)
{
foreach (ItemParameter Rp in Params)
{
if (Rp.Dependencies != null)
{
foreach (var d in Rp.Dependencies)
{
var MyParamValue = _ParamDependenciesValues.FirstOrDefault(c => c.Name == d);
if (MyParamValue == null)
{
Array.Resize(ref _ParamDependenciesValues, _ParamDependenciesValues.Count() + 1);
var MyNewParamValue = new ParameterValue {Name = d};
_ParamDependenciesValues[_ParamDependenciesValues.Length - 1] = MyNewParamValue;
MyParamValue = _ParamDependenciesValues.FirstOrDefault(c => c.Name == d);
}
if (_Dependencies.Contains(d))
{
if (MyParamValue != null && _Dependencies[d] != null)
MyParamValue.Value = _Dependencies[d].ToString();
}
}
}
}
ItemParameter[] Parameters = _Rs.GetItemParameters(_ThisReportPath, null, true, _ParamDependenciesValues, null);
}
This needs to be repeated until all parameters have all dependencies resolved...
I'm trying to query a DynamoDB that i have created using code from the Amazon docs with a few simple modifications. I'm trying to take the data i get and write it to a log file as strings. But all i can seem to get is this:
2013-02-22 20:21:37.9268|Trace|[System.Collections.Generic.Dictionary2+KeyCollection[System.String,Amazon.DynamoDB.Model.AttributeValue] System.Collections.Generic.Dictionary2+ValueCollection[System.String,Amazon.DynamoDB.Model.AttributeValue]]|
I've tried a few different things but all return either the same thing, or something very similar.
The code i'm using:
private static void GetCallsForRange()
{
AmazonDynamoDBConfig config = new AmazonDynamoDBConfig();
config.ServiceURL = "http://dynamodb.us-west-2.amazonaws.com";
AmazonDynamoDBClient client = new AmazonDynamoDBClient(config);
DateTime startDate = DateTime.Today.AddDays(-21);
string start = startDate.ToString("G", DateTimeFormatInfo.InvariantInfo);
DateTime endDate = DateTime.Today;
string end = endDate.ToString("G", DateTimeFormatInfo.InvariantInfo);
QueryRequest request = new QueryRequest
{
TableName = "Inquiry",
HashKeyValue = new AttributeValue { S = "+15555555555" },
RangeKeyCondition = new Condition
{
ComparisonOperator = "BETWEEN",
AttributeValueList = new List<AttributeValue>()
{
new AttributeValue { S = start },
new AttributeValue { S = end }
}
}
};
QueryResponse response = client.Query(request);
QueryResult result = response.QueryResult;
foreach (Dictionary<string, AttributeValue> item in response.QueryResult.Items)
{
string logMsg = String.Format("[{0} {1}]", item.Keys, item.Values);
Logging.LogTrace(logMsg);
}
}
You will need to iterate over each item in the response.QueryResult.Items. You could rewrite your loop like this (taken from the Amazon DynamoDB documentation):
foreach (Dictionary<string, AttributeValue> item in response.QueryResult.Items)
{
LogItem(item);
}
private void LogItem(Dictionary<string, AttributeValue> attributeList)
{
foreach (KeyValuePair<string, AttributeValue> kvp in attributeList)
{
string attributeName = kvp.Key;
AttributeValue value = kvp.Value;
string logValue =
(value.S == null ? "" : value.S) +
(value.N == null ? "" : value.N.ToString()) +
(value.B == null ? "" : value.B.ToString()) +
(value.SS == null ? "" : string.Join(",", value.SS.ToArray())) +
(value.NS == null ? "" : string.Join(",", value.NS.ToArray())) +
(value.BS == null ? "" : string.Join(",", value.BS.ToArray()));
string logMsg = string.Format("[{0} {1}]", attributeName, logValue);
Logging.LogTrace(logMsg);
}
}
Essentially, you need to discover the "type" of the AttributeValue(String, Number, Binary, StringSet, NumberSet, BinarySet) and then output that to your log.
I hope that helps!
We have some code that exports data from a database to Excel and it works well.
The main problem is we use a DB view to gather all our data. This creates the issue of having a sizable view as we have multiple types of objects of which we export.
class Event
int id
string title
List<EventDate> Dates
string desc
class EventDate
DateTime start
DateTime end
List<EventLocation> Locations
class EventLocation
string address
string city
string zip
class Birthday : Event
int balloonsOrdered
string cakeText
class Meeting : Event
string Organizer
string Topic
So, above is the model. Birthday and Meeting inherit from Event and all Event objects have a list of EventDate objects. Each EventDate object has a start date, end date and a list of Location objects.
Our goal is to find a dynamic way to get data from the DB to an Excel doc. We'd rather not maintain a massive view in the database (as we'll add more event types eventually).
I'm not all that familiar with .NET's XML capabilities, but the solution we are using now uses OpenXML and the code makes sense.
You could create a CSV file using List<T> and and following code:
using System;
using System.Collections.Generic;
using System.IO;
using System.Reflection;
using System.Web;
public static void CreateCSV<T>(List<T> list, string csvNameWithExt)
{
if (list == null || list.Count == 0) return;
HttpContext.Current.Response.Clear();
HttpContext.Current.Response.AddHeader(
"content-disposition", string.Format("attachment; filename={0}", csvNameWithExt));
HttpContext.Current.Response.ContentType = "application/vnd.ms-excel";
//get type from 0th member
Type t = list[0].GetType();
string newLine = Environment.NewLine;
using (StringWriter sw = new StringWriter())
{
//gets all properties
PropertyInfo[] props = t.GetProperties();
//this is the header row
//foreach of the properties in class above, write out properties
foreach (PropertyInfo pi in props)
{
sw.Write(pi.Name.ToUpper() + ",");
}
sw.Write(newLine);
//this acts as datarow
foreach (T item in list)
{
//this acts as datacolumn
foreach (PropertyInfo Column in props)
{
//this is the row+col intersection (the value)
string value = item.GetType().GetProperty(Column.Name).GetValue(item, null).ToString();
if (value.Contains(","))
{
value = "\"" + value + "\"";
}
sw.Write(value + ",");
}
sw.Write(newLine);
}
// render the htmlwriter into the response
HttpContext.Current.Response.Write(sw.ToString());
HttpContext.Current.Response.End();
}
}
I improved the solution from Brad to better work with Entity Framework Data Annotations :
it gets the display name from annotations instead of the column name
it does not export columns that are marked "scaffold = false" via annotations
it offers you a way to deal with complex types (not primitive types)
public static class ExportListToCSV
{
public static void CreateCSV<T>(List<T> list, string csvNameWithExt)
{
if (list == null || list.Count == 0) return;
HttpContext.Current.Response.Clear();
HttpContext.Current.Response.AddHeader(
"content-disposition", string.Format("attachment; filename={0}", csvNameWithExt));
HttpContext.Current.Response.ContentType = "application/vnd.ms-excel";
//get type from 0th member
Type t = list[0].GetType();
string newLine = Environment.NewLine;
//gets all columns
PropertyInfo[] columns = t.GetProperties();
// skip columns where ScaffoldColumn = false
// useful to skip column containing IDs for Foreign Keys
var props = t.GetProperties(BindingFlags.Instance | BindingFlags.Public)
.Select(p => new
{
Property = p,
Attribute = p.GetCustomAttribute<ScaffoldColumnAttribute>()
})
.Where(p => p.Attribute == null || (p.Attribute != null && p.Attribute.Scaffold != false))
.ToList();
using (StringWriter sw = new StringWriter())
{
//this is the header row
foreach (var prop in props)
{
var pi = prop.Property;
string columnName = "";
// outputs raw column name, but this is not really meaningful for the end user
//sw.Write(pi.Name + ",");
columnName = pi.GetDisplayName();
sw.Write(columnName + ",");
}
sw.Write(newLine);
//this acts as datarow
foreach (T item in list)
{
//this acts as datacolumn
foreach (var prop in props)
{
var column = prop.Property;
//this is the row+col intersection (the value)
PropertyInfo info = item.GetType().GetProperty(column.Name);
//string value = info.GetValue(item, null);
string value = GetDescriptionForComplexObjects(info.GetValue(item, null));
if (value.Contains(","))
{
value = "\"" + value + "\"";
}
sw.Write(value + ",");
}
sw.Write(newLine);
}
// render the htmlwriter into the response
HttpContext.Current.Response.Write(sw.ToString());
HttpContext.Current.Response.End();
}
}
private static string GetDescriptionForComplexObjects(object value)
{
string desc = "";
if (
(value != null && !IsSimpleType(value.GetType()))
)
{
dynamic dynObject = value;
if (dynObject != null)
{
//using Model Extensions,
//I made sure all my objects have a DESCRIPTION property
//this property must return a string
//for an employee object, you would return the employee's name for example
//desc = dynObject.DESCRIPTION;
}
}
else
{
desc = "" + value;
}
return desc;
}
public static string GetDisplayName(this PropertyInfo pi)
{
if (pi == null)
{
throw new ArgumentNullException(nameof(pi));
}
return pi.IsDefined(typeof(DisplayAttribute)) ? pi.GetCustomAttribute<DisplayAttribute>().GetName() : pi.Name;
}
public static bool IsSimpleType(Type type)
{
return
type.IsPrimitive ||
new Type[] {
typeof(Enum),
typeof(String),
typeof(Decimal),
typeof(DateTime),
typeof(DateTimeOffset),
typeof(TimeSpan),
typeof(Guid)
}.Contains(type) ||
Convert.GetTypeCode(type) != TypeCode.Object ||
(type.IsGenericType && type.GetGenericTypeDefinition() == typeof(Nullable<>) && IsSimpleType(type.GetGenericArguments()[0]))
;
}
}
}