How can I change MaxValueLength of NewValue and OriginalValue in EntityPropertyChange.cs? - aspnetboilerplate

I am using aspnetzero and I have enabled entity history. Maxvaluelength of NewValue and OriginalValue is 512 chars in AbpEntityPropertyChanges table.
How can I change MaxValueLength of NewValue and OriginalValue?
Update -
Below is the way tried extending entity, but still its truncating characters after 512.
public class EntityPropertyChangeExtended : EntityPropertyChange
{
[Column(TypeName = "varchar(MAX)")]
[StringLength(8000)]
public override string NewValue { get; set; }
[Column(TypeName = "varchar(MAX)")]
[StringLength(8000)]
public override string OriginalValue { get; set; }
public override void SetNewValue(string newValue)
{
NewValueHash = newValue?.ToMd5();
NewValue = newValue;
}
/// <summary>
/// Use to set original value. (Also fills <see cref="OriginalValueHash"/> according to <paramref name="originalValue"/>)
/// </summary>
public override void SetOriginalValue(string originalValue)
{
OriginalValueHash = originalValue?.ToMd5();
OriginalValue = originalValue;
}
}

Related

Build dynamic select using LINQ with nested class

I am trying to create a dynamic select builder that includes also nested class.
Basically I have entity class DB then DTO like in the following classes
/// <summary>
/// Base entity class
/// </summary>
public abstract class BaseEntity
{ }
/// <summary>
/// EF Schema entity
/// </summary>
public class AMOS_AMOSUSER : BaseEntity
{
public decimal USERID { get; set; }
public decimal? SUPERIORID { get; set; }
public decimal? EMPLOYEEID { get; set; }
public virtual AMOS_AMOSUSER SUPERIOR { get; set; }
public string LOGINID { get; set; }
public string NAME { get; set; }
public string COMMENT1 { get; set; }
public decimal ACCOUNTDISABLED { get; set; }
public DateTime? LASTLOGIN { get; set; }
}
/// <summary>
/// DTO base for all dtos
/// </summary>
public abstract class DTO_BASE
{
}
/// <summary>
/// DTO amos user base
/// </summary>
public class DTO_AMOSUSER_BASE : DTO_BASE
{
public decimal USERID { get; set; }
public string LOGINID { get; set; }
public string NAME { get; set; }
}
/// <summary>
/// DTO AMOS User
/// </summary>
public class DTO_AMOSUSER : DTO_AMOSUSER_BASE
{
public decimal? SUPERIORID { get; set; }
public virtual DTO_AMOSUSER_BASE SUPERIOR { get; set; }
}
Here below code for testing and creating dynamic select.
public static class TestDynamicSelect
{
public static void TestDynamicSelectDTO()
{
List<AMOS_AMOSUSER> users = new List<AMOS_AMOSUSER>();
PopulateUsers(users);
var q = users.AsQueryable().Select(DynamicSelectGenerator2<AMOS_AMOSUSER, DTO_AMOSUSER>(typeof(DTO_AMOSUSER).GetProperties().Select(p => p.Name)));
var cc = q.ToArray();
foreach (var item in cc)
{
Console.WriteLine(Newtonsoft.Json.JsonConvert.SerializeObject(item));
}
Console.ReadLine();
}
public static Expression<Func<T, TSelect>> DynamicSelectGenerator1<T, TSelect>(string fields)
{
return DynamicSelectGenerator2<T, TSelect>(fields.Split(','));
}
/// <param name="fields">
/// Format1: "Field1"
/// Format2: "Nested1.Field1"
/// Format3: "Field1:Field1Alias"
/// </param>
public static Expression<Func<T, TSelect>> DynamicSelectGenerator2<T, TSelect>(IEnumerable<string> fields)
{
string[] EntityFields;
if (fields == null || fields.Count() == 0)
// get Properties of the T
EntityFields = typeof(T).GetProperties().Select(propertyInfo => propertyInfo.Name).ToArray();
else
EntityFields = fields.ToArray();
// input parameter "x"
var xParameter = Expression.Parameter(typeof(T), "x");
// new statement "new Data()"
var xNew = Expression.New(typeof(TSelect));
// create initializers
var bindings = EntityFields
.Select(x =>
{
string[] xFieldAlias = x.Split(':');
string field = xFieldAlias[0];
string[] fieldSplit = field.Split('.');
if (fieldSplit.Length > 1)
{
// original value "x.Nested.Field1"
Expression exp = xParameter;
foreach (string item in fieldSplit)
exp = Expression.PropertyOrField(exp, item);
// property "Field1"
PropertyInfo member2 = null;
if (xFieldAlias.Length > 1)
member2 = typeof(TSelect).GetProperty(xFieldAlias[1]);
else
member2 = typeof(T).GetProperty(fieldSplit[fieldSplit.Length - 1]);
// set value "Field1 = x.Nested.Field1"
var res = Expression.Bind(member2, exp);
return res;
}
// property "Field1"
var mi = typeof(T).GetProperty(field);
PropertyInfo member;
if (xFieldAlias.Length > 1)
member = typeof(TSelect).GetProperty(xFieldAlias[1]);
else
member = typeof(TSelect).GetProperty(field);
// original value "x.Field1"
if (member != null)
{
if (mi != null)
{
if (typeof(BaseEntity).IsAssignableFrom(mi.PropertyType))
{
List<string> props = new List<string>();
mi.PropertyType.GetProperties().ToList().ForEach(p => {
if (member.PropertyType.GetProperties().Any(c => c.Name.Equals(p.Name, StringComparison.Ordinal)))
props.Add(p.Name);
});
Type ex = typeof(TestDynamicSelect);
MethodInfo mm = ex.GetMethod("DynamicSelectGenerator2");
MethodInfo miConstructed = mm.MakeGenericMethod(mi.PropertyType, member.PropertyType);
var expr = (LambdaExpression)miConstructed.Invoke(null, new object[] { props });
// Probably here is not correct
return Expression.Bind(member, Expression.TypeAs(expr, member.PropertyType));
}
else
{
var xOriginal = Expression.Property(xParameter, mi);
return Expression.Bind(member, xOriginal);
}
}
}
//return Expression.Constant(GetDefault(mi.PropertyType));
throw new Exception("No property to bind");
}
);
var b = bindings.Where(x => x != null && xNew.Type.GetProperties().Any(p => p.Name.Equals(x.Member.Name, StringComparison.OrdinalIgnoreCase)));
// initialization "new Data { Field1 = x.Field1, Field2 = x.Field2 }"
var xInit = Expression.MemberInit(xNew, b);
// expression "x => new Data { Field1 = x.Field1, Field2 = x.Field2 }"
var lambda = Expression.Lambda<Func<T, TSelect>>(xInit, xParameter);
return lambda;
}
}
I can't populate correctly the nested property SUPERIOR which is always null.
Basically what I am trying to do is to populate DTO classes from an Entity class using property name comparison.
Please help.

