Imagine that I want to validate if the object is null and I have 3 methods that will help me, such:
GetCompanyBySystemId();
GetCompanyById();
GetCompanyByName();
So, first I'll try to get the Company by system Id, if it's null I'll try to get it by Id, and, last but not least, I'll try to get it by Name.
My question is, what is the best practice to validate the company without doing this:
var company = GetCompanyBySystemId(string);
if (company == null)
{
company = GetCompanyById(string);
}
if (company == null)
{
company = GetCompanyByName(string);
}
...
You may use the null-coalescing-operator:
var company = GetCompanyBySystemId(#string) ??
GetCompanyById(#string) ??
GetCompanyByName(#string) ??
throw new InvalidOperatonException($"No company found for { #string }");
Also notice I added # as string is a reserved keyword and therefor not allowed as variable-name.
I always like the Try* paradigm (like int.TryParse) when you are stating that something may or may not be available, and you won't know at runtime:
public static void Main()
{
string id = "test";
object company = null;
if(!TryGetCompanyBySystemId(id, out company)
&& !TryGetCompanyById(id, out company)
&& !TryGetCompanyByName(id, out company))
{
//we've failed entirely
}
else
{
//we got a company
}
}
public static bool TryGetCompanyBySystemId(string id, out object company)
{
company = null;
//locate company and return true if found
return false;
}
public static bool TryGetCompanyById(string id, out object company)
{
company = null;
//locate company and return true if found
return false;
}
public static bool TryGetCompanyByName(string id, out object company)
{
company = null;
//locate company and return true if found
return false;
}
If you often have to seach for Company in such a way, I suggest extrcating a method in which we can hide all the details:
public static Company GetCompany(string idOrName) {
if (value is null)
throw new ArgumentNullException(nameof(idOrName));
var result = GetCompanyBySystemId(idOrName) ??
GetCompanyById(idOrName) ??
GetCompanyByName(idOrName);
return result != null
? result
: throw new ArgumentException($"Company with id or name {idOrName} is not found.",
nameof(idOrName));
}
Then whenever you want a company just get it:
// Just find me the company by string and don't make we think
// names, ids etc. things
Company company = GetCompany(#string);
In case not finding company is normal situation, you can implement TryGetCompany:
public static bool TryGetCompany(string idOrName, Company company) {
// Still exceptional situaltion
if (value == null)
throw new ArgumentNullException(nameof(idOrName));
company = GetCompanyBySystemId(idOrName) ??
GetCompanyById(idOrName) ??
GetCompanyByName(idOrName);
return company != null;
}
usage
if (TryGetCompany(#string, out var company)) {
// company with id or name string has been found and put into company
}
else {
// company with id or name string has not been found
}
Related
I have some test code to check if 2 sides are equal.
public void GetCompanies_WithCorrectCompanyRequest_ReturnCompanyDtos()
{
// Arrange
var companyRequset = new CompanyRequest();
// Act
var companyDtos = _datlinqServiceMock.GetCompanies(companyRequset);
// Assert
Assert.IsTrue(companyDtos != null && companyDtos.Any());
Assert.AreEqual(DatlinqServiceMock.CompanyName, companyDtos.FirstOrDefault().Name);
}
That calls this.
public class DatlinqServiceMock: DatlinqService
{
public static string CompanyName = "Company_Test";
public override T GetApi<Q,T>(string apiMethod, Q request)
{
var companyList = new List<Company>()
{
new Company(){ Name = CompanyName}
};
return (T)Convert.ChangeType(companyList, typeof(T));
}
}
GetCompanies:
public List<Company> GetCompanies(CompanyRequest request)
{
if (request == null)
{
return new List<Company>();
}
var searchCompany = new SearchCompanyRequest();
searchCompany.Query = request.Name;
searchCompany.DatlinqKey = ConfigurationManager.AppSettings["Datlinq_Key"];
var searchCompanyResponse = GetApi<SearchCompanyRequest,SearchCompanyResponse>(DatlinqApiMethod.SearchCompany, searchCompany);
var companies = searchCompanyResponse.Result
.Select(c => new Company { Id = c.Id, Name = c.Name })
.ToList();
return companies;
}
GetApi:
public virtual T GetApi<Q,T>(string apiMethod, Q request)
{
var result = default(T);
try
{
var url = String.Format("{0}{1}", _apiUrl, apiMethod);
if (request != null)
{
url = QueryHelpers.AddQueryString(url, request.ToDictionary());
}
var apiResponse = _httpClient.GetAsync(url).Result;
if (apiResponse.IsSuccessStatusCode)
{
string apiResponseString = apiResponse.Content.ReadAsStringAsync().Result;
if (!string.IsNullOrEmpty(apiResponseString))
{
result = JsonConvert.DeserializeObject<T>(apiResponseString);
}
}
}
catch (Exception)
{
// do something
}
return result;
}
And I get an error when I execute the first test
Message:
Test method Lavazza.ContractTool.Datlinq.Tests.Services.DatlinqServiceTests.GetCompanies_WithCorrectCompanyRequest_ReturnCompanyDtos threw exception:
System.InvalidCastException: Object must implement IConvertible.
Stack Trace:
Convert.ChangeType(Object value, Type conversionType, IFormatProvider provider)
Convert.ChangeType(Object value, Type conversionType)
DatlinqServiceMock.GetApi[Q,T](String apiMethod, Q request) line 79
DatlinqService.GetCompanies(CompanyRequest request) line 23
DatlinqServiceTests.GetCompanies_WithCorrectCompanyRequest_ReturnCompanyDtos() line 32
I hope this is enough code to know what the problem/solution is if not let me know what you need.
To answer some question asked below.
Dai: I am trying to find what this is and why I need it because it came up in the error but the microsoft docs don't make it clear for me.
Jeroen: It is kind of a legacy project so I can't/won't add extra dependencies.
The "issue" here is that your GetApi method is designed to return a sort of generic response - in this specific case SearchCompanyResponse - but you're trying to override it to return a List<Company> which is wrong because that is what your GetCompanies method does (ie turn a SearchCompanyResponse to a List<Company>).
Suffice it to say this has nothing to do with implementing IConvertable.
What you would be better off doing is serializing a SearchCommpanyResponse that you wish to use for testing to a file, and using that file-based response for your testing.
That way your overridden "Mock" would be
public override T GetApi<Q,T>(string apiMethod, Q request)
{
return JsonConvert.DeserializeObject<T>(File.ReadAllText("mytestfile.json"));
}
I wonder how to deal with dependent properties in C#. I have following simplified classes (I'm using DevExpress XAF):
public class Company
{
public ContactMethods? PreferredContactMethod { get; set; }
// Missing: Collection of Employees...
}
public class Employee
{
private Company company;
public Company Company
{
get
{
return this.company;
}
set
{
company = value;
if (company != null && PreferredContactMethod == null)
{
PreferredContactMethod = company.PreferredContactMethod;
}
}
}
public ContactMethods? PreferredContactMethod { get; set; }
}
When assigning a Company to an Employee, I set the Employee.PreferredContactMethod to the PreferredContactMethod of the Company (just for convenience, can be changed later).
Update:
I want to use the Company.PreferredContactMethod just as a default value when initializing new Employees. Every Employee stores its own ContactMethod independently from the Company. Later changes to the Company.PreferredContactMethod shouldn't update the Employee.PreferredContactMethod. It is perfectly legal for the Employee.PreferredContactMethod to be null (e.g. if explicitly set be user).
Quite simple code and of course this is just working fine. But I think it violates Microsoft's Property Design Guidelines:
Do allow properties to be set in any order even if this results in a
temporary invalid object state.
Company = A, PreferredContactMethod = null gives another result than PreferredContactMethod = null, Company = A.
I think I cannot rely on a 'correct' order of the property setters (e.g. if using Automapper/Reflection), How do you deal with such a situation? I think it's not uncommon.
Thank you!
You want to ensure the invariant that the Employee's has the same PreferredContactMethod as this Company himself, unless he has a specified PreferredContancMethod himself. One way, extending your solution, is to implement this is to update this value like this:
public class Employee
{
private Company company;
public Company Company
{
get { return this.company; }
set
{
this.company = value;
UpdateCompanyPreferredContactMethod();
}
}
private ContactMethods? preferredContactMethod;
public ContactMethods? PreferredContactMethod
{
get { return this.preferredContactMethod; }
set
{
this.preferredContactMethod = value;
UpdateCompanyPreferredContactMethod();
}
}
private void UpdateCompanyPreferredContactMethod()
{
if (PreferredContactMethod == null)
{
PreferredContactMethod = company != null ?company.PreferredContactMethod : null;
}
}
}
However, this solution is fragile, as you have to update this value every time you change either. Instead, I would actually do something like this:
public class Employee
{
public Company Company { get; set; }
private ContactMethods? preferredContactMethod;
public ContactMethods? PreferredContactMethod
{
get
{
if (this.preferredContactMethod != null)
{
return this.preferredContactMethod;
}
else if (this.Company != null)
{
return this.Company.PreferredContactMethod;
}
else
{
return null;
}
}
set { this.preferredContactMethod = value; }
}
}
You say you want to use the PreferredContactMethod as default value when initializing a new Employee. If this is the case, you should pass the Company to the constructor of the Employee, and set it then:
public Employee(Company company) {
// Null checks as appropriate
this.company = company;
this.PreferredContactMethod = company.PreferredContactMethod;
}
If you also want to change the PreferredContactMethod the way you do, and your only concern is violating the design guidelines, then maybe use a method Employee.SetCompany to indicate that the operation has side effects in addition to just changing the Company property.
I think you need to assign it just before when saving the Employee object therefore any order gives the same result.
try this;
if (IsNewRecord && Company != null && PreferredContactMethod == null)
PreferredContactMethod = Company.PreferredContactMethod;
I have an Azure Mobile Service with multiple controllers. One of my controllers (TestSetController) has some extra methods to check on insert.
Problem I need to solve: The TestSet table has two different types of TestSets, one for a local team and another for a field team. The table contains data for both and the records are differentiated by a "TeamType" field which says if the local team inserted the TestSet or the field team did. On any insert I want to check if a similar TestSet exists that was inserted by the other team. I want to compare the TestSets (if found) then do some other inserts/updates on the same table if the TestSets are different.
However, I keep getting this error:
Exception=System.InvalidOperationException: Multiple actions were found that match the request:
PostTestSetDTO on type sbp_ctService.Controllers.TestSetController
CheckForDiscrepancy on type sbp_ctService.Controllers.TestSetController
CompareTestPointAttempts on type sbp_ctService.Controllers.TestSetController
at System.Web.Http.Controllers.ApiControllerActionSelector.ActionSelectorCacheItem.SelectAction(HttpControllerContext controllerContext)
at System.Web.Http.Controllers.ApiControllerActionSelector.SelectAction(HttpControllerContext controllerContext)
at System.Web.Http.ApiController.ExecuteAsync(HttpControllerContext controllerContext, CancellationToken cancellationToken)
at System.Web.Http.Dispatcher.HttpControllerDispatcher.<SendAsync>d__1.MoveNext(), Id=f07761ae-1be7-4f00-90b0-685dd0c108f3, Category='App.Request'
Here's my controller:
public class TestSetController : TableController<TestSetDTO>
{
private Context context;
protected override void Initialize(HttpControllerContext controllerContext)
{
base.Initialize(controllerContext);
context = new Context();
DomainManager = new SimpleMappedEntityDomainManager<TestSetDTO, TestSet>(context, Request, Services, testset => testset.Id);
}
// GET tables/TestSet
[QueryableExpand("TestPointAttempts")]
public IQueryable<TestSetDTO> GetAllTestSetDTO()
{
return Query();
}
// GET tables/TestSet/48D68C86-6EA6-4C25-AA33-223FC9A27959
public SingleResult<TestSetDTO> GetTestSetDTO(string id)
{
return Lookup(id);
}
// PATCH tables/TestSet/48D68C86-6EA6-4C25-AA33-223FC9A27959
public Task<TestSetDTO> PatchTestSetDTO(string id, Delta<TestSetDTO> patch)
{
return UpdateAsync(id, patch);
}
// POST tables/TestSet/48D68C86-6EA6-4C25-AA33-223FC9A27959
public async Task<IHttpActionResult> PostTestSetDTO(TestSetDTO item)
{
TestSet testSet = AutoMapper.Mapper.Map<TestSetDTO, TestSet>(item);
this.CheckForDiscrepancy(testSet);
TestSetDTO current = await InsertAsync(item);
return CreatedAtRoute("Tables", new { id = current.Id }, current);
}
// DELETE tables/TestSet/48D68C86-6EA6-4C25-AA33-223FC9A27959
public Task DeleteTestSetDTO(string id)
{
return DeleteAsync(id);
}
public TestSet CheckForDiscrepancy(TestSet sourceTestSet)
{
// Set the team type to search for opposite to the one being posted.
string searchTeamType = null;
if (sourceTestSet.TestTeamType == "D")
{
searchTeamType = "F";
}
if (sourceTestSet.TestTeamType == "F")
{
searchTeamType = "D";
}
var testSetTable = context.TestSets;
TestSet foundTestSet = (from ts in testSetTable
where ts.TileId == sourceTestSet.TileId && ts.ScenarioId == sourceTestSet.ScenarioId && ts.TestTeamType.StartsWith(searchTeamType)
select ts).SingleOrDefault();
// If no other test set was found from the opposing team then the test set is missing.
// Else a testSet was found so continue with checks.
if (foundTestSet == null)
{
sourceTestSet.DiscrepancyTypeId = DiscrepancyType.Missing.ToString();
}
else
{
var testPointAttemptTable = context.TestPointAttempts;
// Get all of the associated TestPointAttempts for each testSet.
sourceTestSet.TestPointAttempts = (from tpa in testPointAttemptTable
where tpa.TestSetId == sourceTestSet.Id
orderby tpa.TestAttemptNumber
select tpa).ToList<TestPointAttempt>();
foundTestSet.TestPointAttempts = (from tpa in testPointAttemptTable
where tpa.TestSetId == foundTestSet.Id
orderby tpa.TestAttemptNumber
select tpa).ToList<TestPointAttempt>();
bool matchingTestSets = CompareTestPointAttempts(sourceTestSet.TestPointAttempts, foundTestSet.TestPointAttempts);
if (!matchingTestSets)
{
sourceTestSet.DiscrepancyTypeId = DiscrepancyType.Discrepancy.ToString();
sourceTestSet.DiscrepancyTestSetId = foundTestSet.Id;
}
}
return sourceTestSet;
}
public bool CompareTestPointAttempts(IEnumerable<TestPointAttempt> sourceTPAs, IEnumerable<TestPointAttempt> foundTPAs)
{
bool pass = false;
// First check if the total number of testPointAttempts are the same
if (sourceTPAs.Count() == foundTPAs.Count())
{
foreach (TestPointAttempt sTpa in sourceTPAs)
{
bool foundMatch = false;
foreach (TestPointAttempt fTpa in foundTPAs)
{
if (sTpa.TestAttemptNumber == fTpa.TestAttemptNumber)
{
if (sTpa.TalkIn == fTpa.TalkIn && sTpa.TalkOut == fTpa.TalkOut)
{
foundMatch = true;
}
}
}
if (!foundMatch)
{
return pass;
}
}
// The foreach loop finished successfully meaning all matches were found
pass = true;
}
return pass;
}
/// <summary>
/// The type of discrepancy a TestSet can have.
/// </summary>
public enum DiscrepancyType
{
Discrepancy,
Missing,
None
}
}
}
I'm using Data Transfer Objects (DTOs) to map between the entity models. Any help would be appreciated. I've looked at some different answers on StackOverflow for ASP.NET but they all talk about updating the config.Routes. This is for an Azure Mobile Service which might have different requirements than a simple ASP.NET website though.
It was as simple as making the two methods private and leaving the actual POST method as public. ASP.NET will only make routes automatically for public methods.
I have a class named "admin" in my asp.net C# project.
It is:
public class Admin
{
private int ID;
private string FirstName, LastName, IsMainAdmin, Email, Username,
Password,BirthDate, EntryDate;
public int id
{
get { return ID; }
set { ID = value; }
}
public string firstname
{
get { return FirstName; }
set { FirstName = value; }
}
public string lastname
{
get { return LastName; }
set { LastName = value; }
}
.
.
.
After login a session is created like this:
Admin admin = isAdmin(username, password);
if (admin != null)
{
**Session.Add("maskanAdmin", admin);**
Response.Redirect("panel.aspx");
}
In other page i need to get admin's ID from session in code behind section after page request via jquery ajax.
Please notice that my code behind Method is [WebMethod] that is not supporting Session Object.
Can i get it? How?
var adminObj = (Admin)Session["maskanAdmin"];
if(adminObj != null)
{
var id = adminObj.id;
var fname = adminObj.firstname;
}
Read more about Read Values from Session State
Update
I am not sure why the question is updated after one hour saying you are using the code in web methods.
However, have a look at Using ASP.NET Session State in a Web Service
You just need to cast it back to an Admin type object when you retrieve it from the Session:
Admin admin = (Admin)Session["maskanAdmin"];
Then you can use the properties of the object as normal:
if(admin.ID == someOtherID)
{
// do stuff
}
Admin variableName = (Admin)Session["maskanAdmin"];
var adminObj = Session["maskanAdmin"];
if(adminObj != null)
{
var admin = (Admin)adminObj;
}
How can I compare the value of this enum
public enum AccountType
{
Retailer = 1,
Customer = 2,
Manager = 3,
Employee = 4
}
I am trying to compare the value of this enum in an MVC4 controller like so:
if (userProfile.AccountType.ToString() == "Retailer")
{
return RedirectToAction("Create", "Retailer");
}
return RedirectToAction("Index", "Home");
I also tried this
if (userProfile.AccountType.Equals(1))
{
return RedirectToAction("Create", "Retailer");
}
return RedirectToAction("Index", "Home");
In each case I get an Object reference not set to an instance of an object.
use this
if (userProfile.AccountType == AccountType.Retailer)
{
...
}
If you want to get int from your AccountType enum and compare it (don't know why) do this:
if((int)userProfile.AccountType == 1)
{
...
}
Objet reference not set to an instance of an object exception is because your userProfile is null and you are getting property of null. Check in debug why it's not set.
EDIT (thanks to #Rik and #KonradMorawski) :
Maybe you can do some check before:
if(userProfile!=null)
{
}
or
if(userProfile==null)
{
throw new ArgumentNullException(nameof(userProfile)); // or any other exception
}
You can use Enum.Parse like, if it is string
AccountType account = (AccountType)Enum.Parse(typeof(AccountType), "Retailer")
Comparision:
if (userProfile.AccountType == AccountType.Retailer)
{
//your code
}
In case to prevent the NullPointerException you could add the following condition before comparing the AccountType:
if(userProfile != null)
{
if (userProfile.AccountType == AccountType.Retailer)
{
//your code
}
}
or shorter version:
if (userProfile !=null && userProfile.AccountType == AccountType.Retailer)
{
//your code
}
You can use extension methods to do the same thing with less code.
public enum AccountType
{
Retailer = 1,
Customer = 2,
Manager = 3,
Employee = 4
}
static class AccountTypeMethods
{
public static bool IsRetailer(this AccountType ac)
{
return ac == AccountType.Retailer;
}
}
And to use:
if (userProfile.AccountType.isRetailer())
{
//your code
}
I would recommend to rename the AccountType to Account. It's not a name convention.
You should convert the string to an enumeration value before comparing.
Enum.TryParse("Retailer", out AccountType accountType);
Then
if (userProfile?.AccountType == accountType)
{
//your code
}