LinqToSQL - Effecient handling of objects - Retrieving first/single iobject - c#

The last few weeks I have been working with LinqToSql, I love it and the community here is fantastic.
I created a query to find an Object in the Database and update the values, It seems to work fine, but the code doesn't look natural to me, I would imagine there is a better solution.
Please review the code below;
//Return a list of MyObject items
// irrelevant line, just to show what the objects are
List<MyObject> items = Factory.GetObjects();
foreach (var item in items)
{
var myObjects= from myobj in db.MyOjects
where myobj.id == item.Key
select myobj;
//Should ONLY find 1 object
//Below is the code I think we could optimize
if (myObjects.Count() == 1)
{
myObjects.First().propertyToChange1 = item.p1;
myObjects.First().propertyToChange2 = item.p2;
myObjects.First().dateAltered = DateTime.Now;
//Update DB
db.SubmitChanges();
}
}

A better solution is to use SingleOrDefault, like this:
var myObject = db.MyOjects.SingleOrDefault(myobj => myobj.id == item.Key);
if (myObject != null) {
myObject.propertyToChange1 = item.p1;
myObject.propertyToChange2 = item.p2;
myObject.dateAltered = DateTime.Now;
//Update DB
db.SubmitChanges();
}

you can also approach it with first or default in a regular query way
var myObjects= (from myobj in db.MyOjects
where myobj.id == item.Key
select myobj).FirstOrDefault();
if (myObjects != null)
{
myObjects.First().propertyToChange1 = item.p1;
myObjects.First().propertyToChange2 = item.p2;
myObjects.First().dateAltered = DateTime.Now;
//Update DB
db.SubmitChanges();
}

Related

Cannot implicitly convert type to System.Collections.Generic.List to System.DateTime