Performance Entity Framework Code First

I have a performance problem using code first and entity framework. I only want to read a simple mapping info which to up to 3 seconds.
// The enity
[Table("ResourceMappingEntity")]
public class ResourceMappingEntity
{
/// <summary>
/// Gets or sets the testing number.
/// </summary>
/// <value>
/// The testing number.
/// </value>
[Key]
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public long Id { get; set; }
[Required]
[Column(TypeName = "varchar")]
[StringLength(80)]
public string LocationNameOrIp { get; set; }
[Required]
[Column(TypeName = "varchar")]
[StringLength(80)]
public string Resource { get; set; } //todo mb: noch nicht drin
[Column(TypeName = "varchar")]
[StringLength(80)]
public string App { get; set; } //todo mb: noch nicht drin
/// <summary>
/// Gets or sets the created at.
/// </summary>
/// <value>
/// The created at.
/// </value>
[Column(TypeName = "datetime2")]
public DateTime CreatedAt { get; set; }
}
//the query
using (var ctx = new OsmaTestingContextFactory().Create())
{
ctx.Configuration.ProxyCreationEnabled = false;
var two = DateTime.Now.Ticks / TimeSpan.TicksPerMillisecond;
var dif = two - one;
IEnumerable<string> res = await ctx.ResourceMappingEntityStorage.AsNoTracking()
.Where(
x => x.LocationNameOrIp == IPOrName)
.Select(y => y.Resource)
.ToListAsync();
var three = DateTime.Now.Ticks / TimeSpan.TicksPerMillisecond;
var dif2 = three - two;
if (res.Count() != 1)
{
throw new Exception("Can not find a specific resouce");
}
return res.First();
}
// I also set IsUnicode to false....
Any ideas?
Hope for help or alternative solutions?! Should be ok in a few milliseconds like in SQL managment studio
Is there a good or standrad way to test the performance of ef?
Thx
Marcus

