I'm having trouble trying to get ValueInjector to map my objects correctly. This is the code I am using for the mapping:
public IEnumerable<CategoryDTO> FindCategories(IList<object[]> criteria)
{
IEnumerable<Category> categories = _categoryRepo.Find(criteria);
IEnumerable<CategoryDTO> categoriesDto = Mapper.Map<IEnumerable<Category>, IEnumerable<CategoryDTO>>(categories);
return categoriesDto;
}
the variable categories contains a property:
IEnumerable<Standard> Standards
This property contains two Standard objects in the instance I'm calling on. The problem is when I map from my Category to my CategoryDTO. CategoryDTO is defined as this:
public class CategoryDTO : AuditableDTO
{
public Guid CategoryId { get; set; }
public string Name { get; set; }
public string MachineName { get; set; }
public string Description { get; set; }
public IEnumerable<StandardDTO> Standards { get; set; }
}
After the mapping statement is run, and I investigate the contents of categoriesDto.Standards, I can see that it is null. I would have expected my Standards to have mapped, but I'm sure I'm missing something with ValueInjector. Probably something along the lines of telling it how to map Standard to StandardDTO. Any thoughts?
EDIT: I need to clarify, I'm using this http://valueinjecter.codeplex.com/wikipage?title=Automapper%20Simulation&referringTitle=Home
EDIT 2: Digging deeper, I can see that my Iesi.Collections.HashedSet is causing the issue. Categorys' Standards property are typed as Iesi.Collections.ISet, this is turned into the HashedSet. So I guess my real question is how do I check the property for that type and how can I map?
My guess would be that the Mapper.Map doesn't know to map one level deeper than the IEnumerable. Have you tried looping though the collection, mapping it at the Category, CategoryDTO level vs the IEnumerable level?
Related
I have two tables in my database that are linked with 2x 1 to many relations to the same object.
Since we added the second DBLot2 to the database the list in DBLot is not filled with objects anymore.
Is there something we did wrong here?
public class DBNesting
{
[Key]
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public long DBNestingID { get; set; }
public DBLot DBLot { get; set; }
[ForeignKey("DBLot")]
public long DBLotID { get; set; }
public DBLot DBLot2 { get; set; }
[ForeignKey("DBLot2")]
public long? DBLot2ID { get; set; }
}
public class DBLot
{
[Key]
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public long DBLotID { get; set; }
public List<DBNesting> Nestingen { get; set; }
}
This is how we get the objects:
DatabaseContext dc = new DatabaseContext();
dc.DBNesting
.include("DBLot")
.include("DBLot2")
.where(...)
.ToList();
However the other side is not working:
dc.DBLot
.include("Nestingen")
.where(...)
.ToList()
I would expect that all the DBNesting where we used a DBLot in property
DBLot ore DBLot2 shoud be in Nestingen. But the collections are empty.
dc.DBLot
.include("Nestingen")
.where(...)
.ToList()
will not include the DBLot on the Nestingen only the direct object.
So it will have DBLot and a list of Nestingen but that list will not have the DBLot of each of the Nestingen in the list.
So basically you should be able to see... that you have recursion here, object has reference to object which reference itself.
dc.DBLot.include("Nestingen")
.include("Nestingen.DBLot")
.include("Nestingen.DBLot2")
.where(...)
.ToList()
may work, again will only now bring one level deeper, but if that all you need then awesome.
you could enable lazy loading... but also come with "responsibility" wouldn't recommend
ef 6 is not very efficient with include. also there is an extension which allows you to use typed version so include(x=>x.Nestingen), just take the string names out.
is the goal to have nested object relations.. nth levels. something like
Tree data structure in C#
I'm trying to expose static objects using Microsoft.EntityFrameworkCore in a .netcoreapp 2.1.
The data I want to expose is in a .json file, and I can deserialize it without any issue into its corresponding c# classes.
Here's a sample of the structure : https://pastebin.com/SKCKsDJi
For the sake of clarity i suggest you read it using your favourite json reader
And here are the c# version of those objets :
public class FoodItem
{
public int Id { get; set; }
public string Name { get; set; }
public FoodType Type { get; set; }
public string Picture { get; set; }
public float Price { get; set; }
public string Currency { get; set; }
public IEnumerable<Ingredient> Ingredients { get; set; }
public bool IsVegetarian { get; set; }
}
public class FoodType
{
public int Id { get; set; }
public string Name { get; set; }
}
public class Ingredient
{
public int Id { get; set; }
public string Name { get; set; }
}
Lets keep it simple though, there are ingredients, types and items, items are basically sandwiches, which are of a certain type and contain a list of ingredients. All 3 have id's to match them. This is where my problem lies, or so I think.
Everything works fine if I'm just using "Types" for example, in my dbcontext. As soon as I try to add either ingredients or items, or all 3 (which I need, but baby steps), I have the following error.
InvalidOperationException: The instance of entity type 'Ingredient' cannot be tracked because another instance with the key value '{Id: 1}' is already being tracked. When attaching existing entities, ensure that only one entity instance with a given key value is attached.
This is caused when :
public EatupController(EatupContext context)
{
_context = context;
var completeModel = JsonConvert.DeserializeObject<EatUpDataModel>(EatUpDataSet.Complete);
_context.Types.AddRange(completeModel.Types); //Works
_context.Ingredients.AddRange(completeModel.Ingredients); //Crashes here.
//If removed, all is fine but data is incomplete
//_context.Types.AddRange(completeModel.Types); //Unused
//_context.Items.AddRange(completeModel.Items); //Unused
_context.SaveChanges();
}
I don't understand why it's complaining about duplicate ID's, because they're all identical. Except, when I'm referencing the ingredient X in an item, obviously some items will use ingredients used by other items (many sandwiches have tomatoes). But surely that type of relation is allowed.
At first I had id's starting at 0 for all different types of objets, so ingredients ranged from 0 to about 100, items from 0 to 60, and types from 0 to 7. But since I had that error I edited all Id's and I still have the error, which is very confusing.
From what I read, this might also be due to using the context in different threads but this is not the case. If I remove the line that crashes, it stops crashing and I can see the data in the context correctly. In this case, only the types. If I add only the items or the ingredients in the context, it crashes for the same reason, just in another object (ingredient or item).
Where should I go from here? I don't even have a bad solution I could try to implement. My worst idea was to manually change the Id's (which is silly to me, it should work with the older ones), but even that failed.
I'm working my way through a Plural Sight course to get some insight in MongoDB. It's one of the basic courses on the 'path' and it seems to have been made pre 2.0. As such I'm having some trouble understanding some of the subjects the tutor is going through.
I was hoping to see if someone could clarify how to Find any object based on their BsonID?
This is my object:
public class Patient
{
[BsonElement("_id")]
[BsonId]
public string Name { get; set; }
public ICollection<Ailment> Ailments { get; set; }
public ICollection<Medication> Medications { get; set; }
}
And this is how I'm trying to query it:
public HttpResponseMessage Get(int id)
{
var patient = _patients.Find(pat => pat._id == id).ToList();
if(patient == null)
{
return Request.CreateErrorResponse(HttpStatusCode.NotFound, "Patient Not Found..");
}
return Request.CreateResponse(HttpStatusCode.OK, patient);
}
This syntax doesn't allow me to build the project as there isn't a property with the name _id. I've also attempted to have an actual property ID, but that seems to create some sort of a problem when building (most likely since I already have an ID in it, created by BsonID).
I'm really sorry if this question has been asked before, but I seriously couldn't find any help on the subject. It seems like there should be a lot of resources on it (ie. us newbies should be able to get some good sources back when searching for something like "c# querying mongodb find on BsonId").
I fully understand if this is going to be down voted, but I'd really love it if someone with some time over could help me out here.
Your are putting together a C# query which doesn't know about the MongoDB mapping you specify in the attribute [BsonElement("_id")] on your POCO.
What you want is
var patient = _patients.Find(pat => pat.Name == id).FirstOrDefault();
EDIT (for clarification):
The MongoDB C# driver will dissect the filter expression pat => pat.Name == id and based on the attributes on your POCO it will create a MongoDB query for you that uses the "_id" field.
The C# driver for Mongo requires an id field (some way to uniquely identify your documents). This can either be determined by convention or specified explicitly by you.
By convention, if you have a class with a public member of type ObjectId named Id then the C# driver for Mongo will assume this is your id and it will be mapped to a field called _id in your Mongo collection.
For example:
public class Widget
{
public ObjectId Id { get; set; }
public string Foo { get; set; }
}
If you don't want to go with the convention for some reason (for example if you have a natural key that you'd rather use) then you can use the BsonId attribute to tell the Mongo driver that you want to use some other field/type as your Id.
For example:
public class Widget
{
[BsonId]
public ObjectId WidgetId { get; set; }
public string Foo { get; set; }
}
Or using a type other than ObjectID:
public class Widget
{
[BsonId(IdGenerator=typeof(StringObjectIdGenerator))]
public string WidgetId { get; set; }
public string Foo { get; set; }
}
Can you AutoMap on objects with children of themselves?
In this example:
public class Book
{
public int? BookKey { get; set; }
public Categories bookCategories { get; set; }
}
public class Categories
{
public int? CategoryKey { get; set; }
public List<Book> RecommendedBooks { get; set; }
}
Mapper.CreateMap<Common.BookList, Book>().IgnoreAllNonExisting();
Mapper.AssertConfigurationIsValid();
Mapper.CreateMap<Common.Categories, Categories>().IgnoreAllNonExisting();
Mapper.AssertConfigurationIsValid();
Swapping the last two maps around causes errors each time. Book first, means it doesn't understand categories, and categories first means it doesn't understand books.
AutoMapper.AutoMapperConfigurationException: The following property on Common.BookList / Common.Categories cannot be mapped.
You only need to call the configuration validation once. It might make sense to have it multiple times to make debugging easier (which gives you an exception closer to the location of the mapping code), but in this case, the maps are dependent on each other (Building a map of Book requires Automapper to know how to map bookCategories).
Change the code to the below, and it will work fine
Mapper.CreateMap<Common.BookList, Book>().IgnoreAllNonExisting();
Mapper.CreateMap<Common.Categories, Categories>().IgnoreAllNonExisting();
Mapper.AssertConfigurationIsValid();
Suppose I have a class (something like this):
public class User
{
public Guid Id { get; set;}
public DateTime CreationDate { get; set; }
public string Name { get; set; }
public UserGroup Group { get; set; }
}
Is there a way get all property types that are not part of the .NET Framework.
So in this case I want to get only UserGroup ? Is that possible ?
The best thing I can come up with is something like:
IEnumerable<Type> types = typeof(User).GetProperties().Where(p => !p.PropertyType.Namespace.StartsWith("System.")).Select(t => t.PropertyType);
But this looks like a hack-job. Sorry if dup, couldn't find anything like that, and sorry for the formatting, did my best.
I think what you have is probably reliable enough for what you want. However, from a flexibility/readability point of view maybe it would be better to define your own attribute type which you could apply at property level i.e.
public class User
{
public Guid Id { get; set; }
public DateTime CreationDate { get; set; }
public string Name { get; set; }
[CustomProperty]
public UserGroup Group { get; set; }
}
You could then use reflection to query all the properties which have this attribute. This would give you the ability to include/exclude any properties.
Sorry I forgot to mention that I can't modify the domain entities. (can't add an attribute).
You can use the MetadataType to add attributes to a base class e.g.
class UserMetadata
{
...
[CustomProperty]
public UserGroup Group { get; set; }
}
[MetadataType(typeof(UserMetadata)]
public class DomainUser : User
{
}
Reflection is always some kind of hacking, so yes, it FEELS like a hackjob.
But analyzing the entity, the class, will have to be done with reflection. So you are doing it the correct way.
One pitfall. You yourself are able to create your own types inside a namespace "System". That would mess up your search. You also could also analyze then Assembly of the property type, but then you have to know of all .NET assemblies, which is a big list.
What you have done is fine, you could do something like this however and make use of the Except method.
public static Type[] GetNonSystemTypes<T>()
{
var systemTypes = typeof(T).GetProperties().Select(t => t.PropertyType).Where(t => t.Namespace == "System");
return typeof(T).GetProperties().Select(t => t.PropertyType).Except(systemTypes).ToArray();
}
Your approach works. I would just throw in that you could also try to check the Assembly the type was defined in, and for example check if it was loaded from the global assembly cache.
bool wasLoadedFromAssemblyCache = typeof(T).Assembly.GlobalAssemblyCache;