I have a mongodb object as following:
public class Form
{
public string FormId { get; set; }
public boolean Status { get; set; } //Published or Draft
public int Version { get; set; } //The version of the same forms.
}
For the same FormId, there could be different version number and different Status. Some example data such as:
{
"FormId":"1",
"Status":true,
"Version":1
};
{
"FormId":"1",
"Status":true,
"Version":2
};
{
"FormId":"2",
"Status":true,
"Version":1
};
{
"FormId":"2",
"Status":true,
"Version":2
};
{
"FormId":"2",
"Status":false,
"Version":1
}
Now I want to get a list of forms that for the same FormId, the Status is true and the version number is the largest. So the query result should be:
{
"FormId":"1",
"Status":true,
"Version":2
}
and
{
"FormId":"2",
"Status":true,
"Version":2
}
I tried to write the query in C# using Lambda expression:
this.AsQueryable<Form>().Where(p=>p.Status == true)
.GroupBy(item=>item.FormId)
.Select(t=>t.OrderByDescending(c => c.Version).FirstOrDefault()).ToList();
But I got error:
Additional information: FirstOrDefault of type System.Linq.Enumerable is not supported in the expression tree {document}.OrderByDescending(c => c.Version).FirstOrDefault().
Does anyone know what is wrong with my code? By the way, how can I know what is supported or not supported in the expression tree?
I found out that if I write the query as following it works, but I believe this is not a good approach.
return this.AsQueryable<FormTemplateEntity>().Where(p=>p.Status == true).ToList()
.GroupBy(item => item.FormId).ToList()
.Select(t => t.OrderByDescending(c => c.Version).FirstOrDefault()).ToList();
Updated my syntax and tested to be working.
this.Where(x => x.Status)
.GroupBy(item => new { item.FormId, item.Status })
.Select(x =>
new Form
{
FormId = x.First().FormId,
Status = x.First().Status,
Version = x.OrderByDescending(c => c.Version).First().Version
}
).ToList();
Output:
[{"FormId":"1","Status":true,"Version":2},{"FormId":"2","Status":true,"Version":2}]
Actual Code used to test:
var _json =
"[{ \"FormId\":\"1\", \"Status\":true, \"Version\":1},{ \"FormId\":\"1\", \"Status\":true, \"Version\":2},{ \"FormId\":\"2\", \"Status\":true, \"Version\":1},{ \"FormId\":\"2\", \"Status\":true, \"Version\":2},{ \"FormId\":\"2\", \"Status\":false, \"Version\":1}]";
var js = new JavaScriptSerializer();
List<Form> resultList = js.Deserialize<List<Form>>(_json);
var groupedResult = resultList.Where(x => x.Status).GroupBy(item => new { item.FormId, item.Status })
.Select(x =>
new Form
{
FormId = x.First().FormId,
Status = x.First().Status,
Version = x.OrderByDescending(c => c.Version).First().Version
}
).ToList();
var output = js.Serialize(groupedResult);
public List<Form> GetFromDetails()
{
var DB = new Database();
var collection = DB.GetCollection<Form>();
var query = new QueryBuilder<Form>();
var queryLst = new List<IMongoQuery>();
// {
// "FormId":"1",
// "Status":true,
// "Version":1
//};
//{
// "FormId":"1",
// "Status":true,
// "Version":2
//};
List<Form> finalResult = new List<Form>();
finalResult = collection.FindAll().Where(x => x.Status == true).ToList(); // pasing id 1
// {
// "FormId":"1",
// "Status":true,
// "Version":1
//};
//{
// "FormId":"1",
// "Status":true,
// "Version":2
//};
var finalResults = finalResult.Select(n => new Form
{
FormId = n.FormId,
Status = n.Status,
Version = finalResult.OrderByDescending(v => v.Version).First().Version
});
// {
// "FormId":"1",
// "Status":true,
// "Version":2
//};
//{
// "FormId":"1",
// "Status":true,
// "Version":2
//};
DB.Dispose();
return finalResult;
}
Above Method will help and i'm done because i myself made a demo its working
It didn't work for me, but I could make it by using Sort before Group.
return _context.Products
.Aggregate()
.Sort(Builders<Product>.Sort.Descending("InsertedDate"))
.Group(x => x.Code, g => new {Code = g.Key, LastInsertedDate = g.First().InsertedDate })
.ToList();
Related
I have a foreach with an ordebydescending(), but in one case I need to use an orderby() instead. Depending on the value of articleType how can I use an inline condition inside the foreach to allow this to happen.
This is the condition I need to build into to determine the use of orderbydescending or orderby
if (articleType == BusinessLogic.ArticleType.Webinar)
This is the full function
public static List<Article> GetArticles(BusinessLogic.ArticleType articleType, long languageID)
{
List<Article> articles = new List<Article>();
using (var db = new DatabaseConnection())
{
foreach (var record in db
.GetArticles(BusinessLogic.SystemComponentID, (int) articleType, languageID)
.OrderByDescending(c => c.Date_Time))
{
#region articleTextLanguageID
long articleTextLanguageID = GetArticleTextLanguageID(record.ID, languageID);
string previewImageName = GetArticle(record.ID, articleTextLanguageID).PreviewImageName;
#endregion
Article article = new Article()
{
ID = record.ID,
Title = record.Title,
Summary = record.Summary,
PreviewImageName = previewImageName,
Date = record.Date_Time,
ArticleTextLanguageID = articleTextLanguageID
};
articles.Add(article);
}
}
return articles;
}
Was thinking something along these lines, but its not working
foreach (var record in db
.GetArticles(BusinessLogic.SystemComponentID, (int)articleType, languageID)
.Where(articleType == BusinessLogic.ArticleType.Webinar.ToString()?.OrderByDescending(c => c.Date_Time)) : .OrderBy(c => c.Date_Time)))
The way to do this would be to construct the query in pieces. For example:
var query = db.GetArticles(BusinessLogic.SystemComponentID, (int)articleType, languageID);
if(articleType == BusinessLogic.ArticleType.Webinar)
{
query = query.OrderByDescending(c => c.Date_Time);
}
else
{
query = query.OrderBy(c => c.Date_Time);
}
foreach(var record in query)
{
// process
}
If you required additional sorting, you'd need an extra variable typed as IOrderedEnumerable/IOrderedQueryable (depending on what GetArticles returns) as an intermediate to chain ThenBy/ThemByDescending:
var source = db.GetArticles(BusinessLogic.SystemComponentID, (int)articleType, languageID);
IOrderedEnumerable<Article> query;
if(articleType == BusinessLogic.ArticleType.Webinar)
{
query = source.OrderByDescending(c => c.Date_Time);
}
else
{
query = source.OrderBy(c => c.Date_Time);
}
if(somethingElse)
{
query = query.ThenBy(c => c.OtherProperty);
}
foreach(var record in query)
{
// process
}
Based on your comment below, as notes above the second example would look more like the following (this means that db.GetArticles returns an IQueryable<Article> and not an IEnumerable<Article>):
var source = db.GetArticles(BusinessLogic.SystemComponentID, (int)articleType, languageID);
IOrderedQueryable<Article> query;
if(articleType == BusinessLogic.ArticleType.Webinar)
{
query = source.OrderByDescending(c => c.Date_Time);
}
else
{
query = source.OrderBy(c => c.Date_Time);
}
if(somethingElse)
query = query.ThenBy(c => c.OtherProperty);
foreach(var record in query)
{
// process
}
You could also shorten it to the following:
var source = db.GetArticles(BusinessLogic.SystemComponentID, (int)articleType, languageID);
var query = articleType == BusinessLogic.ArticleType.Webinar
? source.OrderByDescending(c => c.Date_Time)
: source.OrderBy(c => c.Date_Time);
if(somethingElse)
query = query.ThenBy(c => c.OtherProperty);
foreach(var record in query)
{
// process
}
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
My scenario is to send invoice details (in PDF format) to customer's emailId on paynow button click event.
I have tried the following but getting exception after calling
actionPDF.BuildPdf(ControllerContext)
Exception is
Timeout expired. The timeout period elapsed prior to completion of the operation or the server is not responding
Code:
dbDetails _db = new dbDetails();
[HttpPost]
public JsonResult Add(Model mdl)
{
using (TransactionScope _ts = new TransactionScope())
{
//Insertion logic of invoice goes here
...
...
int i = _db.SaveChanges();
// if successfull insertion
if(i > 0)
{
var actionPDF = new Rotativa.ActionAsPdf("GetPdfReceipt", new { RegId = _receiptDetails.StudentRegistrationID.Value })
{
FileName = "Receipt.pdf"
};
// Dynamic student receipt pdf
***Getting exception here****
var byteArrayDynamic = actionPDF.BuildPdf(ControllerContext);
// Mail sending logic
......
......
_ts.Complete();
}
}
}
public ActionResult GetPdfReceipt(int RegId)
{
Common _cmn = new Common();
var _studentRegistration = _db.StudentRegistrations
.AsEnumerable()
.Where(r => r.Id == RegId)
.FirstOrDefault();
var _mdlReceiptPdf = new ReceiptPdfVM
{
CentreCode = _studentRegistration.StudentWalkInn.CenterCode.CentreCode,
CompanyAddress = _studentRegistration.StudentWalkInn.CenterCode.Address,
CompanyPhoneNo = _studentRegistration.StudentWalkInn.CenterCode.PhoneNo,
CourseFee = _studentRegistration.TotalCourseFee.Value,
CourseTitle = string.Join(",", _studentRegistration.StudentRegistrationCourses
.Select(rc => rc.MultiCourse.CourseSubTitle.Name)),
CROName = _studentRegistration.StudentWalkInn.CROCount == (int)EnumClass.CROCount.ONE ? _studentRegistration.StudentWalkInn.Employee1.Name :
_studentRegistration.StudentWalkInn.Employee1.Name + ',' + _studentRegistration.StudentWalkInn.Employee2.Name,
Duration = _studentRegistration.TotalDuration.Value,
ReceiptDate = _studentRegistration.StudentReceipts.Last(sr => sr.Status == true).DueDate.Value.ToString("dd/MM/yyyy"),
ReceiptNo = _studentRegistration.StudentReceipts.Last(sr => sr.Status == true).ReceiptNo,
RegistrationNumber = _studentRegistration.RegistrationNumber,
ServiceTax = _studentRegistration.TotalSTAmount.Value,
StudentMaskedEmailId = _cmn.MaskString(_studentRegistration.StudentWalkInn.EmailId, "email"),
StudentMaskedMobileNo = _cmn.MaskString(_studentRegistration.StudentWalkInn.MobileNo, "mobile"),
StudentName = _studentRegistration.StudentWalkInn.CandidateName,
ServiceTaxRegistrationNo = _studentRegistration.StudentWalkInn.CenterCode.STRegNo,
TotalAmount = _studentRegistration.TotalAmount.Value,
TotalAmountInWords = _cmn.NumbersToWords(_studentRegistration.TotalAmount.Value).ToUpper(),
TotalCourseFeePaid = _studentRegistration.StudentReceipts
.Where(r => r.Status == true)
.Sum(r => r.Fee.Value),
ManagerName = _cmn.GetManager(_studentRegistration.StudentWalkInn.CenterCodeId.Value)
.Name,
ReceiptDetailsList = _db.StudentReceipts
.AsEnumerable()
.Where(rc => rc.StudentRegistrationID == RegId)
.Select(rc => new ReceiptPdfVM.ReceiptDetails
{
CourseFee = rc.Fee.Value,
DatePaid = rc.DueDate.Value.ToString("dd/MM/yyyy"),
ReceiptNo = rc.ReceiptNo
}).ToList()
}
return View("Receipts", _mdlReceiptPdf);
}
protected override void Dispose(bool disposing)
{
_db.Dispose();
base.Dispose(disposing);
}
How to solve this issue? Any help will be highly appreciated.
I think the problem is that while generating the PDF document your pending database transaction times out. Move the _ts.Complete to before you generate the PDF.
dbDetails _db = new dbDetails();
[HttpPost]
public JsonResult Add(Model mdl)
{
using (TransactionScope _ts = new TransactionScope())
{
//Insertion logic of invoice goes here
...
...
int i= _db.SaveChanges();
//if successfull insertion
if(i>0)
{
_ts.Complete();
var actionPDF = new Rotativa.ActionAsPdf("GetPdfReceipt", new { RegId = _receiptDetails.StudentRegistrationID.Value })
{
FileName = "Receipt.pdf"
};
//Dynamic student receipt pdf
***Getting exception here****
var byteArrayDynamic = actionPDF.BuildPdf(ControllerContext);
//Mail sending logic
......
......
}
}
}
I know this method is working well. However I reused most of the code and it looks ugly... It should be possible to remove the if else using delegate. Any thoughts?
public IHttpActionResult TimeDatawithUserandServer(string name, string group)
{
List<Model> res = new List<Model>();
//group 1 is Server type
if (group.Equals("1"))
{
var realName = name.Replace("-", ".");
IQueryable<Overalldata> UserData = db.Overalldatas.Where(x => x.Server.Equals(realName));
var groupedUserData = UserData.GroupBy(x => new {x.EventDate, x.User}).ToList();
res.AddRange(groupedUserData.Select(item => new Model()
{
Count = item.Count(),
Date = item.Key.EventDate.ToString("yyyy-MM-dd HH:mm:ss"),
Name = item.Key.User
}));
}
//group 2 is User type
else
{
IQueryable<Overalldata> Serverdata = db.Overalldatas.Where(x => x.User.Equals(name));
var groupServerData = Serverdata.GroupBy(x => new {x.EventDate, x.Server}).ToList();
res.AddRange(groupServerData.Select(item => new Model()
{
Count = item.Count(),
Date = item.Key.EventDate.ToString("yyyy-MM-dd HH:mm:ss"),
Name = item.Key.Server
}));
}
return Ok(res);
}
You could do it this way:
public IHttpActionResult TimeDatawithUserandServer(string name, string group)
{
var getName = group == "1"
? (Func<Overalldata, string>)(od => od.Server)
: (Func<Overalldata, string>)(od => od.User);
var realName = group == "1"
? name.Replace("-", ".")
: name;
var UserData = group == "1"
? db.Overalldatas.Where(x => x.Server == realName)
: db.Overalldatas.Where(x => x.User == realName);
var groupedData =
UserData
.ToArray()
.GroupBy(x => new { x.EventDate, Name = getName(x) })
.ToArray();
var res =
groupedData
.Select(item => new Model()
{
Count = item.Count(),
Date = item.Key.EventDate.ToString("yyyy-MM-dd HH:mm:ss"),
Name = item.Key.Name
})
.ToList();
return Ok(res);
}
Without Delegates
Your code can simply be refactored to following and relieved from repetition:
public IHttpActionResult TimeDatawithUserandServer(string name, string group)
{
List<Model> res = new List<Model>();
var groupedData = group.Equals("1") ? FetchServerData(name) : FetchUserData(name);
res.AddRange(groupedData.Select(item => new Model()
{
Count = item.Count(),
Date = item.Key.EventDate.ToString("yyyy-MM-dd HH:mm:ss"),
Name = item.Key.Server
}));
return Ok(res);
}
private GroupedDataType FetchServerData(string name)
{
var realName = name.Replace("-", ".");
var UserData = db.Overalldatas.Where(x => x.Server.Equals(realName));
return UserData.GroupBy(x => new {x.EventDate, x.User}).ToList();
}
private GroupedDataType FetchUserData(string name)
{
var Serverdata = db.Overalldatas.Where(x => x.User.Equals(name));
return Serverdata.GroupBy(x => new {x.EventDate, x.Server}).ToList();
}
Please replace the GroupedDataType with the appropriate type in the two methods above and in the code below.
With Delegates
Using Reflection we find the correct field (or property) and then compare the value as in the code below:
public IHttpActionResult TimeDatawithUserandServer(string name, string group)
{
List<Model> res = new List<Model>();
var groupedData = GetGroupedList(group, name);
res.AddRange(groupedData.Select(item => new Model()
{
Count = item.Count(),
Date = item.Key.EventDate.ToString("yyyy-MM-dd HH:mm:ss"),
Name = item.Key.Server
}));
return Ok(res);
}
private static IEnumerable<GroupedDataType> GetGroupedList(string group, string name)
{
var filteredList = db.Overalldatas.Where(dbData => Filter(dbData, group, name));
return filteredList.GroupBy(x => new {x.EventDate, x.User}).ToList();
}
private static readonly Func<ODType, string, string, bool> Filter = (ode, group, name) =>
{
var objType = overallDataElement.GetType();
var field = objType.GetMember(group)[0];
return (field != null)
&& ((FieldInfo) field).GetValue(ode).ToString().Equals(name);
};
ODType is Type of the elements in of Overalldatas list and ode is overalldata list's one single element.
I agreed with a comment made by lc who suggested running this through a code review.
There are some potential anti-patterns in your code
The group parameter is an example of control coupling
The control coupling is utilized via the if statement. For me, this is a clear sign that the method should be broken into separate methods.
I would also like to call attention to the Overalldatas collection. It's name is suspicious which leads to other questions including "Is this data object trying to do too much?". But I won't really touch and this as it's less relevant to your question and my answer.
That said, my answer is that you should have two methods, one for "Server Type" and one for "User Type". I realize this pushes that "group" check elsewhere, but that can be solved later and today the intent of your methods will be MUCH clearer.
public IHttpActionResult TimeDataByUserName(string userName)
{
List<Model> res = new List<Model>();
IQueryable<Overalldata> Serverdata = db.Overalldatas.Where(x => x.User.Equals(userName));
var groupServerData = Serverdata.GroupBy(x => new {x.EventDate, x.Server}).ToList();
res.AddRange(groupServerData.Select(item => new Model()
{
Count = item.Count(),
Date = item.Key.EventDate.ToString("yyyy-MM-dd HH:mm:ss"),
Name = item.Key.Server
}));
return Ok(res);
}
public IHttpActionResult TimeDataByServerName(string serverName)
{
List<Model> res = new List<Model>();
IQueryable<Overalldata> UserData = db.Overalldatas.Where(x => x.Server.Equals(serverName));
var groupedUserData = UserData.GroupBy(x => new {x.EventDate, x.User}).ToList();
res.AddRange(groupedUserData.Select(item => new Model()
{
Count = item.Count(),
Date = item.Key.EventDate.ToString("yyyy-MM-dd HH:mm:ss"),
Name = item.Key.User
}));
}
I have the following method that I am trying to test, but my variables are null even if I try to set them up.
public void Cancel(Guid id)
{
var order = _orderRepository.Find(o => o.Id == id); ** This never gets set, even with the setup below.**
if (order == null) return; ** Test Fails here. Returns and all assertions fails.**
order.Status = OrderStatus.Cancelled;
_orderRepository.Update(order);
}
[SetUp]
public void Setup()
{
_orderRepositoryMock = new Mock<IRepository<Order>>();
_accountServiceMock = new Mock<IAccountService>();
_orderService = new OrderService(_accountServiceMock.Object, _orderRepositoryMock.Object);
order = new Order()
{
Id = Guid.NewGuid(),
Customer= new ApplicationUser()
{
Id = Guid.NewGuid().ToString(),
Email = "test#test.com",
FirstName = "Tester",
LastName = "Test",
Address = "123 45 Ave",
City = "ABCVille",
PhoneNumber = "1-888-888-8888",
PostalCode = "T3J 0A4",
Province = "Super"
},
OrderAddons = new List<OrderAddon>(),
Total = 363.99m,
Status = OrderStatus.Created
};
}
[Test]
public void CancelShouldCallRepositoryWhenValid()
{
//var order ... (test data, in setUp)
var id = Guid.NewGuid();
order.Id = id;
// Repository Setup
_orderRepositoryMock.Setup(x => x.Find(o => o.Id == id)).Returns(order);
var wasOrderStatusUpdatedCorrectly = false;
_orderRepositoryMock.Setup(x => x.Update(order))
.Callback((Order o) =>
{
wasOrderStatusUpdatedCorrectly = o.Status == OrderStatus.Cancelled;
});
// Test Service
_orderService.Cancel(id);
// Test Assertions
_orderRepositoryMock.Verify(x => x.Find(o => o.Id == It.IsAny<Guid>()), Times.Once);
_orderRepositoryMock.Verify(x => x.Update(order), Times.Once);
}
Is there anyway to test "var order" ? I tried SetupGet as well and didn't seem to work, Moq is new to me so forgive me in advance if this is something simple and easy.
I think the problem is the Expression that the repository's Find method expects. Try this instead:
_orderRepositoryMock
.Setup(x => x.Find(It.IsAny<Expression<Func<Order, bool>>>()))
.Returns(order);
I'm just guessing at the type parameter for the Expression<>, but hopefully that helps.