Getting last inserted row id using repository pattern

I'm new to this repository pattern. I have the following methods in repository.
public abstract class Repository<T> : IRepository<T> where T : class
{
private PHOnlineEntities dataContext;
private readonly IDbSet<T> dbset;
protected Repository(IDatabaseFactory databaseFactory)
{
DatabaseFactory = databaseFactory;
dbset = DataContext.Set<T>();
}
protected IDatabaseFactory DatabaseFactory
{
get;
private set;
}
protected PHOnlineEntities DataContext
{
get { return dataContext ?? (dataContext = DatabaseFactory.Get()); }
}
public virtual int Add(T entity)
{
dbset.Add(entity);
dataContext.SaveChanges();
// return id here
}
public virtual void Update(T entity)
{
dbset.Attach(entity);
dataContext.Entry(entity).State = EntityState.Modified;
}
public virtual void Delete(T entity)
{
dbset.Remove(entity);
}
public virtual void Delete(Expression<Func<T, bool>> where)
{
IEnumerable<T> objects = dbset.Where<T>(where).AsEnumerable();
foreach (T obj in objects)
dbset.Remove(obj);
}
public virtual T GetById(long id)
{
return dbset.Find(id);
}
public virtual T GetById(string id)
{
return dbset.Find(id);
}
public virtual IEnumerable<T> GetAll()
{
return dbset.ToList();
}
public virtual IEnumerable<T> GetMany(Expression<Func<T, bool>> where)
{
return dbset.Where(where).ToList();
}
public T Get(Expression<Func<T, bool>> where)
{
return dbset.Where(where).FirstOrDefault<T>();
}
This is my CustomerRepository class
public interface ICustomerDetailRepository : IRepository<CustomerDetail>
{
}
/// <summary>
/// CustomerDetail repository
/// </summary>
public class CustomerDetailRepository : Repository<CustomerDetail>, ICustomerDetailRepository
{
/// <summary>
///
/// </summary>
private PHOnlineEntities _dataContext;
/// <summary>
///
/// </summary>
protected IDatabaseFactory DatabaseFactory
{
get;
private set;
}
/// <summary>
///
/// </summary>
/// <param name="databaseFactory"></param>
public CustomerDetailRepository(IDatabaseFactory databaseFactory)
: base(databaseFactory)
{
DatabaseFactory = databaseFactory;
}
/// <summary>
///
/// </summary>
protected PHOnlineEntities DataContext
{
get { return _dataContext ?? (_dataContext = DatabaseFactory.Get()); }
}
}
CustomerDetail class contains Model. It has all the Entity column along with ID column.
When i Add the entity to database, i want to return last inserted row id. The Id is identity column. Could anyone help me on this?
Check your CustomerDetail object after inserting, the ID will be populated already
public class CustomerDetail
{
public int Id{ get; set; }
public string Name{ get; set; }
public string Address{ get; set; }
}
var customerDetail = new CustomerDetail { Name = "Bubbles", Address = "1 way, city" }
customerDetailRepository.Add(customerDetail)
Console.WriteLine(customerDetail.Id); // This is the identity value
You have to create an interface like that:
public interface IEntity
{
public int Id { get; set;}
}
Make your entities implement that interface and change your repository class:
public abstract class Repository<T> : IRepository<T> where T : class, IEntity
{
(...)
public virtual int Add(T entity)
{
dbset.Add(entity);
dataContext.SaveChanges();
// return id here
return entity.Id;
}
}

OutputCache varying by a complex object property

I have a controller action that receives a complex object as a parameter, I need the OutputCache to vary by one of the properties of this complex object. Is this possible? How?
if you have a model like
public class person{
public string Name {get;set;}
public string location {get;set;}
}
and in the (strongly typed)view you have a form
#model Person
#Html.BeginForm(){
#Html.TextBoxFor(x=>x.Name)
#Html.TextBoxFor(x=>x.location)
}
and you submit the form to an ActionResult savePerson, with varying signature like
public ActionResult savePerson(Person p){
// p.Name
// p.location
}
or
public ActionResult savePerson(string Name, string location){
}
therefore i think if you annotate the ActionResult like
[OutputCache(Duration=3600, VaryByParam="Name")]
public ActionResult savePerson(Person p)
{
//
return View();
}
it will do for you, or if you have a complex model like
public class person{
public string Name {get;set;}
public Location loc {get;set;}
}
public class Location{
public string address
}
try
[OutputCache(Duration=3600, VaryByParam="Person.Location.address")]
public ActionResult savePerson(Person p)
{
//
return View();
}
I had the same requirement as above and came up with a slightly different approach
The class
/// <summary>
/// This class is used to encapsulate search filters for monitor graphs
/// </summary>
public class DatacarMonitorSearchCriteriaModel
{
public int? SynergyCode { get; set; }
[Required]
[DataType(DataType.Date)]
public DateTime StartDate { get; set; }
[Required]
[DataType(DataType.Date)]
public DateTime EndDate { get; set; }
/// <summary>
/// Filter to apply
/// </summary>
public IEnumerable<int> Countries { get; set; }
public DatacarMonitorSearchCriteriaModel()
{
Countries = new List<int>();
}
}
OutputCacheComplexAttribute
/// <summary>
/// <para>
/// An instance of this class mimic the behaviour of OutputCacheAttribute but for complex objects.
/// </para>
/// <para>
/// It allows to cache the output of any action that takes complex objects
/// </para>
/// </summary>
public class OutputCacheComplexAttribute : OutputCacheAttribute
{
private readonly Type[] _types;
private string _cachedKey;
/// <summary>
/// Initializes a new instance of the <see cref="OutputCacheComplexAttribute"/> class.
/// </summary>
/// <param name="types">Types that this attribute will lookup for in QueryString/Form data and store values in cache.</param>
/// <exception cref="System.ArgumentOutOfRangeException">type;type cannot be null</exception>
public OutputCacheComplexAttribute(params Type[] types)
{
if (types == null)
{
throw new ArgumentOutOfRangeException("type", "type cannot be null");
}
_types = types;
}
public override void OnActionExecuting(ActionExecutingContext filterContext)
{
StringBuilder sbCachedKey = new StringBuilder();
if (filterContext.HttpContext.Request.Url != null)
{
string path = filterContext.HttpContext.Request.Url.PathAndQuery;
IDictionary<string, object> parameters = filterContext.ActionParameters;
//we need to compute a cache key which will be used to store the action output for later retrieval
//The cache key scheme is
// {url}:{key 1}:{value};[{key 2}:{value 2}[; ... {key n}:{value n}]];
// where :
// - url is the url of the action that will be executed
// - key n is the name of the n-th parameter
// - value n is the value of the n-th parameter as json string.
foreach (KeyValuePair<string, object> kv in parameters)
{
var kv1 = kv;
if (kv.Value != null && _types.AtLeastOnce(t => t.IsInstanceOfType(kv1.Value)))
{
sbCachedKey = sbCachedKey.AppendFormat("{0}:{1};",kv.Key,
JsonConvert.SerializeObject(kv.Value, Formatting.None, new JsonSerializerSettings()
{
NullValueHandling = NullValueHandling.Ignore,
ReferenceLoopHandling = ReferenceLoopHandling.Ignore
}));
}
}
_cachedKey = String.Format("{0}:{1}:{2}", GetType().Name, path, sbCachedKey.ToString());
}
if (!String.IsNullOrWhiteSpace(_cachedKey) && filterContext.HttpContext.Cache[_cachedKey] != null)
{
filterContext.Result = (ActionResult)filterContext.HttpContext.Cache[_cachedKey];
}
else
{
base.OnActionExecuting(filterContext);
}
}
public override void OnActionExecuted(ActionExecutedContext filterContext)
{
if (!String.IsNullOrWhiteSpace(_cachedKey))
{
filterContext.HttpContext.Cache.Add(_cachedKey, filterContext.Result, null,
DateTime.UtcNow.AddSeconds(Duration), Cache.NoSlidingExpiration,
CacheItemPriority.Default, null);
}
base.OnActionExecuted(filterContext);
}
}
Attribute usage
[OutputCacheComplex(typeof(DatacarMonitorSearchCriteriaModel), Duration = OutputCacheDurationInSeconds, Location = OutputCacheLocation.Server)]
public async Task<JsonNetResult<DatacarMonitorDetailModel>> ReadMonitorDetailsJson([DataSourceRequest] DataSourceRequest request, DatacarMonitorSearchCriteriaModel criteria)
{
//some really complicated code here
}
with this new attribute, you can specify which type[s] to use for caching and the cache key will be computed based on values of each its properties.
For object, just that work fine:
[OutputCache(VaryByParam = "*", Duration = 60)]

