Best way to write lingq for loop queries - asp.net

I am using oracleDB and i have view like below which contains employees and his managers.
empNo
FirstName
LastName
Manager
I need to select a person and all of his staff. IE
Person1 is Manager
-- Person1_1
----Person1_1_1
----Person1_1_2
-- Person1_1
When i login with the user of Person1, I need to get all of the persons above.
Here is my LINQ but it is too slow.. What is the efficient way to get the data ?
List<decimal> OrgPerson2 = new List<decimal>();
public List<decimal> getOrgPerson(decimal empNo)
{
List<decimal> OrgPerson = new List<decimal>();
OrgPerson.AddRange(db.CRM_PERSON_TITLE_V.Where(c => c.MANAGER == empNo).Select(c => (decimal)c.PERSONID).ToList());
var subPerson = db.CRM_PERSON_TITLE_V.ToList();
foreach (var item in OrgPerson)
{
OrgPerson2.Add(item);
var subPerson2 = subPerson.Where(c => c.MANAGER == item).Select(c => (decimal)c.PERSONID).ToList();
if (subPerson2 != null)
{
if (subPerson2.Count > 0)
{
getOrgPerson(item);
}
}
}
return OrgPerson2.Distinct().ToList();
}

Decided to try out your solution and as I suspected it threw a StackOverflowException for me. Recursive methods are pretty bad if you are not careful.
Here's my solution with a stack. The code is self-explanatory.
List<decimal> GetOrgPerson(decimal id)
{
Stack<Person> iter = new Stack<Person>();
List<decimal> result = new List<decimal>();
iter.Push(db.People.FirstOrDefault(p => p.ID == id));
while (iter.Count() > 0)
{
Person current = iter.Pop();
var subordinates = db.People.Where(p => p.ManagerID == current.ID);
foreach (var s in subordinates)
{
if (result.Contains(s.ID))
continue;
iter.Push(s);
result.Add(s.ID);
}
}
return result;
}
In my test-case People inherits IEnumerable interface.

Your query is just getting all the staff under a manager recursively, it can be greatly simplified:
public IEmunerable<decimal> getOrgPerson(decimal empNo)
{
foreach (var subEmpNo in db.CRM_PERSON_TITLE_V.Where(c => c.MANAGER == empNo).Select(c => (decimal)c.PERSONID))
{
yield return subEmpNo;
foreach (var subSubEmpNo in getOrgPerson(subEmpNo))
yield return subSubEmpNo;
}
}
You should not need Distinct() since each employee has only one manager.
Also, I assume you don't need a List<decimal> which you can add/remove items. Otherwise, you may need something like OrgPerson2 = getOrgPerson(empNo).ToList()

Related

Razor, ASP.NET Core, Entity Framework : updating some fields

I'm trying to update two entities at the same time but the change is not applying and I think that when I try to return the update entity it doesn't even found it.
Here is my Razor view:
public IActionResult OnPost()
{
if (!ModelState.IsValid)
{
return Page();
}
repositorioFamiliar.Actualizar(Familiar);
return RedirectToPage("/Familiares/DetalleFamiliar", new { IdPaciente = Familiar.IdPaciente });
}
Here is my update function:
public FamiliaresPer Actualizar(FamiliaresPer familiar)
{
var familiarActualizar = (from f in _context.Familiars.Where(p => p.IdFamiliar == familiar.IdFamiliar) select f).FirstOrDefault();
if (familiarActualizar != null)
{
familiarActualizar.Correo = familiar.Correo;
_context.Update(familiarActualizar);
_context.SaveChanges();
}
var personaActualizar = (from p in _context.Personas.Where(p => p.Id == familiar.IdPersona) select p).FirstOrDefault();
if (personaActualizar != null)
{
personaActualizar.Telefono = familiar.Telefono;
_context.Update(personaActualizar);
_context.SaveChanges();
}
var familiares = from p in _context.Familiars
from p1 in _context.Personas
where p.IdPaciente == familiar.IdPaciente
where p.IdPersona == p1.IdPersona
select new FamiliaresPer()
{
IdFamiliar = p.IdFamiliar,
IdPaciente = p.IdPaciente,
IdPersona = p1.IdPersona,
Id = p1.Id,
Nombres = p1.Nombres,
Apellidos = p1.Apellidos,
Genero = p1.Genero,
Telefono = p1.Telefono,
Parentesco = p.Parentesco,
Correo = p.Correo,
};
FamiliaresPer familiaresPer = familiares.FirstOrDefault();
return familiaresPer;
}
When I submit the form I get an error
NullReferenceException: Object reference not set to an instance of an object
And the link shows the IdPaciente = 0 when it should use the same IdPaciente of the updated entity (which the Id never changes).
In your OnPost( ) Action Method, you used repositorioFamiliar.Actualizar(Familiar);
but it looks like you didn't define 'Familiar'.
In addition, when I look at your code. I can give you an advice. Let's say your first update was done correctly and you got an error in the second update case. But you want both to be updated at the same time. Assume that the first object is updated in the database but the second one isn't. This is a problem, right? Unit of Work design pattern is very useful to solve this.
In brief, The approach should be to do SaveChanges() after both update processes are completed so there will be no changes in the database until both updates are completed.