I need to add 2 columns(LastTaskCreatedDate & LastTaskUpdatedDate) for a table. I have the query for that 2coulums and I just need to call that query into my repo function.
My repo function already has a query inside it which get the other columns inside it. As i cant add another query inside, I creayed a function in it and added that query inside it.
Please look into the Screenshots for better clarity.
enter image description herepic- The query which gets those two columns inside.
This is the repo funtion which gets the table data inside that and here i need to add those 2 columns also
public WebStationResponse Filter(ProjectsFilter objFilter)
{
try
{
// Taking common project ids
var projectIds = _currentUser.AccessInfo.UserProjects.Select(up => up.ProjectID).ToList();
if (objFilter.ProjectID == null || objFilter.ProjectID.Count == 0)
{
objFilter.ProjectID = projectIds;
}
objFilter.ProjectID = objFilter.ProjectID.Intersect(projectIds).ToList();
objFilter.SetSqlPagingValues();
objFilter.CompanyID = _currentUser.AccessInfo.CompanyID;
objFilter.UserID = _currentUser.AccessInfo.UserID;
objFilter.ServerTimezone = DefaultRepository.ServerTimezoneOffset();
objFilter.UserTimezone = _currentUser.TimeZoneDetails.BaseUTCOffset;
string strQuery = Project.GetProjects(objFilter);
IList<ProjectDetails> lstProjectDetails = _dbContext.Database.Query<ProjectDetails>(strQuery,objFilter).ToList();
if (lstProjectDetails.Count == 0)
{
base.ResponseObject.ResponseId = (int)ResponseCode.RecordDoesnotExist;
return base.ResponseObject;
}
//
int intRecordCount = lstProjectDetails.Count;
if(objFilter.Pagination)
{
intRecordCount = _dbContext.Database.ExecuteScalar<int>(Common.RecordCount());
}
FillPermissions(ref lstProjectDetails);
FillProjectCompetencies(ref lstProjectDetails);
**FillLastDates(ref lstProjectDetails); **
base.FillResponseDetails(null,lstProjectDetails,null);
base.ResponseObject.RecordCount = intRecordCount;
}
catch (Exception ex)
{
base.FillResponseDetails(ex, null, null);
}
return base.ResponseObject;
}`your text`
This is the function I added and inside it has the query- GetLastTaskUpdateDates.
private void FillLastDates(ref IList<ProjectDetails> lstProjects)
{
if (lstProjects == null || lstProjects.Count == 0)
{
return;
}
//
var projectIds = lstProjects.Select(p => p.ProjectID).ToList();
string strQuery = Project.GetLastTaskUpdatedDates();
IList<ProjectDetails> lstDates = _dbContext.Database.Query<ProjectDetails>(strQuery, new { ProjectID = projectIds }).ToList();
//
ProjectDetails objProject = null;
for (int intIndex = 0; intIndex < lstProjects.Count; intIndex++)
{
objProject = lstProjects[intIndex];
lstProjects[intIndex].LastTaskCreated = lstDates.Where(c => c.ProjectID == objProject.ProjectID).ToList();
lstProjects[intIndex].LastTaskUpdated= lstDates.Where(c =>c.ProjectID == objProject.ProjectID).ToList();
}
}`your text`
If we look at last two lines, I am getting this error- Cannot implicitly convert type to System.Collections.Generic.List to System.DateTime
Need help!!, Please reply for any other clarity on the problem
Gave everything in the details of the problem
LastTaskCreate and LastTaskUpdated are DateTime and you try to put lists instead of DateTimes, it's normal.
You must get only one date from the list you have. I think what you're looking for is something like that (assuming that lstDates is a list of dates):
var projectDates = lstDates.Where(c => c.ProjectID == objProject.ProjectID).OrderBy(e => e); // Ordered IEnumerable (it's a list but a bit different) of dates from the oldest to the newest
lstProjects[intIndex].LastTaskCreated = projectDates.First(); // Gives the first elem of the enumerable, should be the date of creation
lstProjects[intIndex].LastTaskUpdated = projectDates.Last(); // Gives the last elem of the enumerable, should be the last update date
I didn't put the null checkers but you might want to add some to be sure that projectDates is not empty.
If lstDates is a list of projects, the code could be this:
var project = lstDates.FirstOrDefault(c => c.ProjectID == objProject.ProjectID);
if (project != null)
{
lstProjects[intIndex].LastTaskCreated = project.LastTaskCreated
lstProjects[intIndex].LastTaskUpdated = project.LastTaskUpdated
}
but it depends on what's inside ProjectDetails.
Hope it helped
Although I don't fully understand what's going on in your scenario, the error seems to be fairly explainatory?
lstProjects[intIndex].LastTaskCreated
expects a Datetime, whereas
lstDates.Where(c => c.ProjectID == objProject.ProjectID).ToList();
is a list of ProjectDetails.
I suspect you want something like:
lstProjects[intIndex].LastTaskCreated = lstDates.First(c => c.ProjectID == objProject.ProjectID).LastTaskCreated;

ORA-00904 Occasional Invalid identifier with linq query