ASP.NET MVC "compare" validation doesn't work properly

I have a compare validation for a password - confirm password fields and also a server validation to check if the password fits with a minimum number of characters.
View:
#Html.PasswordFor(model => model.password)
#Html.PasswordFor(model => model.repeatPassword)
Model:
public class Model_Clerk
{
public int clerkID { get; set; }
public string password { get; set; }
[Compare("password", ErrorMessage = "Error comparing password and password confirm values")]
public string repeatPassword { get; set; }
}
Controller action method:
public ActionResult SaveClerk(Model_Clerk model)
{
//Password minimum lenght
if (!string.IsNullOrEmpty(model.password) && model.password.Trim().Length < 5)
{
ModelState.AddModelError(model.password, "Password must be at least 5 characters long");
}
if (ModelState.IsValid)
{
//Save logic here...
}
else
{
return PartialView("EditClerks", model);
}
}
When the server validation is executed the warning message appears correctly, and after that the compare validation will not work anymore. Any idea?
From our comments, I think that actually the best solution would be to write your own DataAnnotation.
Something like [CompareIf("password", ErrorMessage = "Error comparing password and password confirm values")]
Your DataAnnotation Code would have to check to see if the Password is not empty and valid, and then compare the two values.
Ran across this issue today and wrote a CompareIf custom attribute. It's nearly identical to RequiredIf (covered here), but inherits CompareAttribute instead and accepts the otherProperty as a third argument.
using System.ComponentModel.DataAnnotations;
public class CompareIfAttribute : CompareAttribute
{
private readonly string _dependentProperty;
private readonly object _targetValue;
public CompareIfAttribute(string dependentProperty, object targetValue, string otherProperty) : base(otherProperty)
{
_dependentProperty = dependentProperty;
_targetValue = targetValue;
}
/// <summary>
/// Returns if the given validation result is valid. It checks if the RequiredIfAttribute needs to be validated
/// </summary>
/// <param name="value">Value of the control</param>
/// <param name="validationContext">Validation context</param>
/// <returns></returns>
protected override ValidationResult IsValid(object value, ValidationContext validationContext)
{
var field = validationContext.ObjectType.GetProperty(_dependentProperty);
if (field != null)
{
var dependentValue = field.GetValue(validationContext.ObjectInstance, null);
if (dependentValue == null && _targetValue == null || dependentValue?.ToString() == _targetValue?.ToString())
{
var test = base.IsValid(value, validationContext);
if (test != ValidationResult.Success)
{
return test;
}
}
return ValidationResult.Success;
}
throw new ValidationException($"CompareIf Dependant Property {_dependentProperty} does not exist");
}
}
First two arguments are the dependent property and target value, and the third is the property to compare with. Usage:
public bool CandidateHasNoLegalMiddleName { get; set; }
public string CandidateMiddle { get; set; }
[CompareIf(nameof(CandidateHasNoLegalMiddleName), false, nameof(CandidateMiddle)]
public string ConfirmMiddle { get; set; }
This will allow the comparison to occur only if the condition is true.

Resources