I have created a generic method, and I want to pass an object by a reference to this method to populate few properties. It compiles, and runs without problems, but the object is not being populated.
my generic method
public static void SplitAddress<T>(ref T ob, string addressToSplit) where T : Address
{
//ptr : Postcode, Town, Region
var ptr = addressToSplit.Split(new char[] { '-' }, 2, StringSplitOptions.RemoveEmptyEntries).ToList();
var pt = ptr[0].Split(new char[] { ' ' }, 2, StringSplitOptions.RemoveEmptyEntries).ToList();
if (ptr.Count == 2)
{
ob.Region = ptr[1];
}
for (int x = 0; x < pt.Count; x++)
{
switch (x)
{
case 0:
{
ob.PostCode = pt[x];
break;
}
case 1:
{
ob.Town = pt[x];
break;
}
}
}
}
Object i want to pass
class Merchant : Address
{
public int MeId { get; set; }
public int HoId { get; set; }
public string Name { get; set; }
public string Email { get; set; }
public string Phone { get; set; }
public string Address { get; set; }
public string PostCode { get; set; }
public string Town { get; set; }
public string Region { get; set; }
public string VatNr { get; set; }
public string TRSshopId { get; set; }
}
Address class
abstract class Address
{
public string PostCode;
public string Town { get; set; }
public string Region { get; set; }
public string Email { get; set; }
public string Phone { get; set; }
public string Adrress { get; set; }
}
method invocation
Methods.SplitAddress<Merchant>(ref me, row.Cells[i].Text);
I could create two overloaded methods, for two different object types, but they will repeat the same code, which I want to avoid.
It look very odd, but for example "Postcode" is being populated, but when I hover the mouse on "ob", the property is still empty.
EDIT
As #Lee astutely noticed, you are hiding the properties of Address in Member. Since your generic method is constrained to members of type Address, your code is actually changing the properties of the Address class that are hidden, not the properties of the Merchant class, so you are not seeing those changes if you have a variable of type Merchant. You would see the values if you cast the Member to an Address. Just remove those properties from Merchant and you should be fine.
p.s. Member inheriting form Address seems wrong - a member has an address, it is not an address. a better design would be:
class Merchant
{
public int MeId { get; set; }
public int HoId { get; set; }
public string Name { get; set; }
public Address Address { get; set; }
public string VatNr { get; set; }
public string TRSshopId { get; set; }
}
Original Answer
I want to pass an object by a reference to this method to populate few properties
Since Address is a class, you don't need to use ref. A parameter of a reference types will contain a reference to the same object as the variable that's passed in, so you can change the values of the properties of that object and the calling method will see the changes. The main thing ref lets you do is change the reference to a different object, which you aren't doing, so using ref won't change what you are trying to do.
I suggest you run it in the debugger to make sure your if blocks are getting executed the way you expect them to. (e.g. is ptr.Count == 2 true? could it be greater then 2?)
Also your entire for block can be reduced to:
if(pt.Count > 0) ob.PostCode = pt[0];
if(pt.Count > 1) ob.Town = pt[1];
Related
What I want
I want to send a limited/reduced class/object to frontend (as JSON). I use .NET Core 5.
What I have
I have a model class like this:
namespace Tasks.Models
{
public class Resources
{
public Guid Id { get; set; }
public string Type { get; set; }
public string Url { get; set; }
public string Name { get; set; }
public string Description { get; set; }
public DateTime? Createdon { get; set; }
public Guid Userid { get; set; }
public Guid Taskid { get; set; }
public int Clicked { get; set; }
public byte Active { get; set; }
+++ many more properties
}
}
Now depending on the which controller that calls this model I want to have different "kind" of models. So if the resource is file I maybe want the properties Id,Type,Name. But if the resource is URL I want Id, Url, Name.
I tried setting up a method that "initialized the fields I wanted, but that also returned all properties
public static Responses FileResponse()
{
var response = new Responses()
{
Id = new Guid(),
Name = "",
Type = "File",
};
return response;
}
Now, when I call the Resources class or this method I get all properties, and returning it to the view presents all properties, but mostly as null, because I only set the three fields in the method.
What is the recommended way of solving this?
If you want to remove the field if it's null instead of showing in json with null value.
public class Resources
{
public Guid Id { get; set; }
public string Type { get; set; }
// if null, dont show it in JSON output
[JsonIgnoreAttribute(Condition = JsonIgnoreCondition.WhenWritingNull)]
public string Url { get; set; }
// if null, dont show it in JSON output
[JsonIgnoreAttribute(Condition = JsonIgnoreCondition.WhenWritingNull)]
public string Name { get; set; }
public string Description { get; set; }
public DateTime? Createdon { get; set; }
public Guid Userid { get; set; }
public Guid Taskid { get; set; }
public int Clicked { get; set; }
public byte Active { get; set; }
}
PS: Fiddle https://dotnetfiddle.net/hiMAci
It is just limiting the Resource class I am not able to do
Yep, side effect of C# being strongly typed, with object X definitely having properties Y and Z. You need differently shaped objects - either full on classes or records - that name the reduced set of properties because the serializer is going to look a tthe object and ser every property it can find.
You could make a new class for every variation - quick and easy with records, and easy to pass around inside your C#:
public record FileThing(string Id, string Type, string Name);
//make a new one and return it
new FileThing(someResources.Id, someResources.Type, someResources.Name);
Or can consider using an anonymous type if you're literally looking to put a few properties into some json, down a socket to a consuming front end (I can't quite decide what you mean by "view" - it doesn't seem to be an MVC View) that only cares about a few props out of many
So if the resource is file I maybe want the properties Id,Type,Name. But if the resource is URL I want Id, Url, Name.
public ActionResult SomeControllerMethod(){
if(isFile)
return Ok(new { someResources.Id, someResources.Type, someResources.Name });
else if(isUrl)
return Ok(new { someResources.Id, someResources.Url, someResources.Name });
}
Anonymous types are a bit harder to work with because the compiler writes the class for you, so it's tricky to do things like declare return types from methods if the method is returning an AT.. But if you're using it as some fill-in all within one method, such as a "make this and serialize it", they work well..
I think your approach is not the right one here. I tend to follow more general OO guidelines in this situation (note, some consider these a bit dated, and other solutions exist. But they are still commonly used)
You write against an interface. So let's see what you want... A guid, type and name. All other deatils aren't important.
public interface IResourceDetails
{
public Guid Id { get; }
public string Name { get; }
public string Type { get; }
}
And you can have multiple of these interfaces.
You could then implement the interfaces per type. But I would probably combine them in a base class
public abstract class ResourceBase : IResourceDetails
{
public Guid Id { get; } = new ();
public string Name { get; init; }
public string Type { get; }
public ResourceBase(string type)
{
Type = type;
}
}
Each resource type would have it's own implementation
public class FileResource : ResourceBase
{
public FileResource() : base("File") { }
// File-specific properties.
public string Description { get; init; }
public DateTime? Createdon { get; init; }
}
The response method then could be made generic and look like this
public static IActionResult Response(IResourceDetails resource)
{
return Ok(new
{
resource.Id,
resource.Name,
resource.Type,
});
}
I hope this isn't a foolishly simple question. Im very simply trying to figure out how to manipulate a relatively simple table in SQLite through C#.
Im looking to take a parameter and search a List of Arrays for one such array where the parameter matches, and return a related variable within that same array.
For example where an array in the list might be.
Name IATA
Brisbane BNE
The sqlbind:
public static List<Airport> LoadAirports()
{
using (IDbConnection cnn = new SQLiteConnection(LoadConnectionString()))
{
var output = cnn.Query<Airport>("select * from Airport", new DynamicParameters());
return output.ToList();
}
}
The Class:
class Airport
{
int Id { get; set; }
string Name { get; set; }
string LocationName { get; set; }
string IATA { get; set; }
string PortType { get; set; }
string PortOwner { get; set; }
string MotherPort { get; set; }
bool Active { get; set; }
bool IsApplyMeetAndGreet { get; set; }
decimal MeetAndGreet { get; set; }
}
The main Program:
List<Airport> Airports = new List<Airport>();
public FreightCalculator()
{
LoadAirportsList();
string OriginName = OriginInput.Value;
var OriginAirport = Airports.Where(s => s.Name == OriginName);
}
private void LoadAirportsList()
{
Airports = SqliteDataAccess.LoadAirports();
}
Ive tried various combinations of Where, Equals, For each indexing etc. Always getting an error of some kind.
The Error with the above Airports.Where is that the s.Name is inaccessible due to its protection level.
If I do:
var OriginAirport = Airports.Where(Name => Name == OriginName);
I get an error where the operand == cannot be used with Airport and String (Though Name is a string in Airport.)
Im either missing something simple or making this more complicated than it needs to be. Once I find the matching Airport, I need to return the IATA code.
Which I envisage looking like this:
var OriginIATA = OriginAirport.IATA;
Im tired and feeling dumb. Please help :(
Since you declared all members of the Airport class as properties I assume you wanted to expose them publicly.
The error you get is because they are private members and can't be accessed outside the class.
Change "Airport" class to:
class Airport
{
public int Id { get; set; }
public string Name { get; set; }
public string LocationName { get; set; }
public string IATA { get; set; }
public string PortType { get; set; }
public string PortOwner { get; set; }
public string MotherPort { get; set; }
public bool Active { get; set; }
public bool IsApplyMeetAndGreet { get; set; }
public decimal MeetAndGreet { get; set; }
}
I probably don't know the correct terminology to look this up on google so I'm asking here.
Say I want to have a front end control to run different SQL queries in Winforms that allow entering the parameters which will be generated at runtime.
public class AnalysisQuery
{
public string QueryName { get; set; }
public string QueryDescription { get; set; }
public string QuerySQL { get; set; }
public T QueryParameters { get; set; } // <-- Not sure how to do this
}
and say I have 2 entirely different classes that hold the parameters for a particular query
public class EmployeeQueryParameters
{
public string FirstName { get; set; }
public string LastName { get; set; }
public string Occupation { get; set; }
public DateTime StartingDate { get; set; }
public bool IsMarried { get; set; }
}
public class CarQueryParameters
{
public string CarName { get; set; }
public string CarModel { get; set; }
public string CarMaker { get; set; }
public bool IsDiesel { get; set; }
}
How do I hold these different classes in the property QueryParameters?
What is the best way to do this?
Ultimately I need to use either EmployeeQueryParameters or CarQueryParameters for a datasource, eg
someControl.DataSource = new EmployeeQueryParameters()
{
FirstName = "",
LastName = "",
Occupation = "",
StartingDate = new DateTime(2018, 10, 18),
IsMarried = true
};
What I have tried so far....
1)I've looked into interfaces but this looks like it will only work if all properties are the same in each EmployeeQueryParameters and CarQueryParameters class.
2) This link shows an example to hold different types as list of parameters. It kind of works but ultimately the type must still be known at the end to retrieve the correct type, eg
public class AnalysisQuery
{
public string QueryName { get; set; }
public string QueryDescription { get; set; }
public string QuerySQL { get; set; }
public Parameter QueryParameters { get; set; }
public AnalysisQuery()
{
QueryName = "QueryName1";
QueryDescription = "QueryDescription1";
QuerySQL = "QuerySQL1";
QueryParameters = Parameter.Create<EmployeeQueryParameters>(
new EmployeeQueryParameters() { FirstName = "first name" });
}
}
still requires the type to be known to get the value so kind of defeats the object of using a generic parameter?
var analysisQuery = new AnalysisQuery();
EmployeeQueryParameters parameters =
analysisQuery.QueryParameters.Get<EmployeeQueryParameters>();
The answer depends a lot on your UI. Do you have a form that uses AnalysisQuery as a model in which you dynamically add new key value pairs to define parameters for the query? Then I recommend using a Dictionary for QueryParameters. Do you use only a limited number of predefined types of queries, then I recommend using AnalysisQuery as your model, which then you switch based on a dropdown or radiobutton list, etc. Even more, if your UI doesn't know (or shouldn't know) the list of predefined types, then you will have to construct the UI based on the actual type and values of the object in QueryParameters, in which case you can just declare it as an object and get the information using reflection.
Bottom line, your question is too vague for a clear answer.
In C#, is it possible to read a class tree path in a string and access programmatically a value, given an instance of that class ?
For example:
public class LogGeometricModel
{
public double SmallEndDiameter { get; set; }
public double LargeEndDiameter { get; set; }
public class Log
{
public Guid Id { get; set; }
public LogGeometricModel GeometricModel { get; set; }
}
public class Solution
{
public DateTime TimeStamp { get; set; }
public double Price { get; set; }
public Log RotatedLog { get; set; }
}
The strings could be something like this (a path in the class tree):
SmallEndDiameter = "Solution/RotatedLog/GeometricModel/SmallEndDiameter"
LargeEndDiameter = "Solution/RotatedLog/GeometricModel/LargeEndDiameter"
Price = "Solution/Price"
Id = "Solution/Log/Id"
By reading those strings, I would like to access the actual values of SmallEndDiameter, LargeEndDiameter, Price and Id.
Absolutely possible yes.
public static object GetValue(object instance, string path)
{
object currentObject = instance;
foreach (string propertyName in path.Split('/'))
{
currentObject = currentObject
.GetType()
.GetProperty(propertyName)
.GetValue(currentObject, null);
}
return currentObject;
}
You don't need to include 'Solution' in the string. This obviously lacks error handling, which if you are parsing a string like this, you will want.
I have a web service as per the below ;
[Route("/MyService", Verbs = "POST")]
public class MyData
{
public string GUID { get; set; }
public BankDetails BankDetail { get; set; }
public ContactNameData ContactNames { get; set; }
public List<AddressData> Addresses { get; set; }
}
public class AddressData
{
public string address1 { get; set; }
public string address2 { get; set; }
public string address3 { get; set; }
public string postalCode { get; set; }
public string city { get; set; }
public string state { get; set; }
public string countryCode { get; set; }
}
public class ContactNameData
{
public string title { get; set; }
public string firstName { get; set; }
public string middleName { get; set; }
public string lastName { get; set; }
}
public class BankDetails
{
public string sortCode { get; set; }
public string accountNumber { get; set; }
}
public class My_ServiceStack : Service
{
[Authenticate]
public object Post(MyData data)
{
// do something here
}
}
the problem I have is when I need to leave off the List of Addresses. Sending through a null value for the BankDetails object and the ContactNameData object works as expected but sending through a null value for the List gives me a NullExceptionError
How can I fix this so that I can send through a null to the List?
It is difficult to say what is happening without seeing the concrete line where the exception is thrown.
My best guess is that you do something with the list which is not allowed with null. A NullExceptionError does not exist in C# (except you defined such an Error for yourself), so I assume you mean a NullReferenceException. This exception is thrown when you dereference a null object reference. For example, Addresses.Count will throw such an exception because Count can not be used on a non-existing object.
There are several ways to fix such problems; the most common is to check for null before working with the list. Instead of
int addressCount;
addressCount = Addresses.Count;
you would simply write
int addressCount = 0;
if (Addresses != null)
addressCount = Addresses.Count;
For more concrete information, I would have to see what you do with the list that causes the NullReferenceException.
Thanks Nebr. It was a NullReferenceException error that i was getting and was being caused by code within the web service that was doing something with the Address data. Wrapping that code within a != null if statement sorted the issue. It now works as expected
many thanks
Try sending an empty list rather than a null list