Multiple Description Attribute in c# - c#-4.0

DescriptionAttribute not allow to be set multiple times.
Is there any way to have such opportunity to set kind of DescriptionAttribute many times to property or enum for example.

Solution:
public class ExtraDescriptionAttribute : DescriptionAttribute
{
private string extraInfo; public string ExtraInfo { get { return extraInfo; } set { extraInfo = value; } }
public ExtraDescriptionAttribute(string description)
{
this.DescriptionValue = description;
this.extraInfo = String.Empty;
}
}

You should be calling the base class constructor and remove the Description property. This also shows how to set the ExtraInfo property.
public class ExtraDescriptionAttribute : DescriptionAttribute
{
public String ExtraInfo { get; private set; }
public ExtraDescriptionAttribute (String description, String extraInfo) : base(description)
{
ExtraInfo = extraInfo;
}
}
The description attribute will now look like:
[ExtraDescriptionAttribute("Description", "ExtraInfo")]

Related

inherited class AutoMapper.AutoMapperMappingException

I am new at automapper and it is a very good stuff easy to use, but now I have a problem with it. Trying to convert my derived class to base and it gives me
AutoMapper.AutoMapperMappingException
Missing type map configuration or unsupported mapping.
Mapping types: ClientEventDb -> EventId
Database.ClientEventDb -> EventId
Destination path: ClientEvent
Source value:
Event:Login
Automapper wants to convert ClientEventDb to EventId? I don't understand why. EventId is an enum...
Please help me I have run out of ideas.
Here is the code which I run:
ClientEventDb[] edbl;
using (var context = new DbEntities())
{
edbl=context.Events.Take(1000).ToArray();
}
Mapper.CreateMap<ClientEventDb, ClientEvent>();
Console.WriteLine("hello");
return edbl.Select(edb => Mapper.Map<ClientEvent>(edb)).ToArray();
Here are my classes
[Table("events", Schema = "public")]
public class ClientEventDb : ClientEvent
{
public ClientEventDb(string userName, EventId happening, object userObject = null)
: base(userName, happening, userObject)
{
}
public ClientEventDb()
{
}
}
[ProtoContract]
[Table("events", Schema = "public")]
public class ClientEvent : ClientEventBase
{
[ProtoMember(1)]
[Column("username")]
public string UserName { get; private set; }
[ProtoMember(2)]
[Column("time")]
public DateTime DateTime { get; private set; }
[ProtoMember(3)]
[Key]
[Column("id")]
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public string Id { get; private set; }
[ProtoMember(4)]
[Column("data")]
public byte[] UserObject { get; set; }
public ClientEvent(string userName,EventId happening, object userObject=null) : base(happening)
{
UserName = userName;
DateTime = DateTime.Now;
//UserObject = null;
if (userObject!=null) throw new NotImplementedException();
}
public ClientEvent()
{
}
protected ClientEvent Clone()
{
return (ClientEvent)MemberwiseClone();
}
}
[ProtoContract]
[ProtoInclude(10, typeof(ClientEvent))]
public class ClientEventBase
{
[Column("eventid")]
[ProtoMember(1)]
public int EventIdValue { get; set; } //must be public because of entity framework
[NotMapped]
public EventId EventId
{
get { return (EventId) EventIdValue; }
set { EventIdValue = (int) value; }
}
public ClientEventBase(EventId eventId)
{
EventId = eventId;
}
public ClientEventBase()
{
}
public override string ToString()
{
return String.Format("Event:{0}",EventId);
}
}
public enum EventId
{
Login = 1,
Logout,
ExitApplication,
}
UPDATE
bugfix: ClientEvent [Key] attribute moved to id property
Solution was this (thx to stuartd):
ClientEventDb[] edbl;
using (var context = new DbEntities())
{
edbl=context.Events.ToArray();
}
Mapper.CreateMap<ClientEventDb, ClientEvent>().ConstructUsing((ClientEventDb src) => new ClientEvent());
return edbl.Select(Mapper.Map<ClientEvent>).ToArray();
AutoMapper is confused as its made to map between similar properties in different classes, you are using it incorrectly - you just need to go from the derived class to the base which does not require AutoMapper. You could use this to do what you need....
ClientEventDb[] edbl;
using (var context = new DbEntities())
{
edbl=context.Events.Take(1000).ToArray();
}
return edbl.Cast<ClientEvent>().ToList();
I'd be looking at why you even feel you need a derived ClientEventDb though - understand we dont have the whole picture here but it seems to do nothing in addition to what the base class already does.
The issue is that ClientEvent has two constructors but you have not told AutoMapper which to use.
If you want it to use your constructor with parameters, change your mapping code to this and it will work:
Mapper.CreateMap<ClientEventDb, ClientEvent>()
.ConstructUsing(src => new ClientEvent(src.UserName, src.EventId));
Or to make AutoMapper use the default constructor:
Mapper.CreateMap<ClientEventDb, ClientEvent>()
.ConstructUsing((ClientEventDb src) => new ClientEvent());

Generic Automapper function with custom convension for underscored properties

I simply need to map some auto generated classes from database to domain/viewmodels classes. The autogenerated class may have names like customer_id that I want to be mapped with CustomerId. Somehow I want to register my own convention with auto mapper. I have started with following code however the mapped object properties are not populated:
// Generic method that should map source to target
public static TTarget MapWithUnderScoreConvension(TSource source, TTarget target)
{
Mapper.Initialize(cfg=> cfg.AddProfile<AutoMapperUnderScoreProfile>());
Mapper.CreateMap<TSource, TTarget>();
var mapped = Mapper.Map(source, target);
return mapped;
}
// New underscore profile
public class AutoMapperUnderScoreProfile : Profile
{
public AutoMapperUnderScoreProfile()
{
Mapper.Initialize(configuration => configuration.CreateProfile("UnderScoreProfile", UnderScoreProfile));
Mapper.AssertConfigurationIsValid();
}
private void UnderScoreProfile(IProfileExpression profile)
{
profile.SourceMemberNamingConvention = new PascalCaseNamingConvention();
profile.DestinationMemberNamingConvention = new SourceUnderScoreNamingConvension();
}
}
// Convention class
public class SourceUnderScoreNamingConvension : INamingConvention
{
private readonly string _separatorCharacter="_";
private readonly Regex _splittingExpression = new Regex(#"[\p{Lu}0-9]+(?=_?)");
public Regex SplittingExpression { get { return _splittingExpression;} private set{} }
public string SeparatorCharacter { get { return _separatorCharacter; } private set{} }
}
// Test cases
[TestMethod()]
public void Test_Map_Db_Generated_Class_To_Model()
{
var dbGenerated = new QuestionTypeFromDb()
{
QuestionType_Description = "1",
QuestionType_Id = 1,
QuestionType_Is_Default = true,
QuestionType_Is_TimeBased = true,
QuestionType_Sequence = 1,
QuestionType_Time_In_Seconds = 1
};
var mappedObject = AutoMapperHelper<QuestionTypeFromDb, QuestionType>
.MapWithUnderScoreConvension(dbGenerated, new QuestionType());
mappedObject.QuestionTypeId.Should().Be(dbGenerated.QuestionType_Id);
mappedObject.QuestionTypeDescription.Should().Be(dbGenerated.QuestionType_Description);
mappedObject.TimeInSeconds.Should().Be(dbGenerated.QuestionType_Time_In_Seconds);
mappedObject.QuestionTypeSequence.Should().Be(dbGenerated.QuestionType_Sequence);
}
public class TestQuestionWithAnswerType
{
public int QuestionTypeId { get; set; }
public string QuestionTypeDescription { get; set; }
public int QuestionTypeSequence { get; set; }
public bool QuestionTypeIsTimeBased { get; set; }
public int? QuestionTypeTimeInSeconds { get; set; }
public bool QuestionTypeIsDefault { get; set; }
}
any comments will be appreciated.
Update
I have found that the following workaround works:
I simply replaced used this -> to replace 'underscore' with nothing (Mapper.Initialize(c => c.ReplaceMemberName("_", ""));
public static TTarget MapWithUnderScoreConvension(TSource source, TTarget target)
{
Mapper.Initialize(c => c.ReplaceMemberName("_", ""));
//Mapper.Initialize(cfg => cfg.AddProfile<AutoMapperUnderScoreProfile>());
Mapper.CreateMap<TSource, TTarget>();
var mapped = Mapper.Map(source, target);
return mapped;
}
Your regex needs to be changed to : [\p{L}}0-9]+(?=_?)
This will take care of Customer_Id, CUSTOMER_ID, customer_id
The {L} unicode category includes Lu, Lt, Ll, Lm and Lo characters as mentioned here.
Answer is added in the Update section of the question. Basically the solution for me was very simple -> Mapper.Initialize(c => c.ReplaceMemberName("_", ""));

Azure Table Storage Entity Row/Primary Key as Attribute for existing properties

I already have entity migrated from EntityFramework.
I'm don't want override some propeties and Convert it to string
public class User : TableEntity, ITableStorageEntity<int, Guid>
{
[RowKey]
public Guid ID { get; set; }
[PartitionKey]
public int LanguageID { get; set; }
It's possible ? I don't want override ReadEntity/WriteEntity.
Since your class is already based on TableEntity, you might want to try to override/replace the row key and partition key property of TableEntity using the 'new' keyword instead.
public class User : TableEntity
{
[IgnoreProperty]
public Guid ID { get; set; }
[IgnoreProperty]
public int LanguageID { get; set; }
public new string PartitionKey { get { return ID.ToString(); } set { ID = Guid.Parse(value); } }
public new string RowKey { get { return LanguageID.ToString(); } set { LanguageID = int.Parse(value); } }
}
I am not a big fan of 'new' modifier. In my opinion it is non-OOP approach.
I will suggest following
public class ConsumerApplicationEntity : TableEntity
{
public ConsumerApplicationEntity(string applicationKey, string applicationSecret)
: base(applicationKey, applicationSecret)
{
}
[IgnoreProperty]
public string ApplicationKey
{
get
{
return this.PartitionKey;
}
set
{
this.PartitionKey = value;
}
}
[IgnoreProperty]
public string ApplicationSecret
{
get
{
return this.RowKey;
}
set
{
this.RowKey = value;
}
}
}

How can I set the RowKey to a new value with a sequence

I have the following class:
public class Note : TableServiceEntity
{
public Note( )
{
}
public Note(string pK)
{
PartitionKey = pK,
RowKey = Seq.GetSequence().ToString();
}
public string Description { get; set; }
}
What I need is to set the RowKey to the value generated by the sequence. Can anyone explain how to do this with a constructor? What I get is a syntax error: Virtual member call in constructor.
public static class Seq
{
static int index;
public static int GetSequence()
{
return index++;
}
}
public class Note : TableServiceEntity
{
public Note(string partitionKey, string rowKey)
: base(partitionKey, Seq.GetSequence().ToString()) { }
}

how to confige an abstract class with structure map

is there any problem with this kinda registration via structure map??
static public class ContainerBootstrapper
{
static public void BootstrapDefaultContainer(bool test = false)
{
StructureMap.ObjectFactory.Initialize(x =>
{
x.Scan(p =>
{
p.AssemblyContainingType<IPropertyType>();
p.AddAllTypesOf<IPropertyType>();
// p.AddAllTypesOf<IPropertyType>().NameBy(c => c.Name);
});
});
}
public interface IPropertyType : IIdentityObject, IPriority
{
string PropertyName { get; set; }
ObjectType ObjectType { get; }
string DisplayName { get; set; }
IEntityType EntityType { get; set; }
IList<IPropertyRuleObject> RuleObjects { get; set; }
void AddRuleObject(IPropertyRuleObject ruleObject);
}
public abstract class PropertyTypeBase : PersistentObject, IPropertyType
{
public PropertyTypeBase()
{
}
public PropertyTypeBase(string propertyName, string displayName)
{
PropertyName = propertyName;
DisplayName = displayName;
}
....
}
public class StringType : PropertyTypeBase
{
private ObjectType _objectType;
public StringType()
{
_objectType = new ObjectType(typeof(string));
}
public StringType(string propertyName, string displayName)
: base()
{
PropertyName = propertyName;
DisplayName = displayName;
}
public override ObjectType ObjectType
{
get { return _objectType; }
}
}
when ContainerBootstrapper.BootstrapDefaultContainer(); execute I see this line of error:
StructureMap Exception Code: 200
Could not find an Instance named "StringType" for PluginType Azarakhsh.Domain.Core.AdaptiveObjectModel.Interface.IPropertyType
the calling code:
public IPropertyType GetPropertyType(IIdentityObject identityObject, string name)
{
string[] Properties = name.Split('.');
object Result = identityObject;
foreach (var Property in Properties)
Result = Result.GetType().GetProperty(Property).PropertyType.Name;
IPropertyType propertyType = StructureMap.ObjectFactory.GetNamedInstance<IPropertyType> (Result + "Type");
if (propertyType==null)
throw new Exception("Property type not found");
return propertyType;
}
what is the problem?
You are trying to get a named instance, but from what I can see of the code you have provided, you dont name your instances. The line of code that name your instances is commented out.
But even if you would just use the ObjectFactory.GetInstance<IPropertyType>(); here, you would have got an error because structuremap dont know what constructor to use. There are several solutions to theis problem.
Change your design so you only have one constructor
Mark your default constructor with the [DefaultConstructor] attribute, then it will work.
You can register it with objectFactory manually with something like this:
x.For().Use().Ctor("propertyName").Is("someValue").Ctor("displayName").Is("someValue");
You can write a custom registrationconvention as described here

Resources