I have two different linq expressions that are referencing the same column in the database. One works just fine, but the other throws an invalid identifier exception (ORA-00904).
Most of the questions I've found feature naked sql queries with some syntax errors. Others have to do with the entity model, but seeing as how it doesn't run into the issue in one query, I'm not convinced the issue is with the model.
The one that works:
public List<DateTime> GetAvailableDates()
{
var retData = new List<DateTime>();
using (var context = new CASTDbContext())
{
var result = context.SomeDataEntity.Select(x => x.CAPTURE_DATE).Distinct().ToList();
if(result != null && result.Count > 0)
{
retData = result;
}
}
return retData;
}
The one that doesn't work:
public List<SomeDataModel> GetSomeDataByDate(DateTime date)
{
var retData = new List<SomeDataModel>();
using (var context = new SomeDbContext())
{
var result = context.SomeDataEntity
.Where( y => DbFunctions.TruncateTime(y.CAPTURE_DATE) == date.Date).ToList(); // the line that's throwing the exception
if (result != null && result.Count > 0)
{
foreach (var item in result)
{
retData.Add(mapper.Map<SomeDataModel>(item));
}
}
}
return retData;
}
The issue ended up being a different part of the model, but just some info on Oracle perils:
The first query worked fine because it was only referencing one specific field that had a matching column in the database (oracle doesn't care about the rest of the model in that instance for some reason).
The second query didn't work because it was trying to pull every column from the table, and there was one field missing from the model.

Matching objects by property name and value using Linq

I need to be able to match an object to a record by matching property names and values using a single Linq query. I don't see why this shouldn't be possible, but I haven't been able to figure out how to make this work. Right now I can do it using a loop but this is slow.
Heres the scenario:
I have tables set up that store records of any given entity by putting their primary keys into an associated table with the key's property name and value.
If I have a random object at run-time, I need to be able to check if a copy of that object exists in the database by checking if the object has property names that match all of the keys of a record in the database ( this would mean that they would be the same type of object) and then checking if the values for each of the keys match, giving me the same record.
Here's how I got it to work using a loop (simplified a bit):
public IQueryable<ResultDataType> MatchingRecordFor(object entity)
{
var result = Enumerable.Empty<ResultDataType>();
var records = _context.DataBaseRecords
var entityType = entity.GetType();
var properties = entityType.GetProperties().Where(p => p.PropertyType.Namespace == "System");
foreach (var property in properties)
{
var name = property.Name;
var value = property.GetValue(entity);
if (value != null)
{
var matchingRecords = records.Where(c => c.DataBaseRecordKeys.Any(k => k.DataBaseRecordKeyName == name && k.DataBaseRecordValue == value.ToString()));
if (matchingRecords.Count() > 0)
{
records = matchingRecords;
}
}
}
result = (from c in records
from p in c.DataBaseRecordProperties
select new ResultDataType()
{
ResultDataTypeId = c.ResultDataTypeID,
SubmitDate = c.SubmitDate,
SubmitUserId = c.SubmitUserId,
PropertyName = p.PropertyName
});
return result.AsQueryable();
}
The last statement joins a property table related to the database record with information on all of the properties.
This works well enough for a single record, but I'd like to get rid of that loop so that I can speed things up enough to work on many records.
using System.Reflection;
public IQueryable<ResultDataType> MatchingRecordFor(object entity)
{
var records = _context.DataBaseRecords;
var entityType = entity.GetType();
var properties = entityType.GetProperties().Where(p => p.PropertyType.Namespace == "System");
Func<KeyType, PropertyInfo, bool> keyMatchesProperty =
(k, p) => p.Name == k.DataBaseRecordKeyName && p.GetValue(entity).ToString() == k.DataBaseRecordValue;
var result =
from r in records
where r.DataBaseRecordKeys.All(k => properties.Any(pr => keyMatchesProperty(k, pr)))
from p in r.DataBaseRecordProperties
select new ResultDataType()
{
ResultDataTypeId = r.ResultDataTypeId,
SubmitDate = r.SubmitDate,
SubmitUserId = r.SubmitUserId,
PropertyName = p.PropertyName
});
return result.AsQueryable();
}
Hopefully I got that query language right. You'll have to benchmark it to see if it's more efficient than your original approach.
edit: This is wrong, see comments

New to programming how to make this code more concise

Hi this is my first question so apologies if it is really basic - I am very new to programming!!!
Using c# in MVC I am trying to select object which has a Date property from entitymodel context. This date then selects the relevant Weight object and so on to get my list of "Set" objects.
The code works and does what I want but would like some general guidance on how to make this code more concise. Here is the code:
public ActionResult showDiary(string datein)
{
LocalTestEntities1 dblists = new LocalTestEntities1();
DateTime date = Convert.ToDateTime(datein);
IEnumerable<ExerciseDiary> diary = from o in dblists.ExerciseDiaries where o.Date == date select o;
var mydiary = diary.ToList();
ExerciseDiary thediary = mydiary[0];
IQueryable<Weight> weights = from o in dblists.Weights where o.DiaryID == thediary.ID select o;
var selectedWeight = weights.ToList();
Weight weight = selectedWeight[0];
IEnumerable<Set> sets = from x in dblists.Sets where x.WeightId == weight.WeightID select x;
return View(sets);
}
It seems that I am taking too many steps here. I know that I am only returning one object to diary. Is there a way to get this object from dblists without sending to an IEnumerable?
Using the First() method will make things a little more concise:
public ActionResult showDiary(string datein)
{
using (LocalTestEntities1 dblists = new LocalTestEntities1())
{
DateTime date = Convert.ToDateTime(datein);
var thediary = (from o in dblists.ExerciseDiaries
where o.Date == date
select o).First();
var weight = (from o in dblists.Weights
where o.DiaryID == thediary.ID
select o).First();
var sets = (from x in dblists.Sets
where x.WeightId == weight.WeightID
select x).ToList();
}
return View(sets);
}
You should also wrap your LINQ to Entities data access in a using block so it's properly disposed of.
There's always many ways to do things, but... I think the easiest way would be to use First() since you are always just grabbing the first result in a list.
Another way to make it a little cleaner is to put your LINQ statements on multiple lines like I did for sets.
You can also use var, which some people like and others don't to have the compiler infer the type. I did this with sets below. I feel it cleans up the code a bit when you have large declarations with IEnumerable and generics.
public ActionResult showDiary(string datein)
{
LocalTestEntities1 dblists = new LocalTestEntities1();
DateTime date = Convert.ToDateTime(datein);
ExerciseDiary thediary = dblists.ExerciseDiaries.First(o => o.Date == date);
Weight weight = dblists.Weights.First(o.DiaryID == thediary.ID);
var sets = from x in dblists.Sets
where x.WeightId == weight.WeightID
select x;
return View(sets);
}
IMO this is easier to read than what you had in your answer above.
Be careful using First() because it will throw an exception if there are no records.
public ActionResult showDiary(string datein)
{
using( var dblists = new LocalTestEntities1())
{
var date = Convert.ToDateTime(datein);
var thediary = dblists.ExerciseDiaries.First(o => o.Date == date);
var weight = dblists.Weights.First(o => o.DiaryID ==thediary.ID);
var sets = dblists.Sets.Where(x => x.WeightId == weight.WeightID).AsEnumerable();
return View(sets);
}
}
Warning: If it's possible the data wont always be there. Use FirstOrDefault instead and check for null values.

How to write simple code in LInq

I have the following in my NotSelectedList.
public List<TestModel> SelectedList = new List<TestModel>();
public List<TestModel>NotSelectedList = new List<TestModel>();
NotificationDetailsModel projects = new NotificationDetailsModel();
projects.ProjectID = Convert.ToInt32(Row["ProjectID"]);
projects.Valid= Convert.ToBoolean(Row["Validity"]);
NotSelectedList.Add(projects);
How can I write a simple code in LINQ to select from the NotSelectedList where Validity == True and store the data in SelectedList?
var query = from ns in NotSelectedList
from n in SelectedList
where ns.Valid && ns.ProjectID == n.ProjectID
select ns;
Hope this will help you
The following would select the items with Validity = true from NotSelectedList and place them in SelectedList:
SelectedList.AddRange(NotSelectedList.Where(item => item.Validity));
Try this:
var results = NotSelectedList.Where(x => x.Valid);
foreach (var item in results)
SelectList.Add(item);
Although for performance reasons you may be better off doing something like this:
foreach (var item in NotSelectedList)
{
if (item.Valid)
SelectList.Add(item);
}

Categories