I have created a struct which have some properties such as
public struct DeviceDetailModel
{
public static readonly DeviceDetailModel DT851P = new DeviceDetailModel("851P","v1","v2");
public static readonly DeviceDetailModel DT852P = new DeviceDetailModel("852P","v3","v4");
public static readonly DeviceDetailModel DT83P = new DeviceDetailModel("853P","v5","v6");
public static readonly DeviceDetailModel DT854P = new DeviceDetailModel("854P");
public string DeviceName { get; private set; }
public string Value1 { get; private set; }
public string Value2 { get; private set; }
private DeviceDetailModel(string deviceName,string value1,string value2)
{
DeviceName = deviceName;
Value1 = value1;
Value2 = value2;
}
}
now if i want to get a detail of single item its easy i just had to do DeviceDetailModel.DT854P
but the issue I would be getting a value on runtime using which I had to identify which struct property I had to return
eg = my runtime value is 853P
I want to loop over my struct to identify where in DeviceName matched to this value 853P and which should return DeviceDetailModel.DT83P
I was able to loop over the properties of the struct but wasn't able to get value
Editing: Based on my run time value, i need to iterate over DeviceName's value and if the value matches it should return associated property
Here's one fairly simple option:
public struct DeviceDetailModel
{
private static readonly Dictionary<string, DeviceDetailModel> models = new Dictionary<string, DeviceDetailModel>
{
{"851P", new DeviceDetailModel("851P")},
{"852P", new DeviceDetailModel("852P")},
{"853P", new DeviceDetailModel("853P")},
{"854P", new DeviceDetailModel("854P")},
};
public static DeviceDetailModel DT851P get => models["851P"];
public static DeviceDetailModel DT852P get => models["852P"];
public static DeviceDetailModel DT83P get => models["853P"];
public static DeviceDetailModel DT854P get => models["854P"];
private DeviceDetailModel(string deviceName)
{
DeviceName = deviceName;
}
public string DeviceName {get;private set;}
public DeviceDetailModel? FindByDeviceName(string deviceName)
{
return models.TryGetValue(deviceName, out var value) ? value : (DeviceDetailModel)null;
}
}
Note that the return value of FindByDeviceName is a Nullable<DeviceDetailModel> so in case you're looking for a string that's not found you wont get an exception, but null.
Related
Is there a way I can construct the following class in such a way that I can code the setting of Service and Values only once:
public class MyClass
{
private readonly IService Service;
public List<int> Values { get; set; }
public int Value { get; set; }
public MyClass(IService service, List<int> values)
{
Service = service;
Values = values;
Value = Values.FirstOrDefault(i => i == Service.GetDefaultValue());
}
public MyClass(IService service, List<int> values, int value)
{
Service = service;
Values = values;
Value = Values.FirstOrDefault(i => i == value);
}
}
You can use constructor chaining:
public class MyClass
{
public MyClass(IService service, IEnumerable<int> values)
: this(service, values, valus.FirstOrDefault(i => i == service.GetDefaultValue()) {}
public MyClass(IService service, IEnumerable<int> values, int value)
{
Service = service;
Values = values;
Value = value;
}
}
Note that I've specified the values argument as an IEnumerable instead of a List. This allows the ctor to accept more types than only lists. If your member is a List type, you'll have to call the ToList() method in your constructor.
However, I'd also advise you to specify the Values property as an IEnumerable instead of as a List.
Edit: after the code in the question has been changed, another advise could be to use a private constructor and (overloaded) static factory methods. Doing so indicates (imho) that the creation of such an instance is a more 'expensive' operation instead of calling a simple constructor, as you're calling a method on a 'DataService', which suggests that you might go to the DB to initialize your object ?
public class NewExpenseViewModel
{
private readonly IDataService DataService;
public ExpenseType ExpenseType { get; set; }
CollectionViewSource VatRatesSource { get; set; }
public ICollectionView VatRatesView => VatRatesSource.View;
private NewExpenseViewModel(ServiceProvider serviceProvider, ExpenseType expenseType, VatRate vatRate)
{
DataService = serviceProvider.GetService<IDataService>();
ExpenseType = expenseType;
VatRatesSource = new CollectionViewSource() { Source = DataService.GetVatRates() };
VatRate = vatRate;
}
public static NewExpenseViewModel Create(ServiceProvider sp, ExpenseType expenseType, VatRate vat)
{
DataService = serviceProvider.GetService<IDataService>();
ExpenseType = expenseType;
VatRatesSource = new CollectionViewSource() { Source = DataService.GetVatRates() };
VatRate = vatRate;
}
public static NewExpenseViewModel Create(ServiceProvider sp, ExpenseType expenseType)
{
var instance = Create(sp, expenseType, 0);
instance.Vat =
((IEnumerable<VatRate>)VatRatesSource.Source).FirstOrDefault(v => v.VatRateID.Equals(ExpenseType.SuggestedVatRateID));
return instance;
}
}
The solution I arrived at following Frederik's strategy, after realising I only needed an int as the third parameter:
public NewExpenseViewModel(ServiceProvider serviceProvider, ExpenseType expenseType, int vatRateID)
{
DataService = serviceProvider.GetService<IDataService>();
ExpenseType = expenseType;
VatRatesSource = new CollectionViewSource() { Source = DataService.GetVatRates() };
VatRate = VatRates.FirstOrDefault(v => v.VatRateID.Equals(vatRateID));
}
public NewExpenseViewModel(ServiceProvider serviceProvider, ExpenseType expenseType)
: this(serviceProvider, expenseType, expenseType.SuggestedVatRateID) { }
I have this class that contains a static list
public class TypeList
{
public string Name;
public string NameTag;
public TypeList(string Name, string NameTag)
{
this.Name = Name;
this.NameTag = NameTag;
}
public static List<TypeList> DataType = new List<TypeList>() {
new TypeList("DataType","-1"),
new TypeList("OpOne","1"),
new TypeList("OpTwo","2"),
};
}
I then put the static list called DataType into a combobox:
public void RefreshList()
{
List<TypeList> data = new List<TypeList>();
data = TypeList.DataType;
typeCB.DataSource = data;
typeCB.DisplayMember = "Name";
typeCB.ValueMember = "NameTag";
typeCB.SelectedValue = -1;
typeCB.SelectedText = "Select DataType";
}
However, when I run it, all I get are the classnames in my combobox. Is something wrong with my code? I tried to do
data.Select(x=>x.Name).ToList()
But that just gives me the name portion.
I might be wrong, but based on the Documentation and Example it might be that this Feature only works with public property getters, not public fields:
Gets or sets the property to display for this ListControl.
public class USState
{
private string myShortName;
private string myLongName;
public USState(string strLongName, string strShortName)
{
this.myShortName = strShortName;
this.myLongName = strLongName;
}
public string ShortName
{
get
{
return myShortName;
}
}
public string LongName
{
get
{
return myLongName;
}
}
}
Of course I would also advise against making the list a part of the Type class. A simple Programm scope static would be better. If that is the case and as autoproties have have become a thing by now, this should be enough of a fix:
public class Type
{
public string Name { private set; get } ;
public string NameTag {private set; get };
public TypeList(string Name, string NameTag)
{
this.Name = Name;
this.NameTag = NameTag;
}
}
//use in the class of main, the form or some similar central point
static List<Type> TypeList = new List<Type>();
I created a Unit class that implements the typesafe enum pattern. I implemented an implicit operator in it to simplify its usage. But I want to refactor the implicit operator from string to Unit. Currently, I'm using a switch block but this will get huge pretty quick once I add more units. My current code looks like this.
[DataContract]
public class Unit
{
public static readonly Unit USFeet = new Unit("US Feet", 1);
public static readonly Unit Meters = new Unit("Meters", 0.3048006096);
[DataMember] public double ConversionConstant { get; private set; }
[DataMember] private string Name { get; set; }
private Unit(string name, double conversionConstant)
{
Name = name;
ConversionConstant = conversionConstant;
}
public override string ToString()
{
return Name;
}
public static implicit operator string(Unit unit)
{
return unit.Name;
}
public static implicit operator Unit(string name)
{
switch (name)
{
case "US Feet":
return USFeet;
case "Meters":
return Meters;
default:
return null;
}
}
}
So my question is, is there a better way to approach this instead of using a switch block?
I tried something like this but it doesn't work...
public static SortedList<string, Unit> UnitList = new SortedList<string, Unit>();
private Unit(string name, double conversionConstant)
{
Name = name;
ConversionConstant = conversionConstant;
UnitList.Add(name, this);
}
public static implicit operator Unit(string name)
{
return UnitList[name];
}
You can build a lookup table, and update it from the .ctor :
private static Dictionary<string, Unit> definedUnits = new Dictionary<string, UserQuery.Unit>();
private Unit(string name, double conversionConstant)
{
Name = name;
ConversionConstant = conversionConstant;
definedUnits.Add(name, this);
}
public static implicit operator Unit(string name)
{
Unit result;
return definedUnits.TryGetValue(name, out result) ? result : null;
}
You can also build that table dynamically with reflection :
private static Dictionary<string, Unit> definedUnits = typeof(Unit)
.GetFields(BindingFlags.Public | BindingFlags.Static)
.Where(x => x.IsInitOnly && x.FieldType == typeof(Unit))
.ToDictionary(x => x.Name, x => (Unit)x.GetValue(null));
It seems that what I tried above will work when done like this (I believe it's called lazy initialization)
[DataContract]
public class Unit
{
public static readonly Unit USFeet = new Unit("US Feet", 1);
public static readonly Unit Meters = new Unit("Meters", 0.3048006096);
private static Dictionary<string, Unit> _unitList;
public static readonly Dictionary<string, Unit> UnitList = _unitList ?? (_unitList = new Dictionary<string, Unit>());
[DataMember] private readonly double _conversionConstant;
[DataMember] private readonly string _name;
private Unit(string name, double conversionConstant)
{
_name = name;
_conversionConstant = conversionConstant;
UnitList.Add(name, this);
}
}
The following code shows how I am trying to serialize/deserialze a List using Protobuf-Net. Method getNewItem() returns an object of ItemsStore, which is added to the List.
// Create an empty list
private ItemsStoreList text;
// Add some elements to the list
lock (text.SyncRoot)
{
text.AddItem(getNewItem());
text.AddItem(getNewItem());
text.AddItem(getNewItem());
}
// Serialize
var file = File.Create("testfile.bin");
Serializer.Serialize<ItemsStoreList>(file, text);
// Deserialize
ItemsStoreList textNew = Serializer.Deserialize<ItemsStoreList>(file);
After I run this code, the list contained in object textNew is always empty. Object textNew is instantiated from class ItemsStoreList, which is shown below:
[ProtoContract]
public class ItemsStoreList
{
[ProtoMember(1)]
private List<ItemsStore> m_listData;
private readonly object m_SyncRoot = new object();
public ItemsStoreList()
{
m_listData = new List<ItemsStore>();
}
public void AddItem(ItemsStore item)
{
m_listData.Add(item);
}
public object SyncRoot
{
get { return this.m_SyncRoot; }
}
public int Count
{
get { return m_listData.Count; }
}
public ItemsStore getItem(int idx)
{
return (ItemsStore)m_listData[idx];
}
public void Clear()
{
m_listData.Clear();
}
}
[ProtoContract]
public class ItemsStore
{
[ProtoMember(1)]
public myStruct m_Text;
[ProtoMember(2)]
public ulong m_Time;
public ItemsStore(myStruct newText, ulong newTime)
{
m_Text = newText;
m_Time = newTime;
}
public myStruct Text
{
get { return m_Text; }
}
public ulong Time
{
get { return m_Time; }
}
}
[ProtoContract]
public struct myStruct
{
[ProtoMember(1)]
public uint var1;
[ProtoMember(2)]
public byte var2;
[ProtoMember(3)]
public byte[] var3;
[ProtoMember(4)]
public string var4;
}
The first thing I note is that you have not rewound the stream; adding this (between serialize and deserialize) changes the behaviour:
file.Position = 0;
We now get an exception relating to the ItemsStore constructor, so we can instruct protobuf-net to ignore that constructor completely:
[ProtoContract(SkipConstructor = true)]
public class ItemsStore
Now we get 3 items back:
System.Console.WriteLine(textNew.Count);
which outputs:
3
Further probing shows that it is most likely fetching the other data too:
for (int i = 0; i < textNew.Count; i++)
{
var item = textNew.getItem(i);
System.Console.WriteLine(item.m_Text.var1);
System.Console.WriteLine(item.m_Time);
}
I will, however, add the obligatory warning about mutable structs and public fields.
Within code I want to do something like this:
item.Stage = Stage.Values.ONE;
Where Stage.Values.ONE represents some predefined Stage:
public class Stage
{
[Key]
public virtual int StageId { get; set; }
public string Name { get; set; }
public TimeSpan Span { get; set; }
}
I'm dealing with EF CodeFirst... and I have a lot of stages to define. I'm not sure if I should store the data in the database, or in the dbContext, or what, but I'm looking for the simplest implementation.
I've tried this:
I've tried the following (defining two constants):
public class Stage
{
[Key]
public virtual int StageId { get; set; }
public string Name { get; set; }
public TimeSpan Span { get; set; }
public static class Values
{
public static readonly Stage ONE = new Stage()
{
StageId = 0,
Name = "ONE",
Span = new TimeSpan(0, 0, 0)
};
public static readonly Stage TWO = new Stage()
{
StageId = 1,
Name = "TWO",
Span = new TimeSpan(0, 0, 10)
};
}
But whenever I create a new instance of an entity that has a Stage, a new Stage is added to the db. I just need a few constant stages.
Use of Stage:
public class Side
{
public Side()
{
Stage = Stage.Values.ONE; // Adds new Stage to DB, when it should be a reference to the one I defined above
}
public virtual Stage Stage { get; set; }
}
It looks a bit like an enum, and I've used a kind of 'extended enum' patter several times before with some success. Because you're refencing these values in code, it may not make sense to store them in the database as well, but it's possible if needed.
The technique is described in detail here: http://lostechies.com/jimmybogard/2008/08/12/enumeration-classes/
Basically, you create a base class which provides a number of services similar to an enum, and then to create your "enumerated class" you inherit from it and provide a bunch of static instances which call the constructor with however many properties you need to have.
To avoid link rot, here is the base class to use (just put the whole class into your project somewhere), and scroll down for your own code.
public abstract class Enumeration : IComparable
{
private readonly int _value;
private readonly string _displayName;
protected Enumeration()
{
}
protected Enumeration(int value, string displayName)
{
_value = value;
_displayName = displayName;
}
public int Value
{
get { return _value; }
}
public string DisplayName
{
get { return _displayName; }
}
public override string ToString()
{
return DisplayName;
}
public static IEnumerable<T> GetAll<T>() where T : Enumeration, new()
{
var type = typeof(T);
var fields = type.GetFields(BindingFlags.Public | BindingFlags.Static | BindingFlags.DeclaredOnly);
foreach (var info in fields)
{
var instance = new T();
var locatedValue = info.GetValue(instance) as T;
if (locatedValue != null)
{
yield return locatedValue;
}
}
}
public override bool Equals(object obj)
{
var otherValue = obj as Enumeration;
if (otherValue == null)
{
return false;
}
var typeMatches = GetType().Equals(obj.GetType());
var valueMatches = _value.Equals(otherValue.Value);
return typeMatches && valueMatches;
}
public override int GetHashCode()
{
return _value.GetHashCode();
}
public static int AbsoluteDifference(Enumeration firstValue, Enumeration secondValue)
{
var absoluteDifference = Math.Abs(firstValue.Value - secondValue.Value);
return absoluteDifference;
}
public static T FromValue<T>(int value) where T : Enumeration, new()
{
var matchingItem = parse<T, int>(value, "value", item => item.Value == value);
return matchingItem;
}
public static T FromDisplayName<T>(string displayName) where T : Enumeration, new()
{
var matchingItem = parse<T, string>(displayName, "display name", item => item.DisplayName == displayName);
return matchingItem;
}
private static T parse<T, K>(K value, string description, Func<T, bool> predicate) where T : Enumeration, new()
{
var matchingItem = GetAll<T>().FirstOrDefault(predicate);
if (matchingItem == null)
{
var message = string.Format("'{0}' is not a valid {1} in {2}", value, description, typeof(T));
throw new ApplicationException(message);
}
return matchingItem;
}
public int CompareTo(object other)
{
return Value.CompareTo(((Enumeration)other).Value);
}
}
And now your code will look something like this:
public class Stage : Enumeration
{
public TimeSpan TimeSpan { get; private set; }
public static readonly Stage One
= new Stage (1, "Stage one", new TimeSpan(5));
public static readonly Stage Two
= new Stage (2, "Stage two", new TimeSpan(10));
public static readonly Stage Three
= new Stage (3, "Stage three", new TimeSpan(15));
private EmployeeType() { }
private EmployeeType(int value, string displayName, TimeSpan span) : base(value, displayName)
{
TimeSpan = span;
}
}
Once you have that set up, you can just store the .Value in the database. I'm afraid I haven't done it in EF, but in nHibernate it's reasonably straight-forward to tell a property to just store the ".Value" of the property, and you can wire it back up when you load the value by having it call:
Stage.FromValue<Stage>(intValue);
Hold the Stage as a property of your entity, use it the way you're doing and add
Ignore(x => x.Stage)
to your mapping. This will ignore this property when mapping to your database.
Edit: I misinterpreted the question.
If you want just the different stages in your database, you should put the stages in their own table with an ID, and refer to that ID trough a relationship. Every entity will hold an additional reference and you'll have to define relationships for them.
Is this what you were looking for?