What can be used instead of Datatable in LINQ - c#

I have a SQL query that returns a Datatable:
var routesTable = _dbhelper.Select("SELECT [RouteId],[UserId],[SourceName],[CreationTime] FROM [Routes] WHERE UserId=#UserId AND RouteId=#RouteId", inputParams);
and then we can work with Datatable object of routesTable
if (routesTable.Rows.Count == 1)
{
result = new Route(routeId)
{
Name = (string)routesTable.Rows[0]["SourceName"],
Time = routesTable.Rows[0]["CreationTime"] is DBNull ? new DateTime() : Convert.ToDateTime(routesTable.Rows[0]["CreationTime"])
};
result.TrackPoints = GetTrackPointsForRoute(routeId);
}
I want to change this code to linq but I don't know how can I simulate Datatable in LINQ ,I wrote this part:
Route result = null;
aspnetdbDataContext aspdb = new aspnetdbDataContext();
var Result = from r in aspdb.RouteLinqs
where r.UserId == userId && r.RouteId==routeId
select r;
....
but I don't know how can I change this part:
if (routesTable.Rows.Count == 1)
{
result = new Route(routeId)
{
Name = (string)routesTable.Rows[0]["SourceName"],
Time = routesTable.Rows[0]["CreationTime"] is DBNull ? new DateTime() : Convert.ToDateTime(routesTable.Rows[0]["CreationTime"])
};
would you please tell me how can I do this?
EDIT
here you can see the whole block of code in original
public Route GetById(int routeId, Guid userId)
{
Route result = null;
var inputParams = new Dictionary<string, object>
{
{"UserId", userId},
{"RouteId", routeId}
};
var routesTable = _dbhelper.Select("SELECT [RouteId],[UserId],[SourceName],[CreationTime] FROM [Routes] WHERE UserId=#UserId AND RouteId=#RouteId", inputParams);
if (routesTable.Rows.Count == 1)
{
result = new Route(routeId)
{
Name = (string)routesTable.Rows[0]["SourceName"],
Time = routesTable.Rows[0]["CreationTime"] is DBNull ? new DateTime() : Convert.ToDateTime(routesTable.Rows[0]["CreationTime"])
};
result.TrackPoints = GetTrackPointsForRoute(routeId);
}
return result;
}
SELECT Function:
public DataTable Select(string query, Dictionary<string, object> parameters)
{
var dt = new DataTable();
using (_command = new SqlCommand(query, _connnection))
{
InitializeParametersAndConnection(parameters);
using (_adapter = new SqlDataAdapter(_command))
{
_adapter.Fill(dt);
}
}
return dt;
}
and the GetTrackPointsForRoute
private List<TrackPoint> GetTrackPointsForRoute(int routeId)
{
aspnetdbDataContext aspdb = new aspnetdbDataContext();
var result = new List<TrackPoint>();
var trackPointsTable = from t in aspdb.TrackPointlinqs
where t.RouteFK == routeId
select t;
foreach (var trackPointRow in trackPointsTable)
{
var trackPoint = new TrackPoint
{
Id = (int)trackPointRow.TrackPointId,
Elevation = Convert.ToSingle(trackPointRow.Elevation),
Latitude = Convert.ToDouble(trackPointRow.Latitude),
Longitude = Convert.ToDouble(trackPointRow.Longitude),
Time = trackPointRow.TrackTime is DBNull ? new DateTime() : (DateTime)trackPointRow.TrackTime
};
result.Add(trackPoint);
}
return result;
}

var firstRoute = aspdb.RouteLinqs
.Where(r => r.UserId == userId && r.RouteId == routeId)
.FirstOrDefault();
if (firstRoute == null)
{
return null;
}
else
{
return new Route(routeId)
{
Name = first.SourceName,
Time = first.CreationTime ?? new DateTime(),
TrackPoints = GetTrackPointsForRoute(routeId)
};
}
If this is LINQ to SQL you can simplify it further (this won't work with LINQ to Entity Framework though):
return aspdb.RouteLinqs
.Where(r => r.UserId == userId && r.RouteId == routeId)
.Select(r => new Route(routeId)
{
Name = r.SourceName,
Time = r.CreationTime ?? new DateTime(),
TrackPoints = GetTrackPointsForRoute(routeId)
})
.FirstOrDefault();
Note: You probably can replace GetTrackPointsForRoute with a join to the child table, meaning that the entire method can be done with a single call to the database, rather than one call to get the routes, and a second call to get the points. To do this you should learn about associations and joins in LINQ to SQL.

Related

Push multiple objects with dynamic data into a DTO and return it

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

Odoo rpc query with join in C#

I have this type of code to make a selection from Odoo Rpc:
XmlRpcClient client = new XmlRpcClient();
client.Url = LocalApplication.Url;
client.Path = "common";
// LOGIN BG
XmlRpcRequest requestLogin = new XmlRpcRequest("authenticate");
requestLogin.AddParams(LocalApplication.Db, LocalApplication.User, LocalApplication.Pass, XmlRpcParameter.EmptyStruct());
XmlRpcResponse responseLogin = client.Execute(requestLogin);
// READ
client.Path = "object";
// var x = client.Execute("select * from res_partner");
XmlRpcRequest requestSearch = new XmlRpcRequest("execute_kw");
requestSearch.AddParams(LocalApplication.Db, responseLogin.GetInt(),
LocalApplication.Pass, "res.partner", "search_read",
XmlRpcParameter.AsArray(
XmlRpcParameter.AsArray(
XmlRpcParameter.AsArray("is_agent", "=", "True".ToLower()))),
XmlRpcParameter.AsStruct(
XmlRpcParameter.AsMember("fields",
XmlRpcParameter.AsArray("name", "id", "phone", "email"))));
// SAVE
XmlRpcResponse responseSearch = client.Execute(requestSearch);
var agents = (responseSearch.GetObject() as List<object>);
Taswiq.Bussiness.Person p = new Person(LocalApplication.ConnectionString);
if (agents != null)
{
foreach (var a in agents)
{
Dictionary<string, object> agent = a as Dictionary<string, object>;
p.Name = agent["name"].ToString() == "False" ? "ND" : agent["name"].ToString();
p.Marca = Convert.ToInt32(agent["id"]);
p.Type = 1;
p.Phone = agent["phone"].ToString() == "False" ? "ND" : agent["phone"].ToString();
p.Email = agent["email"].ToString() == "False" ? "ND" : agent["email"].ToString();
MessageStruct result = p.Save();
if (result.HasErrors)
{
return result.Message;
}
}
return "Success";
}
All works fine, but now I need to make some join from this 2 tables like this:
Select *
from product_template as d
inner join product_product as r on r.product_tmpl_id = d.id;
I can't find anything but Python code.
Can anyone help me with that?
I think I found a solution. I do not know if is the best way but in my case it solved the problem.
I have made same list with every table that I need to go with join:
public List<object> lstStock = new List<object>();
public void PopulateStockList()
{
XmlRpcClient stock = new XmlRpcClient();
stock.Url = LocalApplication.Url;
stock.Path = "common";
//LOGIN BG
XmlRpcRequest requestLogin = new XmlRpcRequest("authenticate");
requestLogin.AddParams(LocalApplication.Db, LocalApplication.User,
LocalApplication.Pass,
XmlRpcParameter.EmptyStruct());
XmlRpcResponse responseLogin = stock.Execute(requestLogin);
//READ
stock.Path = "object";
XmlRpcRequest requestSearch = new XmlRpcRequest("execute_kw");
requestSearch.AddParams(LocalApplication.Db, responseLogin.GetInt(),
LocalApplication.Pass, "stock.quant", "search_read",
XmlRpcParameter.AsArray(
XmlRpcParameter.AsArray(
XmlRpcParameter.AsArray("id", ">", 0)
)
),
XmlRpcParameter.AsStruct(
XmlRpcParameter.AsMember("fields",
XmlRpcParameter.AsArray("product_id", "quantity", "in_date",
"owner_id"))
)
);
XmlRpcResponse responseSearch = stock.Execute(requestSearch);
lstStock = (responseSearch.GetObject() as List<object>);
}
Same way for every table that i need to join with.
And in the end, inside the foreach i made that join that i need to populate my sql table
public string DownloadRpcStock()
{
try
{
PopulateProdTemplate();
PopulateStockList();
PopulateProductList();
Stock s = new Stock(LocalApplication.ConnectionString);
foreach (var st in lstStock)
{
Dictionary<string, object> stc = st as Dictionary<string, object>;
var productId = stc["product_id"] as List<object>;
if (productId != null)
s.IdProduct = Convert.ToInt32(productId[0]); //Din lista care vine pe product_id imi iau valoarea dorita
s.IdStorage = 1;
s.StockQuantity = Convert.ToDouble(stc["quantity"]);
s.Rest = Convert.ToDouble(stc["quantity"]);
var prod = lstProduct.FirstOrDefault(
k => Convert.ToInt32((k as Dictionary<string, object>)["id"]) == Convert.ToInt32((stc["product_id"] as List<object>)[0])) as Dictionary<string, object>;
var prodTemplate =
lstProdTemplate.FirstOrDefault(
k => Convert.ToInt32((k as Dictionary<string, object>)["id"]) ==
Convert.ToInt32((prod["product_tmpl_id"] as List<object>)[0])) as Dictionary<string, object>;
if (prodTemplate != null)
s.Price = Convert.ToDouble(prodTemplate["list_price"]);
s.Batch = "NoBatch"; // Provizoriu
s.Date = Convert.ToDateTime(stc["in_date"]);
s.ExpireDate = Convert.ToDateTime(stc["in_date"]); // Provizoriu
s.IdCustomerSuplier = Convert.ToInt32(stc["owner_id"]);
s.Id = 0; //if (Id == 0) act = insert
MessageStruct result = s.Save();
if (result.HasErrors)
{
return result.Message;
}
}
return "Success";
}
catch (Exception ex)
{
return ex.Message;
}
}
I found many such questions on the internet without a solution.
I hope my solution will help someone else in the future.

Put LINQ query result to DataTable

I have this query:
var smallExchangeReport = from ex in exchangeProgReport
where !string.IsNullOrEmpty(ex.comment)
group ex by new { ex.siteName } into g
select new SummuryReportTraffic
{
siteName = g.Key.siteName,
exchangeCounter = g.Where(x => x.Prog1ToProg2Check == 1).Count(),
descriptions = (from t in g
group t by new { t.comment, t.siteName } into grp
select new Description
{
title = grp.Key.comment,
numbers = grp.Select(x => x.comment).Count()
})
};
At some point I put it to the dataTable using foreach loop:
foreach (var item in smallExchangeReport)
{
dr = smrTable.NewRow();
foreach (var d in item.descriptions)
{
dr[d.title] = d.numbers;
}
smrTable.Rows.Add(dr);
}
But I need to put the LINQ result to dataTable without using foreach loop.
So I made some changes to my code above according to this link:
DataTable dt = new DataTable();
DataRow dr = dt.NewRow();
IEnumerable<DataRow> smallExchangeReport = from ex in exchangeProgReport.AsEnumerable()
where !string.IsNullOrEmpty(ex.comment)
group ex by new { ex.siteName } into g
select new
{
siteName = g.Key.siteName,
exchangeCounter = g.Where(x => x.Prog1ToProg2Check == 1).Count(),
descriptions = (from t in g.AsEnumerable()
group t by new { t.comment, t.siteName } into grp
select new
{
title = grp.Key.comment,
numbers = grp.Select(x => x.comment).Count()
})
};
// Create a table from the query.
DataTable boundTable = smallExchangeReport.CopyToDataTable<DataRow>();
But on changed LINQ query I get this error:
Cannot implicitly convert type:'System.Collections.Generic.IEnumerable<<anonymous type: string siteName, int exchangeCounter>>' to
'System.Collections.Generic.IEnumerable<System.Data.DataRow>'. An explicit conversion exists (are you missing a cast?)
My question is how to cast the query to make it work?I tryed to cast to(DataRow) the result of the LINQ but it didn't worked.
In your LINQ query, you are trying to get IEnumerable<DataRow> as the result, but actually you select new objects of an anonymous type: select new { siteName = .... }. This cannot work because your anonymous type cannot be cast to DataRow.
What you need to do is use a function that would populate a DataRow like this:
DataRow PopulateDataRow(
DataTable table,
string siteName,
int exchangeCounter,
IEnumerable<Description> descriptions
{
var dr = table.NewRow();
// populate siteName and exchangeCounter
// (not sure how your data row is structured, so I leave it to you)
foreach (var d in descriptions)
{
dr[d.title] = d.numbers;
}
return dr;
}
then in your LINQ query, use it as follows:
IEnumerable<DataRow> smallExchangeReport =
from ex in exchangeProgReport.AsEnumerable()
where !string.IsNullOrEmpty(ex.comment)
group ex by new { ex.siteName } into g
select PopulateDataRow(
smrTable,
siteName: g.Key.siteName,
exchangeCounter: g.Where(x => x.Prog1ToProg2Check == 1).Count(),
descriptions: (from t in g.AsEnumerable()
group t by new { t.comment, t.siteName } into grp
select new Description {
title = grp.Key.comment,
numbers = grp.Select(x => x.comment).Count()
}
)
);
This solution gets rid of one foreach (on rows) and leaves the other one (on descriptions).
If removing the second foreach is important... I would still leave it inside PopulateDataRow. I don't see an elegant way to remove it. You can call a method from LINQ query which reads like a deterministic function, but actually creates the side effect of setting a column value on a data row, but it doesn't feel right to me.
this is can help you.
defining table structure.
DataTable tbl = new DataTable();
tbl.Columns.Add("Id");
tbl.Columns.Add("Name");
and we need to create datarow from anonymous type.
Func<object, DataRow> createRow = (object data) =>
{
var row = tbl.NewRow();
row.ItemArray = data.GetType().GetProperties().Select(a => a.GetValue(data)).ToArray();
return row;
};
test with fake query:
var enumarate = Enumerable.Range(0, 10);
var rows = from i in enumarate
select createRow( new { Id = i, Name = Guid.NewGuid().ToString() });
var dataTable = rows.CopyToDataTable<DataRow>();
You can use this method:
private DataTable ListToDataTable<T>(List<T> objs, string tableName) {
var table = new DataTable(tableName);
var lists = new List<List<object>>();
// init columns
var propertyInfos = new List<PropertyInfo>();
foreach (var propertyInfo in typeof(T).GetProperties()) {
propertyInfos.Add(propertyInfo);
if(propertyInfo.PropertyType.IsEnum || propertyInfo.PropertyType.IsNullableEnum()) {
table.Columns.Add(propertyInfo.Name, typeof(int));
} else {
table.Columns.Add(propertyInfo.Name, Nullable.GetUnderlyingType(propertyInfo.PropertyType) ?? propertyInfo.PropertyType);
}
table.Columns[table.Columns.Count - 1].AllowDBNull = true;
}
// fill rows
foreach(var obj in objs) {
var list = new List<object>();
foreach(var propertyInfo in propertyInfos) {
object currentValue;
if(propertyInfo.PropertyType.IsEnum || propertyInfo.PropertyType.IsNullableEnum()) {
var val = propertyInfo.GetValue(obj);
if(val == null) {
currentValue = DBNull.Value;
} else {
currentValue = (int)propertyInfo.GetValue(obj);
}
} else {
var val = propertyInfo.GetValue(obj);
currentValue = val ?? DBNull.Value;
}
list.Add(currentValue);
}
lists.Add(list);
}
lists.ForEach(x => table.Rows.Add(x.ToArray()));
return table;
}
Edit:
this extension method is used:
public static bool IsNullableEnum(this Type t) {
var u = Nullable.GetUnderlyingType(t);
return u != null && u.IsEnum;
}

LINQ Filtering Select Ouput with IEnumerable<T>

I have following methods:
Controller:
...
var appmap = Services.GetReqAppMapList(value);
var applist = Services.GetApplicationList(docid, appid, reqid, appmap);
...
Model:
public static IEnumerable<AppMap> GetReqAppMapList(int aiRequestTypeId)
{
try
{
var appmap = new List<AppMap>();
using (var eties = new eRequestsEntities())
{
appmap = (from ram in eties.ReqAppMaps
where ram.IsActive == 1
select new AppMap
{
RequestTypeId = ram.RequestTypeId
}).ToList();
return appmap;
}
}
catch(Exception e)
{
throw e;
}
}
public static IEnumerable<TicketApplication> GetApplicationList(int aiDocumentTypeId, int aiApplicationTypeId, int aiRequestTypeId, IEnumerable<AppMap> appmap)
{
try
{
var applicationlist = new List<TicketApplication>();
using (var applicationentity = new eRequestsEntities())
{
applicationlist = (from app in applicationentity.Applications
where 1==1
<<<Some Conditions Here???>>>
== && appmap.Contains(app.ApplicationTypeId) ==
&& app.IsActive == 1
select new TicketApplication
{
ApplicationId = app.ApplicationId,
Description = app.Description,
DeliveryGroupId = app.DeliveryGroupId,
ApplicationTypeId = app.ApplicationTypeId,
DeliveryTypeId = app.DeliveryTypeId,
DocumentTypeId = app.DocumentTypeId,
SupportGroupId = app.SupportGroupId
}).OrderBy(a => a.Description).ToList();
return applicationlist;
}
And I was thinking how can filter query result of GetApplicationList using the result from GetReqAppMapList
I'm kinda stuck with the fact that I must convert/cast something to the correct type because every time I do a result.Contains (appmap.Contains to be exact), I always get the following error
Error 4 Instance argument: cannot convert from
'System.Collections.Generic.IEnumerable<Test.Models.AppMap>' to
'System.Linq.ParallelQuery<int?>'
You should directly join the two tables in one query.
using (var applicationentity = new eRequestsEntities())
{
applicationlist = (from app in applicationentity.Applications
join ram in applicationentity.ReqAppMaps on app.ApplicationTypeId equals ram.RequestTypeId
where ram.IsActive == 1 && app.IsActive == 1
select new TicketApplication
{
ApplicationId = app.ApplicationId,
Description = app.Description,
DeliveryGroupId = app.DeliveryGroupId,
ApplicationTypeId = app.ApplicationTypeId,
DeliveryTypeId = app.DeliveryTypeId,
DocumentTypeId = app.DocumentTypeId,
SupportGroupId = app.SupportGroupId
}).OrderBy(a => a.Description).ToList();
You can delete the other method if it is not needed anymore. No point hanging onto code which is dead.
Looks like there is no other way to do this (as far as I know), so I have to refactor the code, I hope still that there would be a straight forward conversion and matching method in the future (too lazy). Anyway, please see below for my solution. Hope this helps someone with the same problem in the future. I'm not sure about the performance, but this should work for now.
Controller:
...
var appmap = Services.GetReqAppMapList(value);
var applist = Services.GetApplicationList(docid, appid, reqid, appmap);
...
Model:
<Removed GetReqAppMapList>--bad idea
public static IEnumerable<TicketApplication> GetApplicationList(int aiDocumentTypeId, int aiApplicationTypeId, int aiRequestTypeId)
{
try
{
//This is the magic potion...
List<int?> appmap = new List<int?>();
var applist = (from ram in applicationentity.ReqAppMaps
where ram.RequestTypeId == aiRequestTypeId
&& ram.IsActive == 1
select new AppMap
{
ApplicationTypeId = ram.ApplicationTypeId
}).ToList();
foreach (var item in applist)
{
appmap.Add(item.ApplicationTypeId);
}
//magic potion end
var applicationlist = new List<TicketApplication>();
using (var applicationentity = new eRequestsEntities())
{
applicationlist = (from app in applicationentity.Applications
where 1==1
===>>>&& appmap.Contains(app.ApplicationTypeId)<<<===
&& app.IsActive == 1
select new TicketApplication
{
ApplicationId = app.ApplicationId,
Description = app.Description,
DeliveryGroupId = app.DeliveryGroupId,
ApplicationTypeId =app.ApplicationTypeId,
DeliveryTypeId = app.DeliveryTypeId,
DocumentTypeId = app.DocumentTypeId,
SupportGroupId = app.SupportGroupId
}).OrderBy(a => a.Description).ToList();
return applicationlist;
}
A side-note, C# is a strongly-typed language, just make sure your data types matches during evaluation, as int? vs int etc.., will never compile. A small dose of LINQ is enough to send some newbies circling around for hours. One of my ID-10T programming experience but just enough to remind me that my feet's still flat on the ground.

MOQ Exception Was unhandled by user

I am pretty new to MOQ and I am in learning right now. I got task that I need to write unit test But I am getting the below error which I couldn't able to find. Please anyone help me with this. Also please review the code and give me some advises if my testcase needs changes.
Expection: IAcronisService.GetActivitiesFromDate("mygroup", 5/24/2016
8:33:34 PM) invocation failed with mock behavior Strict.....
for Save method(I have commented at save method where the exception has been thrown)
Process method calls save method
public int Process()
{
AcronisService = AcronisSvcManager.Get(ClientFactory, DataProviderFactory, LogAdapter);
DataObject backupGroupsDO = GetListOfAllCurrentGroups();
int activitiesSavedCount = Save(backupGroupsDO);
return activitiesSavedCount;
}
When I debug I also have seen the below line when I mouse over at first line on above process method. I thought service object is not calling/mocking. Is this anything to do with above exception? Please suggest some changes.
(IAcronisServiceProxy)AcronisService).AcronisURL threw a exception of type MOQ.MockException.
Save Method
private int Save(DataObject backupGroupsDO)
{
int count = 0;
foreach (DataRecord dr in backupGroupsDO.DataRecord)
{
BackupGroup backupGroup = new BackupGroup(dr);
// Get all activities for each group
AcronisClient.DataModel.Activity.ActivitiesResponse acronisActivities;
if (backupGroup.LastActivityDate == null)
{
// Get all activities for each group
***//Got Exception at this line***
acronisActivities = AcronisService.GetActivitiesFromDate(backupGroup.GroupName, DateTime.Now.AddDays(-90));
}
else
{
acronisActivities = AcronisService.GetActivitiesFromDate(backupGroup.GroupName, backupGroup.LastActivityDate);
}
if (acronisActivities == null || acronisActivities.Activities == null)
{
// Stop processing b/c might be an issue with the connection to Acronis
LogAdapter.LogError(KCBLog.Main, "Stopped processing activities, possible Acronis connection issue with getting Activities");
return -1;
}
var lastBackUpActivity = acronisActivities.Activities.OrderByDescending(z => z.StartTime).FirstOrDefault();
List<string> lastSuccessfulActivities = new List<string>();
List<string> lastActivities = new List<string>();
foreach (var acronisActivity in acronisActivities.Activities)
{
Kaseya.KCB.Common.DataModels.AcronisActivity activity = new AcronisActivity();
activity.BackupPlanId = acronisActivity.BackupPlanId;
activity.BytesProcessed = acronisActivity.BytesProcessed;
activity.BytesSaved = acronisActivity.BytesSaved;
activity.Cause = acronisActivity.CompletionResult == null ? null : acronisActivity.CompletionResult.Cause;
activity.Reason = acronisActivity.CompletionResult == null ? null : acronisActivity.CompletionResult.Reason;
activity.Effect = acronisActivity.CompletionResult == null ? null : acronisActivity.CompletionResult.Effect;
activity.DateCreated = DateTime.Now;
activity.FinishTime = acronisActivity.FinishTime;
activity.GroupId = backupGroup.Id;
activity.Id = acronisActivity.Id;
activity.InitiatedBy = acronisActivity.InitiatedBy;
activity.InstanceId = acronisActivity.InstanceId;
activity.IsRootActivity = (bool)acronisActivity.IsRootActivity;
activity.ParentActivityId = acronisActivity.ParentActivityId;
activity.PartitionId = PartitionId;
activity.StartTime = acronisActivity.StartTime;
activity.State = acronisActivity.State;
activity.Status = acronisActivity.Status;
activity.Title = acronisActivity.Title;
activity.UpdateTime = acronisActivity.UpdateTime;
AcronisActivityDataProvider.AddUpdateAcronisActivity(activity);
AcronisClient.DataModel.Activity.Activity lastSuccessfulActivity = acronisActivities.Activities.Where(z => z.Status == "ok" && z.Title.Contains("Running backup plan") && z.InstanceId==acronisActivity.InstanceId).OrderByDescending(z => z.FinishTime).FirstOrDefault();
var lastActivity = acronisActivities.Activities.Where(z => z.Title.Contains("Running backup plan") && z.InstanceId == acronisActivity.InstanceId).OrderByDescending(z => z.FinishTime).FirstOrDefault();
if (!string.IsNullOrEmpty(acronisActivity.InstanceId))
{
DataRecord assetDR = AssetDataProvider.GetByInstanceId(acronisActivity.InstanceId, PartitionId);
if (assetDR != null)
{
var assetId = assetDR.FindValue<string>("id", "");
if (lastSuccessfulActivity != null && !lastSuccessfulActivities.Contains(acronisActivity.InstanceId))
{
AssetDataProvider.UpdateLastSuccessfulActivityId(assetId, lastSuccessfulActivity.ParentActivityId);
lastSuccessfulActivities.Add(acronisActivity.InstanceId);
}
if (lastActivity != null && !lastActivities.Contains(acronisActivity.InstanceId))
{
AssetDataProvider.UpdateLastActivityId(assetId, lastActivity.ParentActivityId);
lastActivities.Add(acronisActivity.InstanceId);
}
}
}
count++;
}
if (acronisActivities.Activities != null && acronisActivities.Activities.Count>0)
{
//backupGroup.LastActivityDate = lastBackUpActivity.StartTime;
BackupGroupDataProvider.UpdateLastBackupGroupActivityDate(backupGroup.Id, lastBackUpActivity.StartTime);
}
}
return count;
}
Test Method I have writtern,
public void Test()
{
string groupName = "mygroup";
string mybackupAccountName = "mybackupaccount";
decimal PartitionId = 9m;
DateTime lastActivityDate = DateTime.Parse("2016-08-14T20:47:05");
string instanceId = "utinstanceId";
string assetId = "123";
DataRecord asset = new DataRecord();
asset.AddField("id", 123);
DataObject backupGroupsDO = new DataObject();
DataRecord groupDataRecord = new DataRecord();
groupDataRecord.AddField("id", 123);
groupDataRecord.AddField("partitionId", PartitionId);
groupDataRecord.AddField("groupName", groupName);
//groupDataRecord.AddField("lastActivityDate", lastActivityDate);
groupDataRecord.AddField("backupAccountName", mybackupAccountName);
backupGroupsDO.DataRecord.Add(groupDataRecord);
AcronisActivity acronisActivity = new AcronisActivity();
acronisActivity.BackupPlanId = "utBackupPlanId";
ActivitiesResponse activitiesResponse = new ActivitiesResponse();
AcronisClient.DataModel.Activity.Activity activity = new AcronisClient.DataModel.Activity.Activity();
activity.BackupPlanId = "utackupPlanId";
activity.BytesProcessed = 124674;
activity.BytesSaved = 06446;
activity.CompletionResult = new CompletionResult()
{
Cause = "utCause",
Reason = "utReason",
Effect = "utEffect"
};
activity.FinishTime = DateTime.Parse("2016-08-14T20:47:04");
activity.Id = "utId";
activity.InitiatedBy = "utInitiatedBy";
activity.InstanceId = "utInstanceId";
activity.IsRootActivity = true;
activity.ParentActivityId = "utParentActivityId";
activity.StartTime = DateTime.Parse("2016-08-14T20:47:02");
activity.State = "utState";
activity.Status = "utStatus";
activity.Title = "utTitle";
activity.UpdateTime = DateTime.Parse("2016-08-14T20:47:03");
activitiesResponse.Activities = new List<AcronisClient.DataModel.Activity.Activity>();
activitiesResponse.Activities.Add(activity);
var moqFactory = new MockRepository(MockBehavior.Strict);
var moqDataProviderFactory = moqFactory.Create<IDataProviderFactory>();
var moqDataProvider = moqFactory.Create<IDataProvider>();
var moqLogAdapter = moqFactory.Create<ILogAdapter>();
var moqAcronisServiceManager = moqFactory.Create<IAcronisServiceManager>();
var moqAcronisService = moqFactory.Create<IAcronisService>();
var moqAssetDataProvider = moqFactory.Create<IAssetDataProvider>();
var moqAcronisActivityDataProvider = moqFactory.Create<IAcronisActivityDataProvider>();
var moqBackupGroupDataProvider = moqFactory.Create<IBackupGroupDataProvider>();
Credential MSPCredential = new Credential();
moqDataProviderFactory.Setup(m => m.BackupGroupDataProvider.GetBackupGroups()).Returns(backupGroupsDO);
moqAcronisServiceManager.Setup(m => m.Get(It.IsAny<IRestClientFactory>(), It.IsAny<IDataProviderFactory>(), It.IsAny<ILogAdapter>(), "")).Returns(moqAcronisService.Object);
moqDataProvider.Setup(m => m.VerifyPartitionId(ref PartitionId));
moqDataProvider.Setup(m => m.ExecuteNonQuery(It.IsAny<AddUpdateAcronisActivity>())).Returns(1);
moqAcronisService.Setup(m => m.GetActivitiesFromDate(groupName, lastActivityDate)).Returns(activitiesResponse);
moqAcronisActivityDataProvider.Setup(m => m.AddUpdateAcronisActivity(acronisActivity));
moqAssetDataProvider.Setup(m => m.GetByInstanceId(instanceId, PartitionId,1)).Returns(asset);
moqAssetDataProvider.Setup(m => m.UpdateLastActivityId(assetId, activity.ParentActivityId));
moqAssetDataProvider.Setup(m => m.UpdateLastSuccessfulActivityId(assetId, activity.ParentActivityId));
moqBackupGroupDataProvider.Setup(m => m.UpdateLastBackupGroupActivityDate("1234", lastActivityDate));
// moqAcronisService.Setup(m => m.GetActivitiesFromDate(groupName, Convert.ToDateTime("2016-08-18T13:18:40.000Z"))).Returns(activitiesResponse);
ActivityHarvester activityHarvester = new ActivityHarvester();
activityHarvester.PartitionId = PartitionId;
activityHarvester.DataProvider = moqDataProvider.Object;
activityHarvester.LogAdapter = moqLogAdapter.Object;
activityHarvester.AcronisSvcManager = moqAcronisServiceManager.Object;
activityHarvester.DataProviderFactory = moqDataProviderFactory.Object;
activityHarvester.AcronisService = moqAcronisService.Object;
activityHarvester.AssetDataProvider = moqAssetDataProvider.Object;
activityHarvester.BackupGroupDataProvider = moqBackupGroupDataProvider.Object;
activityHarvester.AcronisActivityDataProvider = moqAcronisActivityDataProvider.Object;
activityHarvester.process();//*process method calls above save method method*
moqFactory.VerifyAll();
}
From your example, your setup for IAcronisService.GetActivitiesFromDate shows that it is expecting lastActivityDate of 2016-08-14T20:47:05 based on the code but the error shows that you used a different date 5/24/2016 8:33:34 PM than expected. As the moq behavior is Strict, this
Causes the mock to always throw an exception for invocations that don't have a corresponding setup.
You can make the setup a little more flexible by using It.IsAny<DateTime>()
moqAcronisService
.Setup(m => m.GetActivitiesFromDate(groupName, It.IsAny<DateTime>()))
.Returns(activitiesResponse);
or changing the behavior in your moq factory to use Default or Loose MockBehavior.

Categories