Let's say I have these 2 classes:
public class Foo {
public int MyProperty1 { get; set; }
public int MyProperty2 { get; set; }
public string SomeTextPropery { get; set; }
public object Tag { get; set; }
}
public class SomeObject {
public string Description { get; set; }
public string Description2 { get; set; }
}
And in my console application I do something like this:
var listOfFoos = new List<Foo>( );
//listOfFoos.Add(foo1) x 10
foreach (var foo in listOfFoos)
{
var someObject = new SomeObject( );
foo.Tag = someObject;
}
listOfFoos.OrderBy(x => x.Tag.Description2)
So the last part (OrderBy) won't work, because we don't know of what type Tag is.
But in my console application (frontend) I know what I set my tag to and I should be able to use a property on the Tag object in OrderBy. Is there away I can sort on Description2 when Tag is of object type?
You will need to cast the Tag property to the appropriate type:
listOfFoos.OrderBy(x => ((SomeObject)x.Tag).Description2);
Of course this requires you to actually know what the type of the Tag property is at runtime.
But if you want to order by a property called Description2, the Tag property must return an object that has this property anyway.
Maybe a common base class for all involved types, where the Description2 property is defined, is the solution to your issue? Then you can change the type of the Tag property from object to this particular type and avoid the casting.
Related
Hey … I am facing a pratical code challenge in my project.
Let's say I have a Model class has a property called Configration. The Model object has different type of configration. For example, The Model was made of by Two Pipe objects, then one Lock object and another ten Pipe objects and they have to be in sequence.
In my project, all entity classes are derived from an Entity class that only has Name and ID property.
Model.cs
class Model{
public int ID { get; set; }
public List<ConfigrationEntry<Entity>> Config {get;set;}
}
class Pipe{
public int Length { get; set; }
}
class Lock{
public string Brand { get; set; }
}
So I create a ConfigrationEntry class that contains generic parameter.
class ConfigrationEntry<T> where T : Entity{
public int Number { get; set; }
public T Entity {get; set;}
public ConfigrationEntry(string name, int num){
Entity = new T (name);
Number = num;
}
}
However, if I want to add this ConfigrationEntry into Model.Config List. I won't allow me to do that.
class Main{
var configEntry1 = new ConfigrationEntry<Pipe>("Test1", 10);
var configEntry2 = new ConfigrationEntry<Lock>("Test2", 3);
var configEntry3 = new ConfigrationEntry<Pipe>("Test3", 3);
var Model = new Model();
// I cannot add this entry. IDE says it has to be ConfigrationEntry<Entity> type even Entity is the base class.
// model.Config.Add(configEntry1);
}
So what's the best practical solution for this scenario? Right now I am thinking two use Dictionary<string, int> and use string to find the object.
Yes you're right. You try to use covariant or contravariant (the Entity property is get; set;) and that's not working that way.
In your case you need to add an interface or base class that handles the Entity as base type and add those to the List<>.
Btw: There is a typo in ConfigrationEntry.
Change your configuration entry class to:
interface IConfigurationEntry
{
int Number { get; set; } // is set required?
Entity Entity { get; set; } // is set required?
}
class ConfigurationEntry<T> : IConfigurationEntry
where T : Entity
{
public int Number { get; set; }
public T Entity {get; set;}
Entity IConfigurationEntry.Entity
{
get => this.Entity;
set => this.Entity = (T)value;
}
public ConfigurationEntry(string name, int num){
Entity = new T (name); // this looks strange!
Number = num;
}
}
After having an interface for all ConfigurationEntry<T> instances you're able to change your model:
class Model{
public int ID { get; set; }
public List<IConfigurationEntry> Config {get;set;}
}
If you're using serialization for that Model you need to serialize any type descriptor to deserialize correct instances - but that's another topic and depends on which serialization is used.
I have an application that has two similar but different objects and I want to store those objects in the same collection. What is the best way to do this? And how can I query this collection?
Today my collections is represented by:
public IMongoCollection<Post> Posts
{
get
{
return _database.GetCollection<Post>("posts");
}
}
And I have this class:
public class Post
{
public string Id { get; set; }
public string Message { get; set; }
}
public class NewTypePost
{
public string Id { get; set; }
public string Image { get; set; }
}
So, today I just can save and query using Post class. Now I want to store and retrive the both classes, Post and NewTypePost.
I tried to change the class type from Post to dynamic. But when I did this, I could not query the collections.
MongoDB .NET driver offers few possibilites in such cases:
Polymorphism
You can build a hierarchy of classes and MongoDB driver will be able to determine a type of an object it gets retrieved from the database:
[BsonKnownTypes(typeof(Post), typeof(NewTypePost))]
public abstract class PostBase
{
[BsonId]
public string Id { get; set; }
}
public class Post: PostBase
{
public string Message { get; set; }
}
public class NewTypePost: PostBase
{
public string Image { get; set; }
}
MongoDB driver will create additional field _t in every document which will represent corresponding class.
Single Class
You can still have Post class and use BsonIgnoreIfNull attribute to avoid serialization exception. MongoDB .NET driver will set those properties to null if they don't exist in your database.
public class Post
{
[BsonId]
public string Id { get; set; }
[BsonIgnoreIfNull]
public string Message { get; set; }
[BsonIgnoreIfNull]
public string Image { get; set; }
}
BsonDocument
You can also drop strongly-typed approach and use BsonDocument class which is dynamic dictionary-like structure that represents your Mongo documents
var collection = db.GetCollection<BsonDocument>("posts");
More details here
dynamic
Specifying dynamic as generic parameter of ICollection you should get a list of ExpandoObject that will hold all the values you have in your database.
var collection = db.GetCollection<dynamic>("posts");
var data = collection.Find(Builders<dynamic>.Filter.Empty).ToList();
var firstMessage = data[0].Message; // dynamically typed code
Suppose I have the next conn to a test database:
var mongoClient = new MongoClient(new MongoClientSettings
{
Server = new MongoServerAddress("localhost"),
});
var database = mongoClient.GetDatabase("TestDb");
Then I can do something like:
var col = database.GetCollection<Post>("posts");
var col2 = database.GetCollection<NewTypePost>("posts");
To get two different instances of IMongoCollection but pointing to the same collection in the database. Further I am able to save to each collection in the usual way:
col.InsertOne(new Post { Message = "m1" });
col2.InsertOne(new NewTypePost { Image = "im1" });
Then, I'm also able to query from those collection base on the specific fields:
var p1= col.Find(Builders<Post>.Filter.Eq(x=>x.Message, "m1")).FirstOrDefault();
var p2 =col2.Find(Builders<NewTypePost>.Filter.Eq(x=>x.Image, "im1")).FirstOrDefault();
Console.WriteLine(p1?.Message); // m1
Console.WriteLine(p2?.Image); // im1
I don't know if that's what you want but it uses the same collection. BTW, change the Id properties to be decorated with [BsonId, BsonRepresentation(BsonType.ObjectId)]. Hope it helps.
Use the BsonDocument data type. It can do all of that. BsonDocument and dynamic back and forth is very convenient.
public class CustomObject{
public long Id{get;set;}
public string Name{get;set;}
public List<(string,object)> CollectionDynamic{get;set;}
}
// inserted in mongo
//public class CustomObject_in_Db{
// public long Id {get;set;}
// public string Name {get;set;}
// public string field2 {get;set;}
// public string field3 {get;set;}
// public string field4 {get;set;}
// public string field5 {get;set;}
// }
// something code... mapper(config)
Automapper.Mapper.CreateMap<BsonDocument,CustomObject>()
.ForMember(dest=>dest.Id, a=>a.MapFrom(s=>s.Id.GetValue(nameof(CustomObject.Id)).AsInt64)
.ForMember(dest=>dest.Name, a=>a.MapFrom(s=>s.Id.GetValue(nameof(CustomObject.Name)).AsString)
.ForMember(dest=>dest.CollectionDynamic, a=>a.MapFrom(s=>_getList(s));
// .......
private List<(string, object)> _getList(BsonDocument source){
return source.Elements.Where(e=>!typeof(CustomObject).GetProperties().Select(s=>s.Name).Any(a=>a ==e.Name)).Select(e=>e.Name, BsonTryMapper.MapToDotNetValue(e.Value)));
}
I have a bunch of classes generated by EF that are simple tables and have similar structures:
public class Contact
{
public int ID { get; set; }
public string Description { get; set; }
}
public class Member
{
public int ID { get; set; }
public string Description { get; set; }
}
I've also got a method for returning an object of a specified type:
public T GetInstance<T>(string type)
{
return (T)Activator.CreateInstance(Type.GetType(type));
}
What I want to do is something like this:
public ActionResult GetAll(string ClassType) // ClassType will be the name of one of the classes above
{
Object LookupType = GetInstance<Object>(ClassType);
List<LookupType> AllList = new List<LookupType>();
AllList = repo.GetAll<LookupType>().ToList<LookupType>(); // some generic method that will return a list;
}
This makes the compiler mad because I'm using a variable (LookupType) rather than a true type to build the list. However, neither of these work either:
List<LookupType.GetType()> Items = new List<LookupType.GetType()>();
List<typeof(LookupType)> Items = new List<typeof(LookupType)>();
Both cause an error - "Using generic type List requires 1 type argument".
Is there a proper way to do this? Is there a way to convert ClassType directly to a type without first making it an object (from which I hope to derive the type)?
Try using the CreateInstance method
SomeObject someObject = new SomeObject();
Type type = someObject.GetType();
Type listType = typeof(List<>).MakeGenericType(new [] { type });
IList list = (IList)Activator.CreateInstance(listType);
You cannot do it with C#!!
Compiler must to know the parameter type.
In that maybe you would like to accomplish, interfaces will help you
public class Contact: IIdDescription
{
public int ID { get; set; }
public string Description { get; set; }
}
public class Member: IIdDescription
{
public int ID { get; set; }
public string Description { get; set; }
}
public interface IIdDescription
{
int ID { get; set; }
string Description { get; set; }
}
public ActionResult GetAll(string type)
{
var AllList = new List<IIdDescription>();
if(type == Member.GetType().Name)
AllList = repo.Set<Member>().Cast<IIdDescription >().ToList();
if(type == Contact.GetType().Name)
AllList = repo.Set<Contact>().Cast<IIdDescription >().ToList();
...
}
and into your view use interface as model, something like
#model IEnumerable<IIdDescription>
If you don't know the type ahead of time maybe using a list of dynamic objects can help.
var item = GetInstance<Contact>("Namespace.Contact");
var items = new List<dynamic>();
items.Add(item);
You can then access the types like so...
Contact contact = items[0];
Just be aware that using dynamic can be expensive.
I have an XML coming in this form:
<run>
<foo status="1">111.9</foo>
<fred status="0">5.5</fred>
</run>
I would like to deserialize this in either of these forms below (I'm undecided, and hoping an answer will help me decide, although I tend to prefer #1, for design aesthetics as much as anything else):
Case # 1
[Serializable]
public class DataValue
{
[XmlAttribute("status")]
public int Status { get; set; }
// I need something here, but what?
public float Value { get; set; }
}
[Serializable]
[XmlRoot("run")]
public class DataBag
{
[XmlElement("foo")]
public DataValue Foo{ get; set; }
[XmlElement("fred")]
public DataValue Fred{ get; set; }
}
When I try this, I get a value of 0 for either member foo or fred.
Case # 2
[Serializable]
[XmlRoot("run")]
public class DataBag2
{
[XmlElement("foo")]
public float Foo{ get; set; }
[XmlElement("foo")]
[XmlAttribute("status")]
public int Foo_status { get; set; }
[XmlElement("fred")]
public float Fred{ get; set; }
[XmlElement("fred")]
[XmlAttribute("status")]
public int Fred_status { get; set; }
}
It compiles but I get an InvalidOperationException while reflecting Foo_status, for which the innermost exception is "For non-array types, you may use the following attributes: XmlAttribute, XmlText, XmlElement, or XmlAnyElement."
What can I do to end up with an actual value in case #1, or no exception (and a valid value and status) for case #2?
The code for the serialization goes like this:
// Case 1
using (var sr = new StreamReader("data.xml"))
{
var xs = new XmlSerializer(typeof(DataBag));
var run = (DataBag)xs.Deserialize(sr);
Console.WriteLine("Got a run: {0}-{1}", run.Fred.Value, run.Fred.Status);
// Issue here is that value is always 0, but status is accurate
}
// case 2
using (var sr = new StreamReader("data.xml"))
{
var xs = new XmlSerializer(typeof(DataBag2));// Exception here
var run = (DataBag2)xs.Deserialize(sr);
Console.WriteLine("Got a run: {0}-{1}", run.Foo, run.Foo_status);
}
Thanks for your attention!
For case 1 you just need to mark it as XMLText:
[XmlText]
public float Value { get; set; }
You want to use [XmlText]:
Indicates to the XmlSerializer that the member must be treated as XML text when the class that contains it is serialized or deserialized.
Thus:
public class DataValue
{
[XmlAttribute("status")]
public int Status { get; set; }
[XmlText]
public float Value { get; set; }
}
Case #2 just won't work as you want. Adding [XmlAttribute("status")] to Foo_status means that Foo_status will be serialized as an attribute of DataBag2, not Foo. Applying [XmlElement("foo")] as well then says it's an element of DataBag2, which is of course in conflict with other attribute.
There's no way with XmlSerializer for an outer container type to specify an attribute to be applied to a nested element.
I have the below code example where I want to prepend some text on to the front of the source param which could be of type ISite or IFactory used in a MVC drop down list via a MoreLinq extension. This function is used in a controller action that returns the list serialised as JSON so that it can be loaded dynamically using some dropdown cascading, otherwise I would have done this separately in a view model.
I was wondering if there is a way to be able to do something similar to the below without having to create a concrete ListItem class, my example shows how I am using as IListItem but I am aware this won't compile.
At the moment my controller has no concept of any of my model's concrete classes and I kind of wanted to keep it that way and I'm not sure if I definately need to make an instance of a ListItem to make this work or if anyone has another suggestion?
My knowledge of Covariance and Contravariance in Generics is limited, in case that is of importance here? Thanks
public interface IListItem
{
int Id { get; set; }
string Name { get; set; }
}
public interface ISite : IListItem
{
int CountryId { get; set; }
}
public interface IFactory : IListItem
{
int SiteId { get; set; }
}
public interface IResource
{
int Id { get; set; }
string Name { get; set; }
int ContentID { get; set; }
string Text { get; set; }
int LanguageID { get; set; }
string LanguageCode { get; set; }
int Priority { get; set; }
}
private IEnumerable<IListItem> PrependSelectionResource(IEnumerable<IListItem> source, string languageCode)
{
if(source == null || source.Count() == 1)
return source; // don't bother prepending the relevant resource in these cases
try
{
// will throw an exception if languageCode is null or blank
var resource = _resourceRepository.GetByNameAndLanguageCode(
"Prompt_PleaseSelect",
languageCode);
if(resource == null)
return source;
// prepend the "Please Select" resource to the beginning of the source
// using MoreLinq extension
return source.Prepend(new {
Id = 0,
Name = resource.Text ?? ""
} as IListItem);
}
catch {
return source;
}
}
Unfortunately, there's nothing you can do. You'll have to pass a concrete instance of a class that implements the IListItem interface to the Prepend method.
If you're worried about exposing implementations elsewhere in your application, you could create a local implementation to your file called PlaceholderListItem and use that instead.
An anonymous type (like new { Id = 0, Name = resource.Text ?? "" }) is a class that does not implement any interfaces (and has no other base classes than object). So it won't work.
If you use a mocking framework, you can create a mock that is some magically generated class which implements the interface. Sometimes also called a stub.
But in this case, it's really easy to write your own mock, of course:
class ListItemMock : IListItem
{
public int Id { get; set; }
public string Name { get; set; }
}
then you can instantiate it with an object initializer: new ListItemMock { Id = 0, Name = resource.Text ?? "", }.