I am using data caching in my asp.net application.
This is my interface for the ICacheProvider.cs
public interface ICacheProvider
{
object Get(string key);
void Set(string key, object data, int cacheTime);
bool IsSet(string key);
void Invalidate(string key);
}
This the way I'm using caching in the services.
public List<EmployeeLookUp> GetEmployeeLookUp(RecordStatusEnum recordStatusEnum, int locatioId)
{
var status = (int)recordStatusEnum;
var employeeData = Cache.Get("EmployeeLookUp") as IEnumerable;
if (employeeData == null)
{
var result = MyDb.Employee.Where(w => w.Status == status && w.LocationId == locatioId).ToList().Select(s => new EmployeeLookUp()
{
EmployeeId = s.EmployeeId,
DepartmentId = s.DepartmentId,
EmployeeValue = string.Format("{0}{1}{2} {3} {4}", "[", s.CustomEmployeeId, "]", s.FirstName, s.LastName)
}).ToList();
if (result.Any())
Cache.Set("EmployeeLookUp", result, 30);
return result;
}
return (List<EmployeeLookUp>) employeeData;
}
In the controller I'm using the returned of employees like this.
public ActionResult Index()
{
var employees = _employeeServices.GetEmployeeLookUp(RecordStatusEnum.Active, User.GetCemexUser().LocationId);
employees.Insert(0, new EmployeeLookUp() { EmployeeId = -1, EmployeeValue = "All" });
var complexRosterViewModel = new ComplexRosterViewModel
{
EmployeeList = new SelectList(employees, "EmployeeId", "EmployeeValue"),
ListEmployeeGroups = new SelectList(_employeeGroupServices.GetEmployeeGroup(RecordStatusEnum.Active, User.GetCemexUser().LocationId), "EmployeeGroupId", "Value"),
ListDepartments = new SelectList(_departmentService.GetDepartments(RecordStatusEnum.Active,User.GetCemexUser().LocationId),"DepartmentId","Value")
};
return View(complexRosterViewModel);
}
Now my problem is, when i'm reloading the page several times, the additional "All" option that I added additionally to the employee select list, has been added to cached object("EmployeeLookUp") several times. How this can be possible? I do not want the "All" option to be cached.
It happens because you're using a reference to cached object.
If you change the object, it will reflect the changes in cached data.
Asp.Net Cache, modify an object from cache and it changes the cached value
You must clone the object or create a new and copy the properties values (you can use AutoMapper to do it for you)
Hope it helps.
Related
I have a method that takes a List<Dictionary<string,object>> as a parameter. The plan is to use that parameter, but only update the values held in a particular class. Here is the (partially written) method
public async Task<Errors> UpdatePageForProject(Guid projectId, List<Dictionary<string, object>> data)
{
if (!IsValidUserIdForProject(projectId))
return new Errors { ErrorMessage = "Project does not exist", Success = false };
if (data.Count == 0)
return new Errors { ErrorMessage = "No data passed to change", Success = false };
var page = await _context.FlowPages.FirstOrDefaultAsync(t => t.ProjectId == projectId);
foreach (var d in data)
{
}
return new Errors { Success = true };
}
My original plan is to take each dictionary, check if the key and the property in page match and then alter the value (so I can pass in 1 dictionary or 8 dictionaries in the list and then alter page to save back to my entity database).
I'd rather not use reflection due to the speed hit (though C#9 is really fast, I'd still rather not use it), but I'm not sure how else this can be done. I did consider using AutoMapper to do this, but for now would rather not (it's a PoC, so it is possibly overkill)
If you want to do this without Reflection (which I agree is a good idea, not just for performance reasons) then you could use a "map" or lookup table with actions for each property.
var map = new Dictionary<string,Action<Page,object>>()
{
{ "Title", (p,o) => p.Title = (string)o },
{ "Header", (p,o) => p.Field1 = (string)o },
{ "DOB", (p,o) => p.DateOfBirth = (DateTime)o }
};
You can then iterate over your list of dictionaries and use the map to execute actions that update the page.
foreach (var dictionary in data)
{
foreach (entry in dictionary)
{
var action = map[entry.Key];
action(page, entry.Value);
}
}
I have this method:
public DemographicData GetDemographicByZipCode(string zipcode)
{
DemographicData demoData = new DemographicData();
using(var context = new DataContext())
{
var result = from item in context.Demographic
where item.ZipCode == zipcode
select item;
foreach (var data in result)
{
demoData.City = data.City;
demoData.State = data.State;
demoData.Zip = data.ZipCode;
}
}
return demoData;
}
I am attempting to write the method without the loop as indicated below but as apparent, it will not work because I cannot use an assignment operator within the expression tree.
public DemographicData GetDemographicByZipCode(string zipcode)
{
DemographicData demoData = null;
// Instantiate to new instance in the select method.
// I need to use this instance demoData
using(var context = new DataContext())
{
var result = from item in context.Demographic
where item.ZipCode == zipcode
select new DemographicData()
{
//assign data to instance member here.
};
}
return demoData;
}
No, you can't do that. But if your goal is for demoData to represent a single result from your query, then you can do something like this:
public DemographicData GetDemographicByZipCode(string zipcode)
{
DemographicData demoData = null;
using(var context = new DataContext())
{
demoData = (from item in context.Demographic
where item.ZipCode == zipcode
select new DemographicData()
{
Zip = item.ZipCode,
City = item.City,
State = item.State
}).FirstOrDefault();
}
//Do other stuff to demoData here, if needed
return demoData;
}
That uses FirstOrDefault to get the first one in the list (or null if there are none). In the loop in your example, you're just overwriting the values, so I assume you are only expecting one result.
Update: If you are expecting more than one result, then return IEnumerable<DemographicData>, like this:
public IEnumerable<DemographicData> GetDemographicByZipCode(string zipcode)
{
List<DemographicData> demoData = null;
using(var context = new DataContext())
{
demoData = (from item in context.Demographic
where item.ZipCode == zipcode
select new DemographicData()
{
Zip = item.ZipCode,
City = item.City,
State = item.State
}).ToList();
}
//Do other stuff to demoData here, if needed
return demoData;
}
Use List<DemographicData> and ToList() inside the method to force it to actually perform the query there. If you don't use ToList(), it will perform the query when the list is first accessed, which will be outside of the using, when your context is disposed. It might also complain about multiple enumerations, depending on your code.
try:
var result = from item in context.Demographic
where item.ZipCode == zipcode
select item;
This will work.
City = item.City,
State = item.State,
Zip = item.ZipCode
What is still wrong with your code is that you are returning single DemographicData object whereas result will be collection of DemographicData objects even if there is only one that fulfills the condition tem.ZipCode == zipcode.
If you are expecting only exactly one instance of DemographicData do it this way
var result = (from item in context.Demographic
.
(your query here)
.
).Single();
If you are expecting one or zero then replace Single() with FirstOrDefault(). If you are expecting a collection then the return type of that method should be IEnumerable<DemographicData> or IQueryable<DemographicData> or any other that suits you best. If you need list/ array then replace Single() with ToList()/ToArray().
I am trying to get a list of all entries in my database that a certain one of their bool properties is false. I used a foreach loop to get the list but I was hoping to find a more optimized way to do this.
this is the controller code I used:
private DataBaseEntities db = new DataBaseEntities();
public ActionResult ApproveUsersList()
{
List<ApproveUserViewModel> unapprovedUsers = new List<ApproveUserViewModel>();
foreach (User dbUser in db.Users)
{
if (!dbUser.Approved)
{
ApproveUserViewModel model = new ApproveUserViewModel();
unapprovedUsers.Add(model);
}
}
return View(unapprovedUsers.ToList());
}
Why not Linq?
var _unapprovedUsers= unapprovedUsers.Where(m => !m.Approved).ToList();
var found = db.Users.Where(w => !w.Approved).Select(s => new ApproveUserViewModel { Approved = s.Approved,... }).ToList();
unapprovedUsers.AddRange(found);
I have 2 tables in database
Subject(subjID, subjName, tchID)
Teacher(tchID, tchName)
How to get list of subjects which have tchID value from Sesstion state and show it in dropdownlist?
My controller:
public ActionResult GetListSubj()
{
db = new DatabaseMng();
Teacher tch = db.Teachers.Find(Session["tchID"].ToString());
ViewBag.subjID = new SelectList(db.Subjects, "subjID", "subjName");
return View();
}
In view:
...
#Html.DropDownList("subjID", String.Empty)
This is my code, it's not complete, because it return all subjects, but I want subjects have tchID from Session state in Login View:
[HttpPost]
public ActionResult Login(Teacher model, FormCollection f)
{
db = new DatabaseMng();
string id = f["txtID"];
string pw= f["txtPass"];
if (ModelState.IsValid)
{
Session["tchID"] = id;
return RedirectToAction("GetListSubj", "Teacher");
}
return View();
}
Currently you are creating the SelectList object using db.Subjects which is all the items in the Subject table.
Include a where clause when querying db.Subjects. You can use the value from session for your where clause.
var idFromSession = string.empty;
if (Session["tchID"] != null)
{
idFromSession = Session["tchID"].ToString();
}
var filterdSubjects = db.Subjects.Where(s=>s.tchID == idFromSession);
// Use filterdSubjects to build your dropdown.
Assuming tchID property is of string type. If it is numeric type(Int32/Int64), convert your session value to numeric type and use that in your where clause.
var idFromSession = 0;
if (Session["tchID"] != null)
{
idFromSession = Convert.ToInt32(Session["tchID"]);
}
var filterdSubjects = db.Subjects.Where(s=>s.tchID==idFromSession);
You might also consider to switch to a more robust strongly typed approach which uses view models to transfer data from your action methods to view rather than relying on dynamic stuff like ViewBag/ViewData.
I'm trying to run this simple query:
var appt = (from a in context.AppointmentSet
select new Appointment{ ModifiedOn = a.ModifiedOn}).First();
but I'm getting a compiler exception since ModifiedOn is readonly.
I could just return a, but then all the attributes of the Appointment entity will be returned, not just the ModifiedOn.
I could return new { a.ModifiedOn }, but then appt would be an AnonymousType and not an Appointment.
What's the suggested way to make this work?
Note, this is an example, assume that I'm returning more than just a single property from Appointment, and then there is a where criteria of some sort
I always do it like that:
var appt = (from a in context.AppointmentSet
select new Appointment {
Attributes =
{
{ "modifiedon", a.ModifiedOn },
{ "createdon", a.CreatedOn },
{ "createdby", a.CreatedBy }
}
})
.First();
It does not require any extra coding, I'm really surprised nobody posted that here yet.
This is the most efficient way I can think of:
Create a new constructor that accepts an AnonymousType (Object)
public Appointment(object anonymousType) : this()
{
foreach (var p in anonymousType.GetType().GetProperties())
{
var value = p.GetValue(anonymousType);
if (p.PropertyType == typeof(Guid))
{
// Type is Guid, must be Id
base.Id = (Guid)value;
Attributes["opportunityid"] = base.Id;
}
else if (p.Name == "FormattedValues")
{
// Add Support for FormattedValues
FormattedValues.AddRange((FormattedValueCollection)value);
}
else
{
Attributes[p.Name.ToLower()] = value;
}
}
}
Then call it like so:
var appt = (from a in context.AppointmentSet
select new Appointment(new { a.ModifiedOn })).First();
It has to reflect over all the properties of the AnoymousType, but it should work and has the added benefit of not having to rewrite the properties names each time.