ASP.NET & SQL Server: won't update but will append/insert

I am using a database-first approach with a custom html helper to get a state of a checkbox using ajax (without using form in the view). I have two tables:
Tbl_1 -> Id, state (true or false), name (name of checkbox)
Tbl_2 -> Id, user_guid, timestamp, Tbl_1Id (foreign_key)
When I do insert operations, it does without any problem but when I try to update it (based upon the logged in user as it also gets GUID, the table gets appended/inserted with new data).
My controller:
public ActionResult SetState(checkboxstate cbstate)
{
var UserId = new Guid(System.Security.Claims.ClaimsPrincipal.Current.FindFirst("sub").Value);
var ent = new StartopDatabaseEntities();
var cbs = ent.checkboxstates.Where(w => w.Name == "World").FirstOrDefault();
if (cbs == null) // when there are no records in the database
{
ent.checkboxstates.Add(cbstate);
ent.checkboxstateUpdates.SingleOrDefault(c => c.Id == cbstate.Id);
var cbsOp = new checkboxstateUpdates();
cbsOp.timestamp = DateTime.Now;
cbsOp.user_guid = UserId;
cbstate.checkboxstateUpdates.Add(cbsOp);
ent.SaveChanges();
} // record in database, update (I've only one user now, so has to update only this one)
else
{
var cbsOp = new checkboxstateUpdates(); // declare in global
var chc = new checkboxstate(); // to be declared in global
var newCbs = ent.checkboxstateUpdates.Include(c => c.checkboxstate).ToList();
foreach (var u in newCbs)
{
if(u.user_guid==UserId && u.CheckboxStateId == u.checkboxstate.Id)
{
chc.state = cbstate.state;
chc.name = cbstate.name;
ent.checkboxstates.Add(chc);
cbsOp.Tidspunkt = DateTime.Now;
cbsOp.OpdateretAfBruger = UserId;
ent.checkboxstateUpdates.Add(cbsOp);
ent.SaveChanges();
}
}
}
Can anyone explain please why it's not updating but appending/inserting same data with a new Id (primary key)? I have a simple view where Ajax sends a call to the controller with the state and name of the checkbox. I have also tried
Db.Entry(obj).state = EntityState.Modified
without any help
You have not written the code for the logic which want to achieve..
I am not clear on the logic of if block also but the else part can be fixed as following.
var newCbs = ent.checkboxstateUpdates.Include(c => c.checkboxstate).Where(u.user_guid == UserId).FirstOrDefault();
if(newCbs != null) {
newCbs.checkboxstate.state = cbstate.state;
newCbs.checkboxstate.name = cbstate.name;
newCbs.Tidspunkt = DateTime.Now;
newCbs.OpdateretAfBruger = UserId;
ent.SaveChanges();
}
Solved this with the help from #David & #Chetan:
I did some modify in the code as per David:
u.checkboxstate.state=cbstate.state;
u.checkboxstate.name=cbstate.name;
u.timestamp=DateTime.Now;
ent.saveChanges();
I was using the wrong logic i.e. getting instance of the class rather than the 'ent' object. Thanks guys for the help.

Get Meteor collection object instance by name

I have seen similar questions but I think my scenario is a bit different. Say I define a collection like this:
MyCol = new Meteor.Collection("myCol"
and I want to get a reference to 'MyCol' using the string 'myCol' - I have created the function below which seems to work:
function GetCollectionObject(name) {
for(var key in window) {
var value = window[key];
if (value instanceof Meteor.Collection) {
if (value._name == name) {
return value;
break;
}
}
}
return null;
}
Is this the only/best/most efficient way to do this?
Why don't you store your collections in a dictionary? It's way more efficient.
Dogs = new Meteor.Collection('dogs');
Cats = new Meteor.Collection('cats');
Alpacas = new Meteor.Collection('alpacas');
MyCollections = {
dogs: Dogs,
cats: Cats,
alpacas: Alpacas,
};
...
MyCollections['dogs'].doSomething();

Is this an inefficient way to compare data across multiple tables?

I am using the following code to first check if a string is located somewhere within a column in my database. If it is, I am then needing to check if a few additional criteria are met by looking at different parts of the database (can be seen in the code below). I am not sure if this is an efficient method for doing this or if there is a much simpler way:
(from my Controller)
[HttpPost]
public ActionResult Index(FormCollection sampleKey)
{
string code = sampleKey["sampleCode"];
ViewBag.code = code;
// Need to check if this code is active
var order = db.Orders.SingleOrDefault(
o => o.OrderCode == code
&& o.Active == true);
if (order == null)
{
//Invalid
}
else
{
var orderIdent = db.OrderDetails.SingleOrDefault(
p => p.OrderDetailId == order.OrderId);
var barIdent = db.Drink.SingleOrDefault(
q => q.EstablishmentsID == orderIdent.DrinksId);
var barName = db.Establishment.SingleOrDefault(
r => r.EstablishmentsId == barIdent.EstablishmentsID);
ViewBag.barId = barName.name;
ViewBag.sample = order.Email;
var custProfile = CustomProfile.GetUserProfile();
if (custProfile.OwnedBar != barName.name)
{
//Not a match
}
else
{
//Match
}
}
return View();
}
Is this something to worry about? Is there a more efficient way of performing the actions that I am currently performing? Should I change the first table that is referenced to include data from the table I ultimately compare it to to avoid what seems to be an inefficient way of comparing information from different tables?
You should check the SQL query that gets generated. You can do that by e.g. outputting the queries to the console, which is done by setting db.Log = Console.Out;. There should be a similar method to output to the web page in your case. The lazy nature of LINQ makes things difficult to predict.
Other than that, you could make your life much easier if you create foreign key relationships between your tables, i.e. OrderDetails has Orders.OrderId as a FK. This will allow Entity Framework to generate navigational properties for your database. With them your code would look like this:
[HttpPost]
public ActionResult Index(FormCollection sampleKey)
{
string code = sampleKey["sampleCode"];
var detail = db.Orders.Where(o => o.OrderCode == code && o.Active == true)
.Select(o => new {
OrderCode = o.OrderCode,
BarId = o.Drink.Establishment.Select(n => n.name),
Sample = o.Email
})
.SingleOrDefault();
if (detail != null)
{
ViewBag.code = detail.OrderCode;
ViewBag.barId = detail.BarId;
ViewBag.sample = detail.Sample;
var custProfile = CustomProfile.GetUserProfile();
if (custProfile.OwnedBar == detail.BarId)
{
//Match
}
else
{
//Not a match
}
}
else
{
//Invalid
}
return View();
}

The best way to build Dynamic LINQ query

Hi I am looking for best method for writing Dynamic LINQ query.
I have a function like
public IQueryable<Student> FindByAllStudents(int? id, string Name, int? CourseID, bool? IsActive) // like this way, all field values are passed
{
// code for compairision
return db.Student;
}
we can also write db.Students.where(predicate)
or
a query like
var students = from s in db.students where s.Name.Contains(Name)
s.ID.Equals(id)
//and so on....
So will this method works if i don't pass ID (i.e. Null)?
is proper way for all the datatypes?
The point is function can have all null values as a parameter for equivalence of select * from statement.
can any one help me to build best query with sample code?
Okay, it's not entirely clear what you want, but if you're trying to only add where clauses for the parameters which are non-null, you could do:
public IQueryable<Student> FindByAllStudents
(int? id, string name, int? courseID, bool? isActive)
{
IQueryable<Student> query = db.Student;
if (id != null)
{
query = query.Where(student => student.ID == id.Value);
}
if (name != null)
{
query = query.Where(student => student.Name.Contains(name));
}
if (courseID != null)
{
query = query.Where(student => student.CourseID == courseID.Value);
}
if (isActive != null)
{
query = query.Where(student => student.IsActive == isActive.Value);
}
return query;
}
I haven't tried that, and it's possible that LINQ to SQL would get confused by the code to find the value of the nullable value types. You may need to write code like this:
if (courseID != null)
{
int queryCourseID = courseID.Value;
query = query.Where(student => student.CourseID == queryCourseID);
}
It's worth trying the simpler form first though :)
Of course, all this gets a bit irritating. A helpful extension method could make life more concise:
public static IQueryable<TSource> OptionalWhere<TSource, TParameter>
(IQueryable<TSource> source,
TParameter? parameter,
Func<TParameter, Expression<Func<TSource,bool>>> whereClause)
where TParameter : struct
{
IQueryable<TSource> ret = source;
if (parameter != null)
{
ret = ret.Where(whereClause(parameter.Value));
}
return ret;
}
You'd then use it like this:
public IQueryable<Student> FindByAllStudents
(int? id, string name, int? courseID, bool? isActive)
{
IQueryable<Student> query = db.Student
.OptionalWhere(id, x => (student => student.ID == x))
.OptionalWhere(courseID, x => (student => student.CourseID == x))
.OptionalWhere(isActive, x => (student => student.IsActive == x));
if (name != null)
{
query = query.Where(student => student.Name.Contains(name));
}
return query;
}
Using a higher order function like this could get confusing if you're not really comfortable with it though, so if you're not doing very many queries like this you might want to stick with the longer but simpler code.

Resources