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;
Related
I did not find any answer on the forum (however if there is one please let me know). I am writing backend structure for the ASP.NET MVC app and have some troubles with C# case.
I am wondering if such a solution is possible to happen (and need to do a following thing the way I described or similarly).
I will show the problem in the example below, because I do not know how to say it clearly in words.
I have defined Enum as following:
public enum myEnum
{
FirstOpt = 0,
SecondOpt= 1,
ThirdOpt = 2,
Unknown = -1,
}
Now, I want to assign myEnum in the different part of my solution in the custom attribute.
The attribute looks the following way:
public class SearchTypeAttribute : Attribute
{
public Type SType { get; set; }
public IEnumerable<object> SItems { get; set; }
}
Then I want to use it in the following way:
public class SomeClass
{
[Key]
public long Id { get; set; }
[SearchTypeAttribute(
SType = typeof(Enums.myEnum),
SItems = Enum.GetValues(typeof(Enums.myEnum))
)]
public string Type{ get; set; } // later to this item will be assigned string name of the assigned value
}
When I am doing this, the following error appears:
Error CS0655 'SItems' is not a valid named attribute argument because it is not a valid attribute parameter type
I was also trying to assign it as here:
public class SomeClass
{
[Key]
public long Id { get; set; }
[SearchTypeAttribute(
SType = typeof(Enums.myEnum),
SItems = Enums.myEnum // here !
)]
public string Type{ get; set; }
}
But I still have no idea what "Type" of property I should use in my SearchTypeAttribute, to be able to assign those values there.
I am doing this to be able to generate different types of fields in search bars in the views later.
Then in my code I want to assign the list of enum values or the specific enum to some variable, so I can then, operate on those values.
What types should I use to assign this type of data SItems ?
Is there other approach to do it ?
I am not yet really advanced in c#. Thank you for any help in advance.
An attribute is a compile-time thing. So you have to provide all the information at compile-time also. However Enum.GetValues will be executed at runtime, making it impossible to be used for an attribute. The only way to achieve this is by writing the possible enum-values directy into the attribute:
public class SomeClass
{
[Key]
public long Id { get; set; }
[SearchTypeAttribute(
SType = typeof(Enums.myEnum),
SItems = new[] { Enums.FirstOpt, Enums.SecondOpt, ...}
)]
public string Type{ get; set; }
}
Apart from this I can´t see why your SItems is of type IEnumerable<object>, when it obviously has only Enums-elements in it. It´s not even possible to use an IEnumerable on an attribute, only arrays of primitive types are allowed, as mentioned here. So SItems should be an Enums[].
Another approach is to rely on the attributes constructor and initialize SItems from there:
public class SearchTypeAttribute : Attribute
{
public SearchTypeAttribute(Type type)
{
this.SType = type;
this.SItems = Enum.GetValues(type);
}
}
Now simply use the attribute as follows:
[SearchTypeAttribute(typeof(Enums.myEnum))]
public string Type{ get; set; }
This is my perfect idea of day: Strong typed ids in Entity Framework.
Motivation:
Comparing ModelTypeA.ID and ModelTypeB.ID is (at least almost) always an error. Why not compiletime handle it?
If you are using example per request DbContext its easy to implement get directly model from id. "id.Value.ProductNumber"
Code will be more self declarative.
They are just naturally typed so why not?
Ok here is my implementation. I hope its pretty self declarative what I mean.
//Optional interface may be handy on some scenarios
public interface Identifiable<T> where T : class, Identifiable<T>
{
DbId<T> ID { get; set; }
}
public class TestModel1 : Identifiable<TestModel1>
{
[Key]
public DbId<TestModel1> ID { get; set; }
public string Data1 { get; set; }
}
public class TestModel2 : Identifiable<TestModel2>
{
[Key]
public DbId<TestModel2> ID { get; set; }
public string Data2 { get; set; }
public DbId<TestModel1> TestModel1ID { get; set; }
public virtual TestModel1 TestModel1 { get; set; }
}
[Serializable]
public class DbId<T> where T : class
{
public int ID { get; set; }
public static implicit operator DbId<T>(int id)
{
var c = new DbId<T>() { ID = id };
return c;
}
public static implicit operator int (DbId<T> id)
{
return id.ID;
}
}
When creating migration its jus complain that there is no key. When trying to set key on fluent api it give more precious error: The property 'ID' cannot be used as a key property on the entity 'MyNs.Models.TestModel1' because the property type is not a valid key type. Only scalar types, string and byte[] are supported key types.
Ok understood key cannot be any type but data of my type is just one int witch even has implicit conversions. Inheriting int in this situation is very tempting but like we know its impossible.
Primary question: How to finish this and tell to EF that converting my DbId to int and back is not rocket science.
Secondary question: Is this good idea? Why? Do you suggest feature request if this is not currently possible?
I believe I understand your goal: your aim is to encapsulate the primary key of each model such that primary keys of two different models cannot be compared directly. So, for example, you would want to avoid the comparison Customer.ID == Order.ID at compile time.
However, in your code example, the implicit operator for int <-> DbId<T> works against your goal because this code compiles:
var model1 = new TestModel1() {ID = 1};
var model2 = new TestModel2() {ID = 2};
Console.WriteLine(model1.ID == model2.ID);
So, if I follow your reasoning, it would not work even if EF6+ allowed [Key] on a class (other than string.)
Getting back to basics, if you believe the name ID is too ambiguous, why not follow the Entity Framework Primary Key Convention of class name followed by "ID"?
Example:
public class Customer
{
// [Key] is implicit by convention
public int CustomerID { get; set; }
public string Name { get; set; }
}
public class Order
{
// [Key] is implicit by convention
public int OrderID { get; set; }
public DateTime SubmittedDate { get; set; }
// [ForeignKey("Customer")] is implicit by convention
public int CustomerID{ get; set; }
public virtual Customer Customer { get; set; }
}
This naming convention (along with plain ID) is the convention I see the most in Entity Framework code. So, it has the major benefit of allowing other people to step in and seamlessly acclimate and maintain your code (a benefit that we tinkerers are all guilty of overlooking sometimes!)
Looking at your motivations...
Comparing ModelTypeA.ID and ModelTypeB.ID is (at least almost) always an error.
Are you solving a problem that isn't actually a problem? How often do programmers really screw up Order.CustomerID == Customer.CustomerID?
Code will be more self declarative.
Up to debate? If I spot DbId<Customer> id = Customer.ID in someone's code, is it really more declarative than int id = Customer.CustomerID?
That said, I applaud your effort! Solving problems is what we programmers love to do. Good luck!
This one takes a little explaining. I have a set of types such that;
public class Child
{
public int ID { get; set;}
}
public class MayHaveChild
{
public Child Value { get; set; }
public int MayID { get; set; }
}
public class MustNotHaveChild { get; set; }
{
public List<MayHaveChild> MayValues { get; set; }
}
In the above scenario, I want any mapping of MayHaveChild to have the values for the Child object, except when I have mapped MustNotHaveChild. E.g.;
When I have
//...some code
MayHave obj = Mapper.Map<MayHaveChild>(childObj);
// I want to be able to access obj.Child.ID
But when I have
//...some code
MustNotHave obj = Mapper.Map<MustNotHaveChild>(notHaveObj);
// I want to be able to access obj.MayValues[0].MayID but
// *not* obj.MayValues[0].Value
I've been through the automapper documention on nesting, polymorphism, lists, etc and I can't find anything that quite matches what I want.
I could solve this by having a inheriting the MayHave class to a MustNotHave variant but this would involve changing quite a lot of existing code. Is there a way to configure Automapper in the manner I need?
I couldn't find a way to configure AutoMapper the way I wanted without going down the inheritance route - though this proved less problematic than I thought. I did something like the following;
public class NoChild : MayHaveChild
{
}
public class MustNotHaveChild { get; set; }
{
// \/--datatype change here
public List<NoChild> MayValues { get; set; }
}
Then, later in the AutoMapper config;
Mapper.CreateMap<MayHave, NoChild>()
.ForMember(c => c.Child, opt => opt.Ignore());
I have an interface ITranslateStuff and a static class and method with a generic parameter that is constrained (where class, ITranslateStuff, new()).
string translation = Translator.TranslateStuff<ITranslateStuff>();
Depending on which implementation of ITranslateStuff that I pass the method returns a different string.
I have ViewModels with a lot of different properties wich returns implementations of ITranslateStuff, for example:
public class ExampleViewModel
{
public string OtherStuff {get; set; }
public string TranslateStuffExample1 Translations { get; set; }
public ExampleViewModel2 SubModel {get; set; }
}
public class ExampleViewModel2
{
public string MoreStuff { get; set; }
public string TranslateStuffExample2 Translations { get; set; }
}
where both DoStuffExample1 and DoStuffExample2 implements ITranslateStuff.
I'm currently populating all theese properties with code like this:
model.Translations = Translator.TranslateStuff<TranslateStuffExample1>();
model.SubModel.Translations = Translator.TranslateStuff<TranslateStuffExample2>();
In the project we use StructureMap. I want to avoid setting all the properties on my view model manually with the same static method call. I have an ActionFilter where I set common properties on my view model, and was thinking I want to do this in an action filter as well.
I've tried finding something in StructureMap that can do this for me.
How can I solve this?
You will want to use 'setter injection'.
http://docs.structuremap.net/ConstructorAndSetterInjection.htm#section4
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?