I have a online dataset that I am looping through to save it to a sqlite database. It works well, however the 'count' in which it is sequentially looping through is based on the ID column, however there are instances where the IDs (for the category that I am filtering on) are not sequential. I'd rather loop through row number or similar if possible - but I can't seem to find a way to do it. Below is my code:
Note the count starts at 793 because this is the ID of the first 'surface' categorised record
private async void updateAssetRegisterLocal()
{
try
{
var current = Connectivity.NetworkAccess;
if (current != NetworkAccess.Internet)
{
await DisplayAlert("No Internet Connectivity", "Move to a location with mobile coverage and try again", "OK");
}
else
{
UserDialogs.Instance.ShowLoading();
HttpClient client = new HttpClient();
var response = await client.GetStringAsync("https://XXXX.azurewebsites.net/api/assets");
var onlineAssets = JsonConvert.DeserializeObject<List<AssetRegister>>(response);
var assetsPrelim = onlineAssets
.Where(w => w.Asset_Category == "Surface")
.OrderBy(d => d.ID)
.ToList();
;
using (SQLiteConnection conn = new SQLiteConnection(App.FilePath))
{
try
{
conn.DropTable<AssetRegister>();
}
catch
{
await DisplayAlert("Unsuccessful", "Can't Access", "OK");
}
}
for (int count = 793; count <= (assetsPrelim.Count + 793); count++)
{
var assets = assetsPrelim.Where(x => x.ID == count);
var id = assets.Select(l => l.ID).FirstOrDefault();
var asset_ID = assets.Select(l => l.Asset_ID).FirstOrDefault();
var road_ID = assets.Select(l => l.Road_ID).FirstOrDefault();
var segmentString = assets.Select(l => l.AssetSegmentString).FirstOrDefault();
var hierarchy = assets.Select(l => l.Road_Hierarchy).FirstOrDefault();
var assetCategory = assets.Select(l => l.Asset_Category).FirstOrDefault();
var lat = assets.Select(l => l.Lat).FirstOrDefault();
var inspectionDue = assets.Select(l => l.Inspection_Due).FirstOrDefault();
var lon = assets.Select(l => l.Lon).FirstOrDefault();
var stdRoadId = assets.Select(l => l.StdRoadID).FirstOrDefault();
var assetType = assets.Select(l => l.Asset_Type).FirstOrDefault();
var inspectionDate = assets.Select(l => l.Last_Inspection_Date).FirstOrDefault();
using (SQLiteConnection con = new SQLiteConnection(App.FilePath))
{
con.CreateTable<AssetRegister>();
var assetsLocal = con.Table<AssetRegister>().ToList();
int arlID = assetsLocal.Count + 1;
AssetRegister arl = new AssetRegister()
{
ID = id,
Asset_ID = asset_ID,
Asset_Type = assetType,
Asset_Category = assetCategory,
Road_ID = road_ID,
Road_Hierarchy = hierarchy,
AssetSegmentString = segmentString,
Inspection_Due = inspectionDue,
StdRoadID = stdRoadId,
Lat = lat,
Lon = lon,
Last_Inspection_Date = inspectionDate,
};
using (SQLiteConnection conn = new SQLiteConnection(App.FilePath))
{
conn.CreateTable<AssetRegister>();
int rowsAdded = conn.Insert(arl);
conn.Close();
}
}
}
await DisplayAlert("Success", "Asset Register Updated on Device", "OK");
UserDialogs.Instance.HideLoading();
}
}
catch
{
await DisplayAlert("Error", "Could not get data - Report to System Adminstrator", "OK");
UserDialogs.Instance.HideLoading();
}
}
It looks like you can loop simply iterating using foreach
Instead of
for (int count = 793; count <= (assetsPrelim.Count + 793); count++)
{
var assets = assetsPrelim.Where(x => x.ID == count);
var id = assets.Select(l => l.ID).FirstOrDefault();
var asset_ID = assets.Select(l => l.Asset_ID).FirstOrDefault();
var road_ID = assets.Select(l => l.Road_ID).FirstOrDefault();
var segmentString = assets.Select(l => l.AssetSegmentString).FirstOrDefault();
var hierarchy = assets.Select(l => l.Road_Hierarchy).FirstOrDefault();
var assetCategory = assets.Select(l => l.Asset_Category).FirstOrDefault();
var lat = assets.Select(l => l.Lat).FirstOrDefault();
var inspectionDue = assets.Select(l => l.Inspection_Due).FirstOrDefault();
var lon = assets.Select(l => l.Lon).FirstOrDefault();
var stdRoadId = assets.Select(l => l.StdRoadID).FirstOrDefault();
var assetType = assets.Select(l => l.Asset_Type).FirstOrDefault();
var inspectionDate = assets.Select(l => l.Last_Inspection_Date).FirstOrDefault();
...
you can have
foreach (var asset in assetsPrelim)
{
var id = asset.ID;
var asset_ID = asset.Asset_ID;
var road_ID = assets.Road_ID;
var segmentString = asset.AssetSegmentString;
var hierarchy = asset.Road_Hierarchy;
var assetCategory = asset.Asset_Category;
var lat = asset.Lat;
var inspectionDue = asset.Inspection_Due;
var lon = asset.Lon;
var stdRoadId = asset.StdRoadID;
var assetType = asset.Asset_Type;
var inspectionDate = asset.Last_Inspection_Date;
...
Simply use foreach() instead of for() loop,
for (var asset in assetsPrelim)
{
var id = asset.Id;
var asset_ID = asset.Asset_ID;
var road_ID = assets.Road_ID;
//...
}
Related
I've implemented a Google Cloud Vision service in my backend code. I want to unit test the service but I don't know how to mock it.
Here's my current code that I'm going to mock. Is there a way to inject the ImageAnnotatorClient so I could mock it?
public class GoogleCloudVisionService : IGoogleCloudVision
{
private readonly GoogleCloudVisionSettings googleCloudVisionSettings;
private ImageAnnotatorClient client;
public GoogleCloudVisionService(IOptions<GoogleCloudVisionSettings> googleCloudVisionSettings)
{
this.googleCloudVisionSettings = googleCloudVisionSettings.Value.WithParsedKey();
var credential = GoogleCredential.FromJson(this.googleCloudVisionSettings.ApiJson);
ImageAnnotatorClientBuilder clientBuilder = new ImageAnnotatorClientBuilder();
clientBuilder.Credential = credential;
client = clientBuilder.Build();
}
public async Task<SafeSearchAnnotation> GetImageAnnotation(byte[] imageBytes)
{
var image = Image.FromBytes(imageBytes);
var annotation = await client.DetectSafeSearchAsync(image);
return annotation;
}
public async Task<bool> IsImageSafe(byte[] imageBytes)
{
var annotations = await GetImageAnnotation(imageBytes);
var isSafe = annotations.Adult < googleCloudVisionSettings.Annotations.Adult &&
annotations.Racy < googleCloudVisionSettings.Annotations.Racy &&
annotations.Violence < googleCloudVisionSettings.Annotations.Violence &&
annotations.Medical < googleCloudVisionSettings.Annotations.Medical &&
annotations.Spoof < googleCloudVisionSettings.Annotations.Spoof;
return isSafe;
}
}
Finally got a working code after looking some examples in our codebase
public class GoogleCloudVisionServiceUnitTests
{
private Infrastructure.GoogleCloud.Api api;
public GoogleCloudVisionServiceUnitTests()
{
var faker = new Faker<Infrastructure.GoogleCloud.Api>()
.RuleFor(x => x.Type, f => f.Random.String2(6))
.RuleFor(x => x.ProjectId, f => f.Random.String2(6))
.RuleFor(x => x.PrivateKeyId, f => f.Random.String2(6))
.RuleFor(x => x.PrivateKey, f => f.Random.String2(6))
.RuleFor(x => x.ClientEmail, f => f.Random.String2(6))
.RuleFor(x => x.ClientId, f => f.Random.String2(6))
.RuleFor(x => x.AuthUri, f => f.Random.String2(6))
.RuleFor(x => x.TokenUri, f => f.Random.String2(6))
.RuleFor(x => x.AuthProviderUrl, f => f.Random.String2(6))
.RuleFor(x => x.ClientCertUrl, f => f.Random.String2(6));
api = faker.Generate(1).First();
}
[Fact]
public async Task GivenSafeImage_ShouldReturnAnnotations()
{
// Arrange
var faker = new Faker();
var expectedAnnotation = new SafeSearchAnnotation
{
Adult = Likelihood.Likely,
Racy = Likelihood.Likely,
Medical = Likelihood.Likely,
Spoof = Likelihood.Likely,
Violence = Likelihood.Likely
};
var mockOptions = new Mock<ImageAnnotatorClient>();
mockOptions.Setup(mock => mock.DetectSafeSearchAsync(It.IsAny<Image>(), It.IsAny<ImageContext>(), It.IsAny<CallSettings>()))
.ReturnsAsync(expectedAnnotation);
var json = JsonConvert.SerializeObject(api);
byte[] bytes = Encoding.Default.GetBytes(json);
var googleCloudVisionSettings = new GoogleCloudVisionSettings
{
Key = Convert.ToBase64String(bytes),
Annotations = new Annotations
{
Adult = Likelihood.Likely,
Racy = Likelihood.Likely,
Medical = Likelihood.Likely,
Spoof = Likelihood.Likely,
Violence = Likelihood.Likely
}
};
// Act
var googleCloudVisionService = new GoogleCloudVisionService(mockOptions.Object, Options.Create(googleCloudVisionSettings));
var annotations = await googleCloudVisionService.GetImageAnnotation(faker.Random.Bytes(512));
// Assert
annotations.Adult.Should().Be(googleCloudVisionSettings.Annotations.Adult);
annotations.Racy.Should().Be(googleCloudVisionSettings.Annotations.Racy);
annotations.Medical.Should().Be(googleCloudVisionSettings.Annotations.Medical);
annotations.Spoof.Should().Be(googleCloudVisionSettings.Annotations.Spoof);
annotations.Violence.Should().Be(googleCloudVisionSettings.Annotations.Violence);
}
[Fact]
public async Task GivenSafeImage_ShouldReturnTrue()
{
// Arrange
var faker = new Faker();
var expectedAnnotation = new SafeSearchAnnotation
{
Adult = Likelihood.VeryUnlikely,
Racy = Likelihood.VeryUnlikely,
Medical = Likelihood.VeryUnlikely,
Spoof = Likelihood.VeryUnlikely,
Violence = Likelihood.VeryUnlikely
};
var mockOptions = new Mock<ImageAnnotatorClient>();
mockOptions.Setup(mock => mock.DetectSafeSearchAsync(It.IsAny<Image>(), It.IsAny<ImageContext>(), It.IsAny<CallSettings>()))
.ReturnsAsync(expectedAnnotation);
var json = JsonConvert.SerializeObject(api);
byte[] bytes = Encoding.Default.GetBytes(json);
var googleCloudVisionSettings = new GoogleCloudVisionSettings
{
Key = Convert.ToBase64String(bytes),
Annotations = new Annotations
{
Adult = Likelihood.Likely,
Racy = Likelihood.Likely,
Medical = Likelihood.Likely,
Spoof = Likelihood.Likely,
Violence = Likelihood.Likely
}
};
// Act
var googleCloudVisionService = new GoogleCloudVisionService(mockOptions.Object, Options.Create(googleCloudVisionSettings));
var isSafe = await googleCloudVisionService.IsImageSafe(faker.Random.Bytes(512));
// Assert
Assert.True(isSafe);
}
[Fact]
public async Task GivenUnsafeImage_ShouldReturnFalse()
{
// Arrange
var faker = new Faker();
var expectedAnnotation = new SafeSearchAnnotation
{
Adult = Likelihood.VeryLikely,
Racy = Likelihood.VeryLikely,
Medical = Likelihood.VeryLikely,
Spoof = Likelihood.VeryLikely,
Violence = Likelihood.VeryLikely
};
var mockOptions = new Mock<ImageAnnotatorClient>();
mockOptions.Setup(mock => mock.DetectSafeSearchAsync(It.IsAny<Image>(), It.IsAny<ImageContext>(), It.IsAny<CallSettings>()))
.ReturnsAsync(expectedAnnotation);
var json = JsonConvert.SerializeObject(api);
byte[] bytes = Encoding.Default.GetBytes(json);
var googleCloudVisionSettings = new GoogleCloudVisionSettings
{
Key = Convert.ToBase64String(bytes),
Annotations = new Annotations
{
Adult = Likelihood.Likely,
Racy = Likelihood.Likely,
Medical = Likelihood.Likely,
Spoof = Likelihood.Likely,
Violence = Likelihood.Likely
}
};
// Act
var googleCloudVisionService = new GoogleCloudVisionService(mockOptions.Object, Options.Create(googleCloudVisionSettings));
var isSafe = await googleCloudVisionService.IsImageSafe(faker.Random.Bytes(512));
// Assert
Assert.False(isSafe);
}
}
To make it mock-able, I need to add a new constructor so I can pass an instance of Mock<ImageAnnotatorClient>
public GoogleCloudVisionService(ImageAnnotatorClient client, IOptions<GoogleCloudVisionSettings> googleCloudVisionSettings)
{
this.client = client;
this.googleCloudVisionSettings = googleCloudVisionSettings.Value.WithParsedKey();
}
So I'm trying to use a DTO to reshape and return data, it's not working because I'm trying to push in an array of objects (as an IQueryable - which I don't think works) into the DTO, I'm also trying to push in dynamic data into one of the properties, as seen below in the 'hasCurrentUserLiked' property. I need to figure out How to change the objects from IQueryable, into actual objects so they can all be pushed into the DTO and the dynamic data can be worked out.
This is the code
public async Task<PagedList<UserPhoto>> GetSpecificFeed(UserParams userParams)
{
var user = _context.Users.FirstOrDefault(x => x.Username == userParams.u);
var userId = user.Id;
var photos = _context.UserPhotos;
var followerIds = _context.Follows.Where(x => x.FollowerId == userId).Select(x => x.FollowingId).ToList();
var feeds = _context.UserPhotos.Where(x => followerIds.Contains(x.UserId)).OrderByDescending(x => x.DateAdded);
// this doesn't work because 'feeds' is an IQueryable, not an object
var like = await hasCurrentUserLiked(user.Id, feeds.id);
// this has the same problem as above
var feedsToReturn = new FeedsForReturnDto
{
Id = feeds.Id,
PhotoUrl = feeds.photoUrl,
Username = feeds.Username,
Description = feeds.Description,
DateAdded = feeds.DateAdded,
IsImage = feeds.IsImage,
hasCurrentUserLiked = like,
Likes = feeds.Likes
}
return await PagedList<UserPhoto>.CreateAsync(feedsToReturn, userParams.PageNumber, userParams.PageSize);
}
I thought that I might be able to get each image.id in a similar way the 'followerIds' are worked out but I couldn't figure out how to get this to work
[EDIT]
As per Enas Osamas answer, I've changed the code to this and I've debugged it, feedsToReturn has the correct info, so it is doing what its supposed to do. The problem I'm having now is that I'm unable to return it as it can't convert the IEnumerable to an IQueryable. I tried adding an explicit cast but that didn't work, I also tried removing the PagedList, and replacing the type to but this didn't work. My is an IQueryable which might be the problem, I tried changing that to an IEnumerable but this would mess up the code in other places.
This is my new code (still returning 'feeds' instead of 'feedsToReturn', will change when it works)
public async Task<PagedList<UserPhoto>> GetSpecificFeed(UserParams userParams)
{
var user = _context.Users.FirstOrDefault(x => x.Username == userParams.u);
var userId = user.Id;
var photos = _context.UserPhotos;
var followerIds = _context.Follows.Where(x => x.FollowerId == userId).Select(x => x.FollowingId).ToList();
var feeds = _context.UserPhotos.Where(x => followerIds.Contains(x.UserId)).OrderByDescending(x => x.DateAdded);
var feedToReturn = feeds.AsEnumerable().Select(feed => new FeedsForReturnDto
{
Id = feed.Id,
PhotoUrl = feed.photoUrl,
Username = feed.Username,
Description = feed.Description,
DateAdded = feed.DateAdded,
IsImage = feed.IsImage,
hasCurrentUserLiked = hasCurrentUserLiked(user.Id, feed.Id),
Likes = feed.Likes
});
return await PagedList<UserPhoto>.CreateAsync(feeds, userParams.PageNumber, userParams.PageSize);
}
I also tried changing some of the types around and this is what I came up with. The problem here is this:
'System.Collections.Generic.IEnumerable<cartalk.api.Dtos.feeds.FeedsForReturnDto>' to 'System.Linq.IQueryable<System.Collections.IEnumerable>'
and this problem is with the 'feedsToReturn' in the last line
public async Task<PagedList<IEnumerable>> GetSpecificFeed(UserParams userParams)
{
var user = _context.Users.FirstOrDefault(x => x.Username == userParams.u);
var userId = user.Id;
var photos = _context.UserPhotos;
var followerIds = _context.Follows.Where(x => x.FollowerId == userId).Select(x => x.FollowingId).ToList();
var feeds = _context.UserPhotos.Where(x => followerIds.Contains(x.UserId)).OrderByDescending(x => x.DateAdded);
var feedToReturn = feeds.AsEnumerable().Select(feed => new FeedsForReturnDto
{
Id = feed.Id,
PhotoUrl = feed.photoUrl,
Username = feed.Username,
Description = feed.Description,
DateAdded = feed.DateAdded,
IsImage = feed.IsImage,
hasCurrentUserLiked = hasCurrentUserLiked(user.Id, feed.Id),
Likes = feed.Likes
});
return await PagedList<IEnumerable>.CreateAsync(feedToReturn, userParams.PageNumber, userParams.PageSize);
}
PagedList code
public static async Task<PagedList<T>> CreateAsync(IQueryable<T> source,
int pageNumber, int pageSize)
{
var count = await source.CountAsync();
var items = await source.Skip((pageNumber - 1) * pageSize).Take(pageSize).ToListAsync();
return new PagedList<T>(items, count, pageNumber, pageSize);
}
[EDIT]
This is the hasCurrentUserLiked function, it works here
public bool checkCurrentUserLiked(int currentUserId, int imageId)
{
var doesCurrentUserLike = _context.PhotoLikes.Where(x => x.LikerId == currentUserId && x.ImageId == imageId);
var value = true;
if (doesCurrentUserLike == null)
{
value = false;
}
else
{
value = true;
}
return value;
}
You can try something like
feeds.AsEnumerable().Select(feed => new FeedsForReturnDto
{
Id = feed.Id,
PhotoUrl = feed.photoUrl,
Username = feed.Username,
Description = feed.Description,
DateAdded = feed.DateAdded,
IsImage = feed.IsImage,
hasCurrentUserLiked = hasCurrentUserLiked(user.Id, feed.id),
Likes = feed.Likes
});
This would return an IEnumerable containing all the feeds mapped to your DTO after being enumerated to avoid performing operations against the database
[EDIT]
you can maybe do a left join with the _context.PhotoLikes.
Like this
feeds.GroupJoin(_context.PhotoLikes,
f => new { Id = f.Id, UserId = user.Id },
p => new { Id = p.ImageId, UserId = p.LikerId },
(f, p) => new { feed = f, photoLike = p })
.SelectMany(f => f.photoLike.DefaultIfEmpty(),
(f, p) => new FeedsForReturnDto
{
Id = f.feed.Id,
PhotoUrl = f.feed.photoUrl,
Username = f.feed.Username,
Description = f.feed.Description,
DateAdded = f.feed.DateAdded,
IsImage = f.feed.IsImage,
hasCurrentUserLiked = p != null,
Likes = feed.Likes
});
Just note that in this part
f => new { Id = f.Id, UserId = user.Id },
p => new { Id = p.ImageId, UserId = p.LikerId },
the datatypes of properties in both objects must match or it won't compile
I need to refactor the below code so that the deleted_at logic will be outside of the foreach (var app in data) loop. I tried to create the list guids and then add guids to it but its not working because model.resources is inside the loop and it is still deleting all the apps.
I need deleted_at logic outside because I'm trying to delete all apps which are in the database but are not in new data that I'm receiving from API.
If you have a better approach on my code I would love to see that, Thank you.
public async Task GetBuilds()
{
var data = new List<GetBuildTempClass>();
var guids = new List<GetBuildTempClass>();
using (var scope = _scopeFactory.CreateScope())
{
var _DBcontext = scope.ServiceProvider.GetRequiredService<PCFStatusContexts>();
foreach (var app in data)
{
var request = new HttpRequestMessage(HttpMethod.Get,
"apps/" + app.AppGuid + "/builds?per_page=200&order_by=updated_at");
var response = await _client_SB.SendAsync(request);
var json = await response.Content.ReadAsStringAsync();
BuildsClass.BuildsRootObject model =
JsonConvert.DeserializeObject<BuildsClass.BuildsRootObject>(json);
foreach (var item in model.resources)
{
var x = _DBcontext.Builds.FirstOrDefault(o =>
o.Guid == Guid.Parse(item.guid));
if (x == null)
{
_DBcontext.Builds.Add(new Builds
{
Guid = Guid.Parse(item.guid),
State = item.state,
CreatedAt = item.created_at,
UpdatedAt = item.updated_at,
Error = item.error,
CreatedByGuid = Guid.Parse(item.created_by.guid),
CreatedByName = item.created_by.name,
CreatedByEmail = item.created_by.email,
AppGuid = app.AppGuid,
AppName = app.AppName,
Foundation = 2,
Timestamp = DateTime.Now
});
}
else if (x.UpdatedAt != item.updated_at)
{
x.State = item.state;
x.UpdatedAt = item.updated_at;
x.Timestamp = DateTime.Now;
}
var sqlresult = new GetBuildTempClass
{
AppGuid = Guid.Parse(item.guid)
};
guids.Add(sqlresult);
}
//var guids = model.resources.Select(r => Guid.Parse(r.guid));
var builds = _DBcontext.Builds.Where(o =>
guids.Contains(o.Guid) == false &&
o.Foundation == 2 && o.DeletedAt == null);
foreach (var build_item in builds)
{
build_item.DeletedAt = DateTime.Now;
}
}
await _DBcontext.SaveChangesAsync();
}
}
I got it working I added var guids = new List < Guid > (); list to store data,
added guids.Add(Guid.Parse(item.guid)); to populate the list and finally wrote var builds = _DBcontext.Builds.Where(o = >guids.Contains(o.Guid) == false && o.Foundation == 2 && o.DeletedAt == null); outside the loop.
If anyone has a better suggestion please add a new answer.
public async Task GetBuilds() {
var data = new List < GetBuildTempClass > ();
var guids = new List < Guid > ();
using(var scope = _scopeFactory.CreateScope()) {
var _DBcontext = scope.ServiceProvider.GetRequiredService < PCFStatusContexts > ();
foreach(var app in data) {
var request = new HttpRequestMessage(HttpMethod.Get, "apps/" + app.AppGuid + "/builds?per_page=200&order_by=updated_at");
var response = await _client_SB.SendAsync(request);
var json = await response.Content.ReadAsStringAsync();
BuildsClass.BuildsRootObject model = JsonConvert.DeserializeObject < BuildsClass.BuildsRootObject > (json);
foreach(var item in model.resources) {
var x = _DBcontext.Builds.FirstOrDefault(o = >o.Guid == Guid.Parse(item.guid));
if (x == null) {
_DBcontext.Builds.Add(new Builds {
Guid = Guid.Parse(item.guid),
State = item.state,
CreatedAt = item.created_at,
UpdatedAt = item.updated_at,
Error = item.error,
CreatedByGuid = Guid.Parse(item.created_by.guid),
CreatedByName = item.created_by.name,
CreatedByEmail = item.created_by.email,
AppGuid = app.AppGuid,
AppName = app.AppName,
Foundation = 2,
Timestamp = DateTime.Now
});
}
else if (x.UpdatedAt != item.updated_at) {
x.State = item.state;
x.UpdatedAt = item.updated_at;
x.Timestamp = DateTime.Now;
}
guids.Add(Guid.Parse(item.guid));
}
}
var builds = _DBcontext.Builds.Where(o = >guids.Contains(o.Guid) == false && o.Foundation == 2 && o.DeletedAt == null);
foreach(var build_item in builds) {
build_item.DeletedAt = DateTime.Now;
}
await _DBcontext.SaveChangesAsync();
}
}
I want to sum of all Volume & TotalTimes Column after Group BY from DataTable Alias as Temp_DT. Remember Volume & All TotalTimes Column may have null or blank value.
Can anyone help me to get the code through Linq Method
Sample Temp_DT:
Result should be in this format
I have tried the below code, but Temp_DT result is Null.
var Temp_DT = tempdt.AsEnumerable().Select(x =>
new
{
UID = x.Field<string>("UID"),
EMPNAME = x.Field<string>("Emp Name"),
EMPROLE = x.Field<string>("Role"),
SUPID = x.Field<string>("Sup ID"),
SUPNAME = x.Field<string>("Sup Name"),
DESIGNATION = x.Field<string>("Designation"),
VOLUME = x.Field<int>("Volume"),
LOGINTIME = x.Field<TimeSpan>("Login Time"),
BREAKTIME = x.Field<TimeSpan>("Break Time"),
HANDLETIME = x.Field<TimeSpan>("Handle Time"),
ACTIVETIME = x.Field<TimeSpan>("Active Time"),
HOLDTIME = x.Field<TimeSpan>("Hold Time"),
ACWTIME = x.Field<TimeSpan>("ACW"),
IDLETIME = x.Field<TimeSpan>("Idle"),
PRODUCTIVE = x.Field<TimeSpan>("Productive"),
NONPRODUCTIVE = x.Field<TimeSpan>("Non Productive"),
USERERROR = x.Field<TimeSpan>("User Error Time")
}).GroupBy(s => new { s.UID, s.EMPNAME, s.EMPROLE, s.SUPID, s.SUPNAME, s.DESIGNATION })
.Select(g => new
{
g.Key.UID,
g.Key.EMPNAME,
g.Key.EMPROLE,
g.Key.SUPID,
g.Key.SUPNAME,
g.Key.DESIGNATION,
VOLUME = g.Sum(x => Convert.ToInt16(x.VOLUME)),
LOGINTIME = new TimeSpan(g.Sum(x => x.LOGINTIME.Ticks)),
BREAKTIME = new TimeSpan(g.Sum(x => x.BREAKTIME.Ticks)),
HANDLETIME = new TimeSpan(g.Sum(x => x.HANDLETIME.Ticks)),
ACTIVETIME = new TimeSpan(g.Sum(x => x.ACTIVETIME.Ticks)),
HOLDTIME = new TimeSpan(g.Sum(x => x.HOLDTIME.Ticks)),
ACWTIME = new TimeSpan(g.Sum(x => x.ACWTIME.Ticks)),
IDLETIME = new TimeSpan(g.Sum(x => x.IDLETIME.Ticks)),
PRODUCTIVE = new TimeSpan(g.Sum(x => x.PRODUCTIVE.Ticks)),
NONPRODUCTIVE = new TimeSpan(g.Sum(x => x.NONPRODUCTIVE.Ticks)),
USERERROR = new TimeSpan(g.Sum(x => x.USERERROR.Ticks)),
});
You have to Parse the TimeSpan and Sum by using Ticks, like below.
var Temp_DT = tempdt.AsEnumerable().Select(x =>
new
{
UID = x["UID"],
EMPNAME = x["Emp Name"],
EMPROLE = x["Role"],
SUPID = x["Sup ID"],
SUPNAME = x["Sup Name"],
DESIGNATION = x["Designation"],
VOLUME = x["Volume"],
ERRORTIME = x["Error_Time"],
ACWTIME = x["ACW"],
BREAKTIME = x["Break Time"],
IDLETIME = x["Idle"],
NONPRODUCTIVE = x["Non Productive"],
}).GroupBy(s => new { s.UID, s.EMPNAME, s.EMPROLE, s.SUPID, s.SUPNAME, s.DESIGNATION })
.Select(g => {
var grouped = g.ToList();
return new
{
UID = g.Key.UID,
EMPNAME = g.Key.EMPNAME,
EMPROLE = g.Key.EMPROLE,
SUPID = g.Key.SUPID,
SUPNAME = g.Key.SUPNAME,
DESIGNATION = g.Key.DESIGNATION,
VOLUME = grouped.Sum(x => Convert.ToInt16(x.VOLUME)),
Error_Time = new TimeSpan(grouped.Select(x => ConvertTimeSpan(x.ERRORTIME.ToString())).ToList().Sum(r=> r.Ticks)),
ACW = new TimeSpan(grouped.Select(x => ConvertTimeSpan(x.ACWTIME.ToString())).ToList().Sum(r=> r.Ticks)),
Break = new TimeSpan(grouped.Select(x => ConvertTimeSpan(x.BREAKTIME.ToString())).ToList().Sum(r=> r.Ticks)),
Idle = new TimeSpan(grouped.Select(x => ConvertTimeSpan(x.IDLETIME.ToString())).ToList().Sum(r=> r.Ticks)),
NonProductive = new TimeSpan(grouped.Select(x => ConvertTimeSpan(x.NONPRODUCTIVE.ToString())).ToList().Sum(r=> r.Ticks))
};
}).ToList();
The Conversion method is,
private static TimeSpan ConvertTimeSpan(string str)
{
TimeSpan outputValue;
TimeSpan.TryParse(str, out outputValue);
return outputValue;
}
C# Fiddle with sample data.
I'm trying to reduce the amount of code in my application.
I want to turn this
return _db.SnapshotQueues
.Include(c => c.SnapshotDefinition)
.Include(c => c.SnapshotDefinition.Client)
.Include(c => c.Server)
.Select(s => new SnapshotQueueModel()
{
SnapshotQueueID = s.SnapshotQueueID,
SnapshotDefinitionID = s.SnapshotDefinitionID,
ScheduleID = s.ScheduleID,
DateCreated = s.DateCreated,
Protected = s.Protected,
StartTime = s.StartTime,
FinishTime = s.FinishTime,
Processed = s.Processed,
CoreSnapshotDatabaseName = s.CoreSnapshotDatabaseName,
Removed = s.Removed,
SnapshotID = s.SnapshotID,
EmailNotification = s.EmailNotification,
Email = s.Email,
ServerID = s.ServerID,
DateRequested = s.DateRequested,
Canceled = s.Canceled,
Active = s.Active,
LastSnapshotQueueActionID = s.SnapshotQueueActions.OrderByDescending(o => o.ActionDate).ThenByDescending(o => o.SnapshotQueueActionID).FirstOrDefault().ActionID,
LastAction = s.SnapshotQueueActions.OrderByDescending(o => o.ActionDate).ThenByDescending(o => o.SnapshotQueueActionID).FirstOrDefault().SnapshotAction.Description,
SnapshotLogCount = s.SnapshotGenerationLogs.Count(),
SnapshotDefinition = s.SnapshotDefinition.Name,
Server = s.Server.ServerName,
ScheduleName = s.Schedule.Name,
ClientName = s.SnapshotDefinition.Client.ClientName_Long
}
)
.Where(s => s.SnapshotDefinitionID == snapshotDefinitionID)
.OrderBy(sortFieldExpression);
into this
return _db.SnapshotQueues
.Include(c => c.SnapshotDefinition)
.Include(c => c.SnapshotDefinition.Client)
.Include(c => c.Server)
.Select(s => _snapshotQueueModelGet(s))
.Where(s => s.SnapshotDefinitionID == snapshotDefinitionID)
.OrderBy(sortFieldExpression);
private readonly Func<SnapshotQueue, SnapshotQueueModel> _snapshotQueueModelGet = s => new SnapshotQueueModel
{
SnapshotQueueID = s.SnapshotQueueID,
SnapshotDefinitionID = s.SnapshotDefinitionID,
ScheduleID = s.ScheduleID,
DateCreated = s.DateCreated,
Protected = s.Protected,
StartTime = s.StartTime,
FinishTime = s.FinishTime,
Processed = s.Processed,
CoreSnapshotDatabaseName = s.CoreSnapshotDatabaseName,
Removed = s.Removed,
SnapshotID = s.SnapshotID,
EmailNotification = s.EmailNotification,
Email = s.Email,
ServerID = s.ServerID,
DateRequested = s.DateRequested,
Canceled = s.Canceled,
Active = s.Active,
LastSnapshotQueueActionID =
s.SnapshotQueueActions.OrderByDescending(o => o.ActionDate)
.ThenByDescending(o => o.SnapshotQueueActionID)
.FirstOrDefault()
.ActionID,
LastAction =
s.SnapshotQueueActions.OrderByDescending(o => o.ActionDate)
.ThenByDescending(o => o.SnapshotQueueActionID)
.FirstOrDefault()
.SnapshotAction.Description,
SnapshotLogCount = s.SnapshotGenerationLogs.Count(),
SnapshotDefinition = s.SnapshotDefinition.Name,
Server = s.Server.ServerName,
ScheduleName = s.Schedule.Name,
ClientName = s.SnapshotDefinition.Client.ClientName_Long
};
The problem is that it passes in to SQL Server, and the database doesn't know what to do with the function. I get the error
The LINQ expression node type 'Invoke' is not supported in LINQ to Entities.
I'm using the same select list in a few places, so it would stop bugs creeping in when field are added.
Declare your field as Expression<Func<SnapshotQueue, SnapshotQueueModel>>:
private readonly Expression<Func<SnapshotQueue, SnapshotQueueModel>> _snapshotQueueModelGet = s => new SnapshotQueueModel
{
SnapshotQueueID = s.SnapshotQueueID,
SnapshotDefinitionID = s.SnapshotDefinitionID,
ScheduleID = s.ScheduleID,
DateCreated = s.DateCreated,
Protected = s.Protected,
StartTime = s.StartTime,
FinishTime = s.FinishTime,
Processed = s.Processed,
CoreSnapshotDatabaseName = s.CoreSnapshotDatabaseName,
Removed = s.Removed,
SnapshotID = s.SnapshotID,
EmailNotification = s.EmailNotification,
Email = s.Email,
ServerID = s.ServerID,
DateRequested = s.DateRequested,
Canceled = s.Canceled,
Active = s.Active,
LastSnapshotQueueActionID =
s.SnapshotQueueActions.OrderByDescending(o => o.ActionDate)
.ThenByDescending(o => o.SnapshotQueueActionID)
.FirstOrDefault()
.ActionID,
LastAction =
s.SnapshotQueueActions.OrderByDescending(o => o.ActionDate)
.ThenByDescending(o => o.SnapshotQueueActionID)
.FirstOrDefault()
.SnapshotAction.Description,
SnapshotLogCount = s.SnapshotGenerationLogs.Count(),
SnapshotDefinition = s.SnapshotDefinition.Name,
Server = s.Server.ServerName,
ScheduleName = s.Schedule.Name,
ClientName = s.SnapshotDefinition.Client.ClientName_Long
};