How can i use DefaultIfEmpty when mocking a database context? - c#

I wrote the following linq query that creates a new object by joining data together as you can see:
var translations = from t in context.Translations
join token in context.Tokens on t.Guid equals token.Guid
join t2 in context.Translations on new { t.Guid, LanguageCode = "fr" } equals new { t2.Guid, t2.LanguageCode} into j //TODO: fr needs to be replaced by the language of the translators account
from j2 in j.DefaultIfEmpty()
where t.LanguageCode == String.Empty
orderby t.Text
select new TranslationView
{
Guid = t.Guid,
LanguageCode = j2.LanguageCode,
SourceText = t.Text,
Translation = j2.Text,
IsNew = j2.Text == null,
Notes = token.Notes,
Required = token.Required,
Type = (Token.TokenType)token.Type,
Location = (Token.LocationType)token.Location
};
The problem is that I am now trying to write a unit test with Rhino.Mocks and it returns the error Object reference not set to an instance of an object.
So my question now is, is there a better way that this query could be written? A way in which it will work in both a real situation and the Unit test situation?
I tried passing a value in in the DefaultIfEmpty() bit and it made it work for the Mock, but then the main code failed.
EDIT
Unit test code:
[Test]
public void Build_Translation_List_TwoItems_Context()
{
//Arrange: Setup context
var context = setupContext();
//Act: Pass the context through
var result = TranslationHelpers.BuildTranslationList(context, 1);
//Result
result.TranslationList.Count.ShouldEqual(2);
result.PagingInfo.TotalItems.ShouldEqual(2);
}
SetupContext Method:
public static ITranslationContext setupContext()
{
var context = new Mock<ITranslationContext>();
context.SetupProperty(x => x.Tokens, new UnitTestHelpers.FakeDbSet<Token>
{
new Token
{
DateAdded = DateTime.Now,
Guid = Guid.Parse("f3099a43-e12d-4ea3-ba06-265fde807f03"),
LastUpdated = DateTime.Now,
Location = (short)0,
Type = (short)0,
LocationDescription = "Test 1",
Notes = "Testing 1",
Required = "Testing"
},
new Token
{
DateAdded = DateTime.Now,
Guid = Guid.Parse("7D6937D8-F7E1-4B92-934E-465683874B65"),
LastUpdated = DateTime.Now,
Location = (short)0,
Type = (short)0,
LocationDescription = "Test 3",
Notes = "Testing 3",
Required = "Testing"
},
});
context.SetupProperty(x => x.Translations, new UnitTestHelpers.FakeDbSet<Translation>
{
new Translation{Guid = Guid.Parse("f3099a43-e12d-4ea3-ba06-265fde807f03"), LanguageCode = String.Empty, Text = "Testing 1"},
new Translation{Guid = Guid.Parse("f3099a43-e12d-4ea3-ba06-265fde807f03"), LanguageCode = "fr", Text = ""},
new Translation{Guid = Guid.Parse("7D6937D8-F7E1-4B92-934E-465683874B65"), LanguageCode = String.Empty, Text = "Testing 3"},
new Translation{Guid = Guid.Parse("7D6937D8-F7E1-4B92-934E-465683874B67"), LanguageCode = "fr", Text = "Testing 4"}
});
return context.Object;
}
Any help would be much appreciated.

DefaultIfEmpty() will not create a "default" j2. It will just get data even if j2 is null.
It's just like an SQL LEFT JOIN
So you have to test for nullity to avoid NRE.
instead of
LanguageCode = j2.LanguageCode
try to do
LanguageCode =j2 != null ? j2.LanguageCode : string.Empty // or null

Pass mocked context to your method, and LINQ will do it's job. Then verify returned list of TranslationView objects.
Here is how your test should look like:
List<Translations> translations = // create translations
List<Tokens> tokens = // create tokens
var context = MockRepository.GenerateStub<IMyContext>();
context.Stub(c => c.Translations).Return(translations);
context.Stub(c => c.Tokens).Return(tokens);
var foo = new Foo(context);
var views = foo.GetTranslationView();
// verify returned views

Related

Quickbooks Online API - Payment 'Unapplied' when Invoice ID provided

I've managed to create a payment using the C# .NET SDK, however it keeps showing up as 'unapplied' when I check in QBO.
I am providing the Invoice ID and tried to follow their developer API documentation but I been at this so long now that maybe I am missing something?
The following code creates the payment but doesn't 'receive' the payment towards the invoice, is there something I missed or need to do in order for the two to be linked together?
Payment payment = new Payment
{
ProcessPayment = false,
CustomerRef = new ReferenceType { name = customer.DisplayName, Value = customer.Id },
CurrencyRef = new ReferenceType { type = "Currency", Value = "CAD" },
TotalAmt = amount,
TotalAmtSpecified = true
};
if (method == PaymentTypes.Cash)
{
var paymentType = paymentMethods.FirstOrDefault(o => o.Id == "1");
if (paymentType != null)
{
payment.PaymentMethodRef = new ReferenceType()
{name = paymentType.Name, Value = paymentType.Id};
}
}
if (method == PaymentTypes.DebitCard)
{
var paymentType = paymentMethods.FirstOrDefault(o => o.Id == "9");
if (paymentType != null)
{
payment.PaymentMethodRef = new ReferenceType()
{ name = paymentType.Name, Value = paymentType.Id };
}
}
if (method == PaymentTypes.CreditCard)
{
var paymentType = paymentMethods.FirstOrDefault(o => o.Id == "8");
if (paymentType != null)
{
payment.PaymentMethodRef = new ReferenceType()
{ name = paymentType.Name, Value = paymentType.Id };
}
}
List<LinkedTxn> linkedTxns = new List<LinkedTxn>
{
new LinkedTxn()
{
TxnId = invoice.Id,
TxnType = TxnTypeEnum.Invoice.ToString()
},
};
foreach (Line line in invoice.Line)
{
//line.Amount = amount;
//line.AmountSpecified = true;
line.Received = amount;
line.ReceivedSpecified = true;
line.DetailType = LineDetailTypeEnum.PaymentLineDetail;
line.DetailTypeSpecified = true;
line.LinkedTxn = linkedTxns.ToArray();
}
payment.DepositToAccountRef = new ReferenceType() { Value = "5" };
payment.Line = invoice.Line;
payment.PaymentRefNum = reference;
DataService dataService = new DataService(serviceContext);
dataService.Add<Payment>(payment);
This is not an answer. However there's too much to add to the comments. I'm hoping this will be a helpful starting point (if not I'll remove it later).
Firstly I'd suggest refactoring your code and parameterise your variables. Doing so you should be able to preform repeatable testing in isolation.
When you preform the dataService.Add<Payment>(payment), store the response object as it may offer clues on how the request was processed. Alternatively if this is suppressing error messages, you might want to try an use Postman to send HTTP requests. This may help determine what's parameters are missing/ incorrect.
Avoid creating objects that are partially assigned it makes it a lot easier to read 7 work out which properties need to be assigned.
Also if you have a look at Full update a payment section on https://developer.intuit.com/app/developer/qbo/docs/api/accounting/all-entities/payment
the json payload has an additional Line with the property TxnType set to CreditMemo. I would assume you'll need to add something like ReceivePayment or CreditCardPayment?
To refactor your code consider:
// TODO - Set variables for testing purposes.
// This can be added as params to a method.
decimal amount = 100;
string reference = "";
string invoiceId = ""; // invoice.Id
string customerDisplayName = ""; //customer.DisplayName
string customerId = ""; //customer.Id
string paymentName = "Cash"; // paymentType.Name
string paymentId = "1"; // paymentType.Id
List<Line> lines = new List<Line>(); // invoice.Line
if(lines.Count() == 0)
{
// TODO: You might want to check there are lines?
throw new ArgumentException("No invoice.");
}
Line[] invoiceLines = lines.Select(m => new Line()
{
AnyIntuitObject = m.AnyIntuitObject,
Amount = m.Amount,
AmountSpecified = m.AmountSpecified,
CustomField = m.CustomField,
Id = m.Id,
LineNum = m.LineNum,
Description = m.Description,
DetailType = LineDetailTypeEnum.PaymentLineDetail, //m.DetailType,
DetailTypeSpecified = true, //m.DetailTypeSpecified,
LinkedTxn = new List<LinkedTxn>
{
new LinkedTxn()
{
TxnId = invoiceId,
TxnType = TxnTypeEnum.Invoice.ToString() // TODO: Should this be ReceivePayment?
},
}.ToArray(), //m.LinkedTxn,
LineEx = m.LineEx,
Received = amount, //m.Received,
ReceivedSpecified = true // m.ReceivedSpecified
}).ToArray();
Payment payment = new Payment
{
ProcessPayment = false,
CustomerRef = new ReferenceType { name = customerDisplayName, Value = customerId },
CurrencyRef = new ReferenceType { type = "Currency", Value = "CAD" },
TotalAmt = amount,
TotalAmtSpecified = true,
DepositToAccountRef = new ReferenceType() { Value = "5" },
Line = invoiceLines, // Variable is for debugging purposes - it should be inline or call a method.
PaymentRefNum = reference,
PaymentMethodRef = new ReferenceType()
{
name = paymentName,
Value = paymentId,
}
};
DataService dataService = new DataService(serviceContext);
Payment results = dataService.Add<Payment>(payment);
var json = JsonConvert.SerializeObject(results);
Console.Write(json); // TODO - Use break point/ or write response somewhere for clues.

Implicit ViewModel generation from LINQ?

In my project I have a GenericImageViewModel which is used by many entities.
Example of getting the ASP User Entity:
var query = UserRepository.Get(Id).Select(a => new TRDIdenityViewModel
{
FirstName = a.UserProfile.FirstName,
LastName = a.UserProfile.LastName,
NickName = a.UserProfile.NickName,
ProfileImage = a.UserProfile.ProfileImage
});
The ProfileImage is the GenericImageViewModel and has an implicit operator as follows:
public static implicit operator TRDGenericImageViewModel(TRDImage image)
{
return new TRDGenericImageViewModel
{
Id = image.Id,
AspectRatio = image.Ratio,
Url = image.Url,
};
}
If I run the query Entity Framework throws an exception:
"Unable to cast the type 'TRDImage' to type 'TRDGenericImageViewModel'. LINQ to Entities only supports casting EDM primitive or enumeration types."
If I create the GenericImageViewModel manually for each ViewModel everything is running fine:
var query = UserRepository.Get(Id).Select(a => new TRDIdenityViewModel
{
FirstName = a.UserProfile.FirstName,
LastName = a.UserProfile.LastName,
NickName = a.UserProfile.NickName,
ProfileImage = new TRDGenericImageViewModel {
Id = a.UserProfile.ProfileImage.Id,
AspectRatio = a.UserProfile.ProfileImage.Ratio,
Url = a.UserProfile.ProfileImage.Url,
},
});
But in this case I have to copy and paste the TRDGenericImageViewModel generation in every ViewModel and thats not the way it should goes. If something changed I have to modify all related classes.
So is there a way to avoid this exception?
Using .AsEnumerable() prior lambda is not possible due to selects later in that query.
var query = UserRepository
.Get(Id)
.AsEnumerable()
.Select(a => new TRDIdenityViewModel
{
FirstName = a.UserProfile.FirstName,
LastName = a.UserProfile.LastName,
NickName = a.UserProfile.NickName,
ProfileImage = new TRDGenericImageViewModel
{
Id = a.UserProfile.ProfileImage.Id,
AspectRatio = a.UserProfile.ProfileImage.Ratio,
Url = a.UserProfile.ProfileImage.Url,
},
Statistics = new TRDUserStatisticsViewModel
{
PostCount = a.Posts.Count(),
CommentCount = a.Comments.Count(),
ImageCount = a.Images.Count(),
VideoCount = a.Videos.Count(),
VoteCount = a.PostVotes.Count(),
}
});
When calling .AsEnumerable() only the entities included by the Include statement are counted. But if the user has more than 1000 Posts and 10.000 Votes the query is a data nightmare.

How to create complex filter in c# for Magento API

I am trying to create a complex filter in c# to get data from magento API. I already written the following code
MagentoService mservice = new MagentoService();
var mlogin = mservice.login("***", "****");
var result = mservice.storeList(mlogin);
var cpf = new complexFilter[2];
cpf[0] = new complexFilter
{
key = "created_in",
value = new associativeEntity
{
key = "in",
value = "website A"
}
};
cpf[1] = new complexFilter
{
key = "bv_customer_number",
value = new associativeEntity
{
key = "in",
value = "Not Approved"
}
};
var filters = new filters();
filters.complex_filter = cpf;
var result3 = mservice.customerCustomerList(mlogin, filters);
This code works perfect the only issue is I want to add the multiple values in my key = "created_in" with value = "website a", "website b"
Anyone got any ideas on how to properly pass multiple values for a single key?
I solved this problem by aggregating values into 1 value with ',' as separator:
new complexFilter
{
key = "created_in",
value = new associativeEntity
{
key = "in",
value = "websiteA,websiteB,websiteC"
}
};

Creating multiple results from Linq query

I have a fairly unique situation, I have never needed to this before anyways. I have a Linq query that returns data from a database using EF4.1. I want to create multiple similar (same signature) anonymous (or even named if necessary) results from each query result.
Here's the code i'm using now:
var data = getMyData().Select(x =>
new
{
GoalName = x.GoalType.Name,
Start = x.StartDate,
End = x.EndDate,
x.StartValue,
x.CheckIns
}).ToList();
var r1 = data.Select(x =>
new
{
title = x.GoalName,
start = x.Start.ToString(),
end = x.End.ToString(),
className = "hidden",
type = "goal"
});
var r2 = data.Select(x =>
new
{
title = string.Format("Start: {0:0.##}", x.StartValue),
start = x.Start.ToString(),
end = x.Start.ToString(),
className = "",
type = ""
});
var r3 = data.Select(x =>
new
{
title = "End",
start = x.End.ToString(),
end = x.End.ToString(),
className = "",
type = ""
});
var r4 = data.SelectMany(x => x.CheckIns)
.Select(y =>
new
{
title = y.CheckInValue.Value.ToString(),
start = y.CheckInDateTime.ToString(),
end = y.CheckInDateTime.ToString(),
className = "",
type = ""
});
var result = r1.Union(r2).Union(r3).Union(r4);
Now maybe this is as good a way as any, but I can't help feeling that i'm missing something.
Is there a better solution?
What you have is actually OK I think.
But StevenzNPaul's suggestion not that bad, here's how you can use the let keyword to store the different projections, then select the results individually (for brevity, I did not project all the fields, but you get the point):
var query = from x in data
let result1 = new {title = x.GoalName, start = x.Start}
let result2 = new {title = string.Format("Start: {0:0.##}", x.StartValue), start = x.Start}
let result3 = new {title = "End", start = x.End}
let checkins = x.CheckIns.Select(checkin => new { title = "...", start = checkin.Start })
from result in new[] { result1, result2, result3 }.Concat(checkins)
select result;
Obviously, whether this is better is a matter of preference. Also, this will result in a different ordering, which may or may not be a problem for you.
You can create an iterator using yield which also has the advantage of being evaluated lazily (doesn't require the ToList()). I created a typed class Result to hold the query results
private IEnumerable<Result> PerformQuery()
{
var results= getMyData().Select(x => new {GoalName = x.GoalType.Name,
Start = x.StartDate, End = x.EndDate, x.StartValue, x.CheckIns});
foreach (var result in results)
{
yield return new Result() { Title = result.GoalName, Start = result.Start.ToString(), End = result.End.ToString(), ClassName = "Hidden", Type = "Goal" };
yield return new Result() { Title = String.Format("Start: {0:0.##}",result.StartValue), Start = result.Start.ToString(), End = result.Start.ToString() }
yield return new Result() { Title = "End", Start = result.End.ToString(), End = result.End.ToString() };
foreach (var checkIn in result.CheckIns)
yield return new Result() { Title = checkIn.CheckInValue.Value.ToString(), Start = checkIn.CheckInDateTime.ToString(), End = checkIn.CheckInDateTime.ToString() };
}
}
try using let keyword it will work for you.

Anonymous Type in LINQ

I'm trying to get anonymous object from query:
var myList = from td in MyObjectList
select new
{
a = td.a,
b = td.b,
c = td.c,
name = (from r in contex.NewList
where r.aa == td.a && r.bb == td.b
select r.Name).ToList()
};
I would like name to have r.Name value cause I expect that name list contains only one element. If it contains 0 elements I would like name to have value NONE if more then 1 element then exception should be thrown or something.
Is it even possible to achieve something like that? Thanks for help.
Instead of .ToList() use
.SingleOrDefault() ?? (td.a == 0 ? "XNone" : "None")
Edit: Changed anwer based on comment.
Also I would recomend not to put such logic into Linq-to-SQL. Sometimes this can result in big chunk of highly-unoptimized SQL code and, unless you dont mind some performance isues, can result in much slower SQL execution.
You can achieve that using SingleOrDefault and a temporary variable within the expression. Something like this:
var myList =
from td in MyObjectList
let nameValue = contex.NewList
.Where(r => r.aa== td.a && r.bb == td.b)
.Select(r => r.Name)
.SingleOrDefault()
select new
{
a = td.a,
b = td.b,
c = td.c,
name = nameValue ?? "NONE"
};
Update: instead of presenting almost the same solution as #Euphorics answer, I've restructured the code a bit. I often find nested LINQ expressions making things less readable. Converting comprehension syntax into call chains could improve that.
Update 2: with some added requirements, the following select should do the trick:
select new
{
a = td.a,
b = td.b,
c = td.c,
name = nameValue ?? (td.a == 0 ? "XNone" : "None")
};
Theoretically you CAN'T.
Unless you have a type from where you could inspect lambda properties.
The trick is to convert your anonymous object to json and deserialize it into a known type that you must have in advance.
Caution working with EF core because your linq query will be executed CLIENT side!!.
That means that all the records will be retrieved from your dbset and evaluate on client.
Do NOT use code like this on EF dbset iquerables.
Any away I will copy some extensions methods code to do it so.
Having a defined type like...
public class Department
{
public int Id { get; set; }
public string Name { get; set; }
}
Extension convert any object to json string...
public static class ObjectExtensions
{
public static string ToJson(this object source)
{
return JsonConvert.SerializeObject(source, Formatting.None);
}
}
Extension convert any json string to typed object...
public static class StringExtensions
{
public static T FromJson<T>(this string source) where T : class
{
return JsonConvert.DeserializeObject<T>(source);
}
}
Some xUnit Test
[Fact]
public void AddSelectTest()
{
var data = new[]
{
new {Id = 01, Name = "Engineering", GroupName = "Engineering and Development"},
new {Id = 02, Name = "Tool Design", GroupName = "Tool Design and Research"},
new {Id = 03, Name = "Sales", GroupName = "Sales and Marketing"},
new {Id = 04, Name = "Marketing", GroupName = "Marketing and Sales"},
new {Id = 05, Name = "Purchasing", GroupName = "Inventory Management"},
new {Id = 06, Name = "Research and Development", GroupName = "Development and Research"},
new {Id = 07, Name = "Production", GroupName = "Manufacturing and Production"},
new {Id = 08, Name = "Production Control", GroupName = "Control and Production"},
new {Id = 09, Name = "Human Resources", GroupName = "Human Resources and Administration"},
new {Id = 10, Name = "Finance", GroupName = "Finance and Executive General"},
new {Id = 11, Name = "Information Services", GroupName = "Information Services and Administration"},
new {Id = 12, Name = "Document Control", GroupName = "Document Control and Quality Assurance"},
new {Id = 13, Name = "Quality Assurance", GroupName = "Administration and Quality Assurance"},
new {Id = 14, Name = "Facilities and Maintenance", GroupName = "Maintenance and Facilities"},
new {Id = 15, Name = "Shipping and Receiving", GroupName = "Receiving and Shipping"},
new {Id = 16, Name = "Executive", GroupName = "Executive General and Administration"}
};
var queryable = data.AsQueryable();
var first = queryable.Select(d => new { Id = d.Id, Name = d.Name }).FirstOrDefault(d => d.ToJson().FromJson<Department>().Id == 1);
Assert.True(first != null, "Expected a department value but 'null' was found.");
}
Once again... let me say that if you are querying anonymous in memory object it could be ok, but be very cautious if your iquerable cames from EF core, since client side evaluation will happen.
Please enable throw exception warning when client side evaluation happens on your EF core code thought your DbContextOptionsBuilder, to prevent EF Core of executing client side evaluation code. You could do it follows..
builder.UseSqlServer(connection, sql =>
{
sql.EnableRetryOnFailure();
sql.MigrationsAssembly(assembly);
})
.UseQueryTrackingBehavior(track ? QueryTrackingBehavior.TrackAll : QueryTrackingBehavior.NoTracking)
.ConfigureWarnings(w => w.Throw(RelationalEventId.QueryClientEvaluationWarning));

Categories