Please help me out to figure what is making my Functions Slow - asp.net-mvc

Using Unit of Work Repository pattern
private UnitOfWork unitOfWork = new UnitOfWork();
private Entities _Entities = new Entities();
var filing_xml = unitOfWork.T_FILING_XMLRepository.Get().Where(a =>
a.filing_id == filingID).FirstOrDefault();
This is taking around 10 seconds to fetch data
var filing_xml = _Entities.T_FILING_XML.Where(a => a.filing_id == filingID).FirstOrDefault();
This is taking around 2 seconds to fetch data
Is there any solution to make the unit of work faster?
public virtual IEnumerable<TEntity> Get(
Expression<Func<TEntity, bool>> filter = null,
Func<IQueryable<TEntity>, IOrderedQueryable<TEntity>> orderBy = null,
string includeProperties = "")
{
IQueryable<TEntity> query = dbSet;
if (filter != null)
{
query = query.Where(filter);
}
foreach (var includeProperty in includeProperties.Split
(new char[] { ',' }, StringSplitOptions.RemoveEmptyEntries))
{
query = query.Include(includeProperty);
}
if (orderBy != null)
{
return orderBy(query).ToList();
}
else
{
return query.ToList();
}
}
The unit of work class
public GenericRepository<T_FILING_XML> T_FILING_XMLRepository
{
get
{
if (this.t_filing_xmlRepository == null)
{
this.t_filing_xmlRepository = new GenericRepository<T_FILING_XML>(context);
}
return t_filing_xmlRepository;
}
}

Code #1
private Entities _Entities = new Entities();
var filing_xml = unitOfWork.T_FILING_XMLRepository.Get().Where(a =>
a.filing_id == filingID).FirstOrDefault();
You are fetching all the records from the database then you filter the result in the application to get only one record. Your Get method has a filter parameter, why not using it?
Using the code below, you will only fetch only one record from the database.
var filing_xml = unitOfWork.T_FILING_XMLRepository.Get(a =>
a.filing_id == filingID).FirstOrDefault();

Related

Using a string to call a table in Entity Framework

Here's my old code.
public override Task ExecuteAsync(string generator, WebHookHandlerContext context)
{
DatawarehouseEntities db = new DatawarehouseEntities();
// Get JSON from WebHook
JObject data = context.GetDataOrDefault<JObject>();
var tableName = data["Table_Name"].ToString();
var columnNames = db.Database.SqlQuery<string>(String.Format("SELECT name FROM sys.columns WHERE object_id = OBJECT_ID('{0}'); ", tableName)).ToList();
var table = db.GetType().GetProperty(tableName).GetValue(db, null);
var assembly = AppDomain.CurrentDomain.GetAssemblies()
.SingleOrDefault(a => a.GetName().Name == "DSI.Data");
var type = assembly.GetTypes().FirstOrDefault(t => t.Name == tableName);
var dbset = Activator.CreateInstance(type);
//var dbset = db.Set(type);
var jsonParams = data.Properties().Select(x => x.Name).ToList();
var selectedColumnNames = columnNames.Intersect(jsonParams);
foreach (var columnName in selectedColumnNames)
{
var property = dbset.GetType().GetProperties().FirstOrDefault(x => x.Name == columnName);
property.SetValue(dbset, data[columnName].ToString(), null);
}
db.Set(type).Add(dbset);
db.SaveChanges();
return Task.FromResult(true);
}
Here's what I try to post http://localhost:port/api/webhooks/incoming/genericjson?code=secret&Table_Name=Table_Name. The type always comes back null. How can I select a table using the string that I pass in?
To make this easier, faster and less error-prone enumerate your entity types and add them to a lookup. EG
public static Dictionary<string, Type> EntityTypesByName { get; } = new Dictionary<string, Type>();
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
base.OnModelCreating(modelBuilder);
foreach (var et in modelBuilder.Model.GetEntityTypes())
{
EntityTypesByName.Add(et.Name, et.ClrType);
}
}
This is what finally worked for me.
public override Task ExecuteAsync(string generator, WebHookHandlerContext context)
{
DatawarehouseEntities db = new DatawarehouseEntities();
// Get JSON from WebHook
JObject data = context.GetDataOrDefault<JObject>();
var tableName = data["Table_Name"].ToString();
var columnNames = db.Database.SqlQuery<string>(String.Format("SELECT name FROM sys.columns WHERE object_id = OBJECT_ID('{0}'); ", tableName)).ToList();
var table = db.GetType().GetProperty(tableName).GetValue(db, null);
var assembly = AppDomain.CurrentDomain.GetAssemblies()
.SingleOrDefault(a => a.GetName().Name == "DSI.Data");
var type = assembly.GetTypes().FirstOrDefault(t => t.Name == tableName);
var dbset = Activator.CreateInstance(type);
var jsonParams = data.Properties().Select(x => x.Name).ToList();
var selectedColumnNames = columnNames.Intersect(jsonParams);
foreach (var columnName in selectedColumnNames)
{
var property = dbset.GetType().GetProperties().FirstOrDefault(x => x.Name == columnName);
property.SetValue(dbset, data[columnName].ToString(), null);
}
db.Set(type).Add(dbset);
db.SaveChanges();
return Task.FromResult(true);
}

ASP.NET Core [FromBody] vs MVC 5 binding

I got an MVC 5 application that i'm porting to asp.net Core.
In the MVC application call to controller we're made using AngularJS $resource (sending JSON) and we we're POSTing data doing :
ressource.save({ entries: vm.entries, projectId: vm.project.id }).$promise...
that will send a JSON body like:
{
entries:
[
{
// lots of fields
}
],
projectId:12
}
the MVC controller looked like this :
[HttpPost]
public JsonResult Save(List<EntryViewModel> entries, int projectId) {
// code here
}
How can I replicate the same behaviour with .NET Core since we can't have multiple [FromBody]
you cannot have multiple parameter with the FromBody attibute in an action method. If that is need, use a complex type such as a class with properties equivalent to the parameter or dynamic type like that
[HttpPost("save/{projectId}")]
public JsonResult Save(int projectId, [FromBody] dynamic entries) {
// code here
}
As pointed out in the comment, one possible solution is to unify the properties you're posting onto a single model class.
Something like the following should do the trick:
public class SaveModel
{
public List<EntryViewModel> Entries{get;set;}
public int ProjectId {get;set;}
}
Don't forget to decorate the model with the [FromBody] attribute:
[HttpPost]
public JsonResult Save([FromBody]SaveViewModel model)
{
// code here
}
Hope this helps!
It's still rough but I made a Filter to mimic the feature.
public class OldMVCFilter : IActionFilter
{
public void OnActionExecuted(ActionExecutedContext context)
{
}
public void OnActionExecuting(ActionExecutingContext context)
{
if (context.HttpContext.Request.Method != "GET")
{
var body = context.HttpContext.Request.Body;
JToken token = null;
var param = context.ActionDescriptor.Parameters;
using (var reader = new StreamReader(body))
using (var jsonReader = new JsonTextReader(reader))
{
jsonReader.CloseInput = false;
token = JToken.Load(jsonReader);
}
if (token != null)
{
var serializer = new JsonSerializer();
serializer.DefaultValueHandling = DefaultValueHandling.Populate;
serializer.FloatFormatHandling = FloatFormatHandling.DefaultValue;
foreach (var item in param)
{
JToken model = token[item.Name];
if (model == null)
{
// try to cast the full body as the current object
model = token.Root;
}
if (model != null)
{
model = this.RemoveEmptyChildren(model, item.ParameterType);
var res = model.ToObject(item.ParameterType, serializer);
context.ActionArguments[item.Name] = res;
}
}
}
}
}
private JToken RemoveEmptyChildren(JToken token, Type type)
{
var HasBaseType = type.GenericTypeArguments.Count() > 0;
List<PropertyInfo> PIList = new List<PropertyInfo>();
if (HasBaseType)
{
PIList.AddRange(type.GenericTypeArguments.FirstOrDefault().GetProperties().ToList());
}
else
{
PIList.AddRange(type.GetTypeInfo().GetProperties().ToList());
}
if (token != null)
{
if (token.Type == JTokenType.Object)
{
JObject copy = new JObject();
foreach (JProperty jProp in token.Children<JProperty>())
{
var pi = PIList.FirstOrDefault(p => p.Name == jProp.Name);
if (pi != null) // If destination type dont have this property we ignore it
{
JToken child = jProp.Value;
if (child.HasValues)
{
child = RemoveEmptyChildren(child, pi.PropertyType);
}
if (!IsEmpty(child))
{
if (child.Type == JTokenType.Object || child.Type == JTokenType.Array)
{
// nested value has been checked, we add the object
copy.Add(jProp.Name, child);
}
else
{
if (!pi.Name.ToLowerInvariant().Contains("string"))
{
// ignore empty value when type is not string
var Val = (string)child;
if (!string.IsNullOrWhiteSpace(Val))
{
// we add the property only if it contain meningfull data
copy.Add(jProp.Name, child);
}
}
}
}
}
}
return copy;
}
else if (token.Type == JTokenType.Array)
{
JArray copy = new JArray();
foreach (JToken item in token.Children())
{
JToken child = item;
if (child.HasValues)
{
child = RemoveEmptyChildren(child, type);
}
if (!IsEmpty(child))
{
copy.Add(child);
}
}
return copy;
}
return token;
}
return null;
}
private bool IsEmpty(JToken token)
{
return (token.Type == JTokenType.Null || token.Type == JTokenType.Undefined);
}
}

asp.net mvc: .net core: how to map stored proc result set to class fields

I got this simple model class:
public class PrvProduct
{
[Key]
public Int32 ProductId
{
get; set;
}
public Int64 ProductLineId;
public String MfgPartNumber;
public String ProductName;
public String ProductDescription;
}
I'm trying to call a stored proc, using .net core, it works fine, returns a list of PrvProduct objects. problem is: their fields are empty, unless I fill them up myself in code. ProductId is always there, not sure why (maybe because i typed there the [key] attribute?) but the rest are not.
is there a simple way to map class fields to results sets, like in ado.net (i would just do SQLDataAdapter.Fill(MyDataTable) and the MyDataTable fields will have the values by field name)... or do i have to do option 2 below every time?
Many thanks!
string sqlQuery = "EXEC Maint.GetProductList '" + sNameFilter + "'";
//option 1: this gets no value in the fields of each PrvProduct (ProductId gets value maybe because its [key], the others don't)
IQueryable results = _context.Products.FromSql(sqlQuery).AsNoTracking();
//option 2: this works, but... do i have to do this for every stored proc i call, every field, or is there a beter way to map class fields to returned results fields?
List<PrvProduct> oList = new List<PrvProduct>();
using (var command = _context.Database.GetDbConnection().CreateCommand())
{
command.CommandText = sqlQuery;
command.CommandType = CommandType.Text;
_context.Database.OpenConnection();
using (var result = command.ExecuteReader())
{
while (result.Read())
{
// Map to your entity
oList.Add(new PrvProduct
{
ProductId = result.GetInt32(0),
ProductName = result.GetString(1)
});
}
}
}
In EF Core, if you execute a stored procedure using one of your DbSet entities then it will map it automatically. The problem is that in many case you need to map a stored procedure to a DTO, for example, and the DTO is not part of your DbSet entities. In those cases you need to go back in time and map it manually which is a waste of time.
In order to avoid mapping the data reader manually, I added a bunch of extension methods that do it for you. The code is not perfect and I'm still improving it but it's good enough in most of the cases.
Once you add the extensions methods I'm gonna describe below, you can use it like this:
return dbContext.Database.SqlQuery<SalesReportDTO>("spGetSalesReport",
SqlParameterBuilder.Build("customerId", customerId),
SqlParameterBuilder.Build("dateFrom", from),
SqlParameterBuilder.Build("dateTo", to)).ToList();
DatabaseFacadeExtensions: adds extensions methods to DatabaseFacade class, allowing you to call the method SqlQuery from dbContext.Database just like we used to do with Entity Framework 6.
public static class DatabaseFacadeExtensions
{
public static List<T> SqlQuery<T>(this DatabaseFacade database, string query, params SqlParameter[] parameters)
{
return SqlQuery<T>(database, query, null, CommandType.StoredProcedure, parameters);
}
public static List<T> SqlQuery<T>(this DatabaseFacade database, string query, CommandType commandType, params SqlParameter[] parameters)
{
return SqlQuery<T>(database, query, null, commandType, parameters);
}
public static List<T> SqlQuery<T>(this DatabaseFacade database, string query, int? commandTimeout, params SqlParameter[] parameters)
{
return SqlQuery<T>(database, query, commandTimeout, CommandType.StoredProcedure, parameters);
}
public static List<T> SqlQuery<T>(this DatabaseFacade database, string query, int? commandTimeout, CommandType commandType, params SqlParameter[] parameters)
{
using (var cmd = database.GetDbConnection().CreateCommand())
{
cmd.CommandText = query;
cmd.CommandType = commandType;
if (commandTimeout.HasValue)
{
cmd.CommandTimeout = commandTimeout.Value;
}
cmd.Parameters.AddRange(parameters);
if (cmd.Connection.State == System.Data.ConnectionState.Closed)
{
cmd.Connection.Open();
}
try
{
using (var reader = cmd.ExecuteReader())
{
return reader.MapToList<T>();
}
}
finally
{
cmd.Connection.Close();
}
}
}
}
DbDataReaderExtensions: adds extensions methods to DbDataReader class so it can map the data reader to your own clases.
public static class DbDataReaderExtensions
{
public static List<T> MapToList<T>(this DbDataReader dr)
{
var objList = new List<T>();
if (dr.HasRows)
{
bool isSingleValue = typeof(T).IsPrimitive || typeof(T) == typeof(string);
IEnumerable<PropertyInfo> props = null;
Dictionary<string, DbColumn> colMapping = null;
if (!isSingleValue)
{
props = typeof(T).GetRuntimeProperties();
colMapping = dr.GetColumnSchema()
.Where(x => props.Any(y => y.Name.ToLower() == x.ColumnName.ToLower()))
.ToDictionary(key => key.ColumnName.ToLower());
}
while (dr.Read())
{
T obj;
if (isSingleValue)
{
obj = (T)dr.GetValue(0);
}
else
{
obj = Activator.CreateInstance<T>();
foreach (var prop in props)
{
string propertyName = prop.Name.ToLower();
if (!colMapping.ContainsKey(propertyName))
{
continue;
}
var val = dr.GetValue(colMapping[propertyName].ColumnOrdinal.Value);
if (val != DBNull.Value)
{
// enum property
if (prop.PropertyType.IsEnum)
{
prop.SetValue(obj, Enum.ToObject(prop.PropertyType, val));
}
// nullable enum property
if (prop.PropertyType.IsGenericType && prop.PropertyType.GetGenericTypeDefinition() == typeof(Nullable<>) && Nullable.GetUnderlyingType(prop.PropertyType).IsEnum)
{
prop.SetValue(obj, Enum.ToObject(Nullable.GetUnderlyingType(prop.PropertyType), val));
}
else
{
prop.SetValue(obj, val);
}
}
}
}
objList.Add(obj);
}
}
return objList;
}
public static T MapToObject<T>(this DbDataReader dr)
{
var props = typeof(T).GetRuntimeProperties();
if (dr.HasRows)
{
var colMapping = dr.GetColumnSchema()
.Where(x => props.Any(y => y.Name.ToLower() == x.ColumnName.ToLower()))
.ToDictionary(key => key.ColumnName.ToLower());
if (dr.Read())
{
T obj = Activator.CreateInstance<T>();
foreach (var prop in props)
{
var val = dr.GetValue(colMapping[prop.Name.ToLower()].ColumnOrdinal.Value);
prop.SetValue(obj, val == DBNull.Value ? null : val);
}
return obj;
}
}
return default(T);
}
}
The next class is optional but I use to build parameters in a simpler way and it's needed in the example I described above:
public class SqlParameterBuilder
{
public static SqlParameter Build(string name, bool? value)
{
if (value.HasValue)
{
return new SqlParameter() { ParameterName = name, Value = value.Value };
}
return new SqlParameter() { ParameterName = name, Value = DBNull.Value };
}
public static SqlParameter Build(string name, int? value)
{
if (value.HasValue)
{
return new SqlParameter() { ParameterName = name, Value = value.Value };
}
return new SqlParameter() { ParameterName = name, Value = DBNull.Value };
}
public static SqlParameter Build(string name, string value)
{
if (value != null)
{
return new SqlParameter() { ParameterName = name, Value = value };
}
return new SqlParameter() { ParameterName = name, Value = DBNull.Value };
}
public static SqlParameter Build(string name, DateTime? value)
{
if (value != null)
{
return new SqlParameter { ParameterName = name, SqlDbType = SqlDbType.DateTime, Value = value };
}
return new SqlParameter() { ParameterName = name, Value = DBNull.Value };
}
public static SqlParameter Build(string name, Guid? value)
{
if (value.HasValue)
{
return new SqlParameter { ParameterName = name, SqlDbType = SqlDbType.UniqueIdentifier, Value = value };
}
return new SqlParameter() { ParameterName = name, Value = DBNull.Value };
}
public static SqlParameter Build(string name, int[] values)
{
SqlParameter par = new SqlParameter(name, SqlDbType.Structured);
par.TypeName = "dbo.IntParameterList";
DataTable dt = new DataTable();
dt.Columns.Add("id", typeof(int));
par.Value = dt;
if (values != null)
{
foreach (int value in values.Where(p => p != 0))
{
dt.Rows.Add(value);
}
}
return par;
}
public static SqlParameter Build(string name, string[] values, VarcharParameterListEnum varcharParameterListType = VarcharParameterListEnum.Varchar50)
{
SqlParameter par = new SqlParameter(name, SqlDbType.Structured);
switch(varcharParameterListType)
{
case VarcharParameterListEnum.Varchar15:
par.TypeName = "dbo.Varchar15ParameterList";
break;
case VarcharParameterListEnum.Varchar50:
par.TypeName = "dbo.Varchar50ParameterList";
break;
case VarcharParameterListEnum.Varchar100:
par.TypeName = "dbo.Varchar100ParameterList";
break;
case VarcharParameterListEnum.Varchar255:
par.TypeName = "dbo.Varchar255ParameterList";
break;
case VarcharParameterListEnum.Varchar510:
par.TypeName = "dbo.Varchar510ParameterList";
break;
}
DataTable dt = new DataTable();
dt.Columns.Add("textValue", typeof(string));
par.Value = dt;
if (values != null)
{
foreach (var value in values.Where(p => !string.IsNullOrWhiteSpace(p)))
{
dt.Rows.Add(value);
}
}
return par;
}
}

Entity framework: Db context scope and transaction

I have an MVC 5 web app which talks to my class library for db needs and the class library uses Entity Framework 6 for that.
Below are 2 methods from the class library.
Both of them are initiating a new context. How do I make 'em use only one context instead, without using class level variable?
Also, if these 2 methods were to save stuff, context.SaveChanges(), how can I wrap them both into one transaction, even if the save happens in different classes?
public int FindUnknownByName(string name, string language)
{
using (var context = new ScriptEntities())
{
int languageId = this.FindLanguage(language);
var script = context.scripts.Where(l => l.Name == name && l.Unknown == true && l.LanguageId == languageId).FirstOrDefault();
if (script != null)
{
return script.Id;
}
return 0;
}
}
public int FindLanguage(string language)
{
using (var context = new ScriptEntities())
{
var lang = context.languages.Where(l => l.Name == language).FirstOrDefault();
if (lang != null)
{
return lang.Id;
}
return 0;
}
}
Make the context a private class variable.
private ScriptEntities context = new ScriptEntities()
public int FindUnknownByName(string name, string language)
{
int languageId = this.FindLanguage(language);
var script = context.scripts.Where(l => l.Name == name && l.Unknown == true && l.LanguageId == languageId).FirstOrDefault();
if (script != null)
{
return script.Id;
}
return 0;
}
public int FindLanguage(string language)
{
var lang = context.languages.Where(l => l.Name == language).FirstOrDefault();
if (lang != null)
{
return lang.Id;
}
return 0;
}
Also implement IDisposable and dispose context.
You can use extension methods:
public int FindUnknownByName(this ScriptEntities context, string name, string language)
{
int languageId = context.FindLanguage(language);
var script = context.scripts.Where(l => l.Name == name && l.Unknown == true && l.LanguageId == languageId).FirstOrDefault();
if (script != null)
{
return script.Id;
}
return 0;.
}
public int FindLanguage(this ScriptEntities context, string language)
{
var lang = context.languages.Where(l => l.Name == language).FirstOrDefault();
if (lang != null)
{
return lang.Id;
}
return 0;
}
then:
using (var context = new ScriptEntities())
{
var language = context.FindUnknownByName('name', 'language')
}

Delete a context record from grid in kendo UI using LINQ

Hi Im using kendo ui grid in my project.
This is my code to insert records in database.
public static void Insert(StudentViewModel student)
{
student.StudentId = All().OrderByDescending(p => p.StudentId).First().StudentId + 1;
//All().Insert(0, student);
UniRegEntities uniRegEntities = new UniRegEntities();
Student stu =new Student();
stu.FName = student.FirstName;
stu.LName = student.LastName;
stu.Gender = uniRegEntities.Genders.Where(x => x.Title == student.Gender).FirstOrDefault();
stu.Id = student.StudentId;
uniRegEntities.Students.Add(stu);
uniRegEntities.SaveChanges();
}
And this is my update statement.
public static void Update(StudentViewModel student)
{
UniRegEntities context = new UniRegEntities();
var studentToUpdate = context.Students.Where(x => x.Id == student.StudentId).FirstOrDefault();
studentToUpdate.FName = student.FirstName;
studentToUpdate.LName = student.LastName;
studentToUpdate.Gender = context.Genders.Where(x => x.Title == student.Gender).FirstOrDefault();
context.SaveChanges();
}
Anyone can suggest me the delete method?
You can either get an entity from the DB and then delete it or create one and then delete it.
So:
var e = // Get
ctx.DeleteObject(e);
ctx.SaveChanges();
or
var e = new Foo() { FooId = id };
ctx.Entity.Attach(e);
ctx.DeleteObject(e);
ctx.SaveChanges();
Applied to your situation:
You are getting a record so you want to use DeleteObject()
public static void Update(StudentViewModel student)
{
UniRegEntities context = new UniRegEntities();
var studentToDelete = context.Students.Where(x => x.Id == student.StudentId).FirstOrDefault();
context.Students.DeleteObject(studentToUpdate);
context.SaveChanges();
}
context.Students.Remove(context.students.Single(x=>x.Id==student.Id));
Can you please try with below code snippet?
using (var db= new AppContext(ConnectionStr))
{
try
{
con.Configuration.AutoDetectChangesEnabled = false;
var o = new Student { StudentId = student.StudentId };
db.Students.Attach(o);
db.Students.Remove(o);
db.SaveChanges();
}
catch (Exception ex)
{
throw new Exception(ex.InnerException.Message);
}
finally
{
con.Configuration.AutoDetectChangesEnabled = true;
}
}

Resources