How to implement the Strategy Pattern? - c#

I have two simiiliar methods for parsing lines.
first method
public IList<LasLine> GetLasLines(string section)
{
var lasLines = new List<LasLine>();
bool startParse = false; // Секція знайдена і почати парсити її
foreach(var line in _lines)
{
if(GetSectionName(line).Equals(section) && !startParse)
{
startParse = true;
}
else
{
if(IsCommentLine(line)) continue;
if(IsBeginSection(line)) break;
LasLine lasLine;
if(!TryParseLasLine(line, out lasLine)) continue;
lasLines.Add(lasLine);
}
}
return lasLines;
}
And second
public IList<AsciiLogDataLine> GetAsciiLogData()
{
var asciiLogData = new List<AsciiLogDataLine>();
bool startParse = false;
foreach(var line in _lines)
{
if(GetSectionName(line).Equals(LasSectionName.ASCIISection) && !startParse)
{
startParse = true;
}
else
{
if(IsCommentLine(line)) continue;
AsciiLogDataLine asciiLogDataLine;
if(!TryParseAsciiLogDataLine(line, out asciiLogDataLine)) continue; asciiLogData.Add(asciiLogDataLine);
}
}
return asciiLogData;
}
Is it possible to implement this pattern?

As i think, you can create common Contract (Interface) for this Line Classes, and use for example Generic Method.
IEnumerable<IDataLine> GetLines<T>(Func<object> parseFunction)
{
...
var dataLine = parseFunction(line);
if (dataLine == null)
{
continue;
}
...
}
Or you can create IParser and inject it into the method
IEnumerable<IDataLine> GetLines<T>(IParser parser)
{
...
if (!parser.CanParse(line))
{
continue;
}
var dataLine = parser.Parse(line);
...
}
Last sample more similar to the Strategy Pattern.

Related

How to create custom validation based on xml in .net 6

I am trying to perform validation using custom class attributes. I have to keep my model validation rules inside XML files. Custom Class attribute specifies the path to the XML file and custom attributes will contain the logic to read XML files and get the validation rules for that class. When I will call Model.Isvalidate() that time Validation will get executed. I already have this working in .Net 4.5 MVC application. The same thing I am trying in .Net 6 its not working.
public class CustomDynamicModelValidatorProvider : DataAnnotationsModelValidatorProvider,IModelValidatorProvider
{
protected override IEnumerable<ModelValidator> GetValidators(ModelMetadata metadata, IEnumerable<ModelValidatorProvider> validatorProviders, IEnumerable<Attribute> attributes)
{
string resourceKeyPath = string.Empty;
IList<Attribute> newAttributes = new List<Attribute>(attributes);
CustomDynamicValidatorsAttribute resourceMapperAttr = attributes.FirstOrDefault(a => a is CustomDynamicValidatorsAttribute) as CustomDynamicValidatorsAttribute;
if (resourceMapperAttr == null)
{
System.Reflection.MemberInfo classInfo = metadata.ContainerType;
if (classInfo != null)
{
var resourceMapperRootAttr = classInfo.GetCustomAttributes(typeof(ResourceMappingRootAttribute), false).FirstOrDefault() as ResourceMappingRootAttribute;
if (resourceMapperRootAttr != null)
{
resourceKeyPath = resourceMapperRootAttr.Path + "." + metadata.PropertyName;
}
}
}
else
{
resourceKeyPath = resourceMapperAttr.ResourceKeyPath;
}
if (!string.IsNullOrEmpty(resourceKeyPath))
{
string[] validators = ResourceManager.GetValidators(resourceKeyPath).Replace(" ", "").Split(',');
var maxLength = ResourceManager.GetMaxLength(resourceKeyPath);
if (!string.IsNullOrEmpty(maxLength))
{
var required = new MaxLengthAttribute(maxLength.ToInteger());
required.ErrorMessage = string.Format(CustomAttributeHelper.GetResourceText("Shared.Messages.MaximumLengthExceeded"), maxLength);
newAttributes.Add(required);
}
for (int i = 0; i < validators.Length; i++)
{
if (string. Equals(validators[i], "required", StringComparison.OrdinalIgnoreCase))
{
var required = new CustomRequiredAttribute();
newAttributes.Add(required);
}
else if (validators[i].StartsWith("email", StringComparison.OrdinalIgnoreCase))
{
var email = new CustomEmailAttribute();
newAttributes.Add(email);
}
}
}
return base.GetValidators(metadata,validatorProviders);
}
public static IDictionary<string, object> GetValidatorAttributes(string validators)
{
IDictionary<string, object> attributes = new Dictionary<string, object>();
string[] validatorList = !string.IsNullOrEmpty(validators) ? validators.Replace(" ", "").Split(',') : new string[] { };
foreach (var item in validatorList)
{
if (!attributes.Keys.Contains("data-val"))
{
attributes.Add("data-val", "true");
}
if (string.Equals(item, "required", StringComparison.OrdinalIgnoreCase))
{
attributes.Add("data-val-required", CustomAttributeHelper.GetResourceText("Shared.Messages.Mandatory"));
}
else if (item.StartsWith("email", StringComparison.OrdinalIgnoreCase))
{
attributes.Add("data-val-email", CustomAttributeHelper.GetResourceText("Shared.Messages.vEmailField"));
}
else if (item.StartsWith("phone", StringComparison.OrdinalIgnoreCase))
{
attributes.Add("data-val-phone", CustomAttributeHelper.GetResourceText("Shared.Messages.vPhoneField"));
}
else if (item.StartsWith("zipcode", StringComparison.OrdinalIgnoreCase))
{
attributes.Add("data-val-zipcode", CustomAttributeHelper.GetResourceText("Shared.Messages.vZipCodeField"));
}
else if (item.StartsWith("mark", StringComparison.OrdinalIgnoreCase))
{
string min = string.Empty;
string max = string.Empty;
string rangeValidatorMessage = string.Empty;
if (item.Contains("-"))
{
var rangeArray = item.Split('-');
if (rangeArray.Length > 2)
{
min = rangeArray[1];
max = rangeArray[2];
rangeValidatorMessage = CustomAttributeHelper.GetResourceText("Shared.Messages.NumberRange");
}
else
{
max = rangeArray[1];
rangeValidatorMessage = CustomAttributeHelper.GetResourceText("Shared.Messages.vNumericLimited");
}
}
else
{
max = "10000";
}
if (!string.IsNullOrWhiteSpace(min))
{
attributes.Add("data-val-range-min", min);
}
attributes.Add("data-val-range-max", max);
attributes.Add("data-val-range", rangeValidatorMessage);
}
else if (item.StartsWith("number", StringComparison.OrdinalIgnoreCase))
{
attributes.Add("data-val-number", CustomAttributeHelper.GetResourceText("Shared.Messages.NumberRange"));
}
else if (item.StartsWith("number", StringComparison.OrdinalIgnoreCase))
{
attributes.Add("data-val-decimal", string.Format(CustomAttributeHelper.GetResourceText("Shared.Messages.DecimalRange"), CustomAttributeHelper.GetResourceText("Shared.Limits.Decimal")));
}
else if (item.StartsWith("file-", StringComparison.OrdinalIgnoreCase))
{
attributes.Add("data-val-filetype", item.Split('-')[1]);
}
}
return attributes;
}
public void CreateValidators(ModelValidatorProviderContext context)
{
}
}

C# Mongo Query efficiency

I have a FilterDefinition build that will look for an address based on the properties that are not empty.
public static FilterDefinition<TU> FindPointByAddress<TU>(Address address)
{
var filterBuilder = Builders<TU>.Filter;
var filterItems = new List<FilterDefinition<TU>>();
if (!String.IsNullOrWhiteSpace(address.Street))
{
filterItems.Add(filterBuilder.Eq("Address.Street", address.Street));
}
if (!String.IsNullOrWhiteSpace(address.City))
{
filterItems.Add(filterBuilder.Eq("Address.City", address.City));
}
if (!String.IsNullOrWhiteSpace(address.StateProvince))
{
filterItems.Add(filterBuilder.Eq("Address.StateProvince", address.StateProvince));
}
if (!String.IsNullOrWhiteSpace(address.PostCode))
{
filterItems.Add(filterBuilder.Eq("Address.PostCode", address.PostCode));
}
return filterBuilder.And(filterItems);
}
IMO this query feels dirty, is there a better way to build this type of query or is this the correct way?
A few days ago I had a similar situation. I wrote a simple method that takes a field name and field value as a string.
public void AddEqualCompareFilter(string fieldName, string fieldValue)
{
if (String.IsNullOrEmpty(fieldValue) == false) {
if (Filter != null) {
Filter = Filter & Builders<TranslationsDocument>.Filter.Eq(fieldName, fieldValue);
}
else {
FilterCount++;
Filter = Builders<TranslationsDocument>.Filter.Eq(fieldName, fieldValue);
}
}
}
I am then using this snippet to decide based on FilterCount:
if (FilterCount > 0) {
Result = collection.Find(Filter).ToListAsync().GetAwaiter().GetResult();
return true;
}
else {
Result = collection.Find(new BsonDocument()).ToListAsync().GetAwaiter().GetResult();
return true;
}

C# Sorted list without LINQ and SORT

My question is that is it possible to create a list that sorts the objects in it upon these object being placed in them?
After not getting anywhere, I made a new linked list. The only task is to make this list ordered by the string field of the objects it will containt while remaining foreachable.
I have the following code:
class LancoltLista<T> : IEnumerable
{
class ListaElem
{
public T tartalom;
public ListaElem kovetkezo;
}
ListaElem fej;
public void ElejereBeszuras(T elem)
{
ListaElem uj = new ListaElem();
uj.tartalom = elem;
uj.kovetkezo = fej;
fej = uj;
}
public void VegereBeszuras(T elem)
{
if (fej == null)
{
ElejereBeszuras(elem);
}
else
{
ListaElem e = fej;
while (e.kovetkezo != null)
{
e = e.kovetkezo;
}
ListaElem uj = new ListaElem();
uj.tartalom = elem;
e.kovetkezo = uj;
}
}
public IEnumerator GetEnumerator()
{
return new ListaBejaro(fej);
}
class ListaBejaro : IEnumerator<T>
{
ListaElem elso, jelenlegi;
public ListaBejaro(ListaElem elso)
{
this.elso = elso;
jelenlegi = null;
}
public bool MoveNext()
{
if (jelenlegi == null)
{
jelenlegi = elso;
}
else
{
jelenlegi = jelenlegi.kovetkezo;
}
return jelenlegi != null;
}
public void Reset()
{
jelenlegi = null;
}
object IEnumerator.Current
{
get { return this.jelenlegi.tartalom; }
}
public T Current
{
get { return this.jelenlegi.tartalom; }
}
public void Dispose()
{
elso = null;
jelenlegi = null;
}
}
}
The problem here is that I'm not able to compare p.kulcs and kulcs.
For real world applications you could use the built-in SortedList<T>.
For your homework, you will have to check every item that you get in your add method against the entire list and insert it into the correct place: between the last element that it's grater than or equal to, and the first element that it's smaller then.
Of course, if the list is empty, or if there is no element greater than the one you add, then you simply append the element to the last available location.
Since this is homework, I'll leave you to write the code yourself.

Find objects having a specific value in a specific property in Autocad drawing

I have objects in Autocad drawing with property named Base. I am trying to find all objects in that drawing with Base property has a specific string value such as "Pipe".
I can iterate objects in the drawing and get all object ids. Then I get all properties of object with that Id and check if property named Base = "Pipe".
Iteration performance is not good enough. Is there any way to directly get object ids that has property named Base = "Pipe"?
Here is how I iterate through all objects:
List<ObjectId> ObjectIds = new List<ObjectId>();
foreach (Document Document in Documents)
{
Database Database = Document.Database;
using (Transaction Transaction = Database.TransactionManager.StartTransaction())
{
for (long i = Database.BlockTableId.Handle.Value; i < Database.Handseed.Value; i++)
{
ObjectId Id;
if (Database.TryGetObjectId(new Handle(i), out Id))
{
ObjectIds.Add(Id);
}
}
Transaction.Commit();
}
}
And here is how I get all properties of the objects in my ObjectIds collection.
public static DataLinksManager DataLinks
{
get
{
if (null == _DataLinks)
{
StringCollection Coll = Autodesk.ProcessPower.DataLinks.DataLinksManager.GetLinkManagerNames();
if (Coll.Count > 0)
{
if (Coll[0] != string.Empty)
{
_DataLinks = Autodesk.ProcessPower.DataLinks.DataLinksManager.GetManager(Coll[0]);
}
}
}
return _DataLinks;
}
}
private static DataLinksManager _DataLinks;
foreach(var Id in ObjectIds)
{
List<KeyValuePair<string, string>> Properties = DataLinks.GetAllProperties(Id, true);
// I check existence of my property and if so its value.
}
In case anyone needs, here is the code that is solution to my problem. The trick is the Iterate method. This is based on this article by Philippe Leefsma. What I add to this method is a list of ObjectClass properties of found ObjectId instances. My sample dawing has around 8500 ObjectIds. However what I'm interested in is objects with base classes acppasset and acppdynamicasset and count of such objects is 90.
using Autodesk.AutoCAD.ApplicationServices;
using AcadApp = Autodesk.AutoCAD.ApplicationServices.Application;
public static void GetObjects()
{
List<KeyValuePair<string, ObjectId>> ObjectIds = new List<KeyValuePair<string, ObjectId>>();
List<string> Filter = new List<string>() { "acppasset", "acppdynamicasset" };
foreach (Document Document in this.Documents)
{
ObjectIdCollection Ids = this.Iterate(Document, Filter);
if (null != Ids) foreach (var Id in Ids.OfType<ObjectId>()) ObjectIds.Add(new KeyValuePair<string, ObjectId>(System.IO.Path.GetFileNameWithoutExtension(Document.Name), Id));
}
this.Results = new Dictionary<string, List<List<KeyValuePair<string, string>>>>();
foreach (var Id in ObjectIds)
{
try
{
var Properties = this.GetObject(Id.Value);
if (null == Properties) continue;
var Base = Properties.Where(x => x.Key == "Base").FirstOrDefault();
if (string.IsNullOrWhiteSpace(Base.Value)) continue;
if (!this.Results.ContainsKey(Base.Value)) this.Results.Add(Base.Value, new List<List<KeyValuePair<string, string>>>());
this.Results[Base.Value].Add(Properties);
} catch { }
}
}
public ObservableCollection<Document> Documents { get; set; }
public ObjectIdCollection Iterate(Document Document, List<string> Filter = null)
{
ads_name Instance = new ads_name();
Database Database = Document.Database;
ObjectIdCollection ValidIds = new ObjectIdCollection();
// Get the last handle in the Database
Handle Handseed = Database.Handseed;
// Copy the handseed total into an efficient raw datatype
long HandseedTotal = Handseed.Value;
for (long i = 1; i < HandseedTotal; ++i)
{
string Handle = Convert.ToString(i, 16);
int Result = acdbHandEnt(Handle, ref Instance);
if (Result != 5100) continue; // RTNORM
ObjectId Id = new ObjectId(Instance.a);
if (!Id.IsValid) continue;
try
{
if (null != Filter)
{
if (!Filter.Contains(Id.ObjectClass.Name.ToLower())) continue;
}
using (DBObject DBObject = Id.Open(OpenMode.ForRead, false))
{
ValidIds.Add(Id);
DBObject.Dispose();
}
} catch { }
}
return ValidIds;
}
public List<KeyValuePair<string, string>> GetObject(ObjectId Id)
{
if (Command.DataLinks != null) try { return Command.DataLinks.GetAllProperties(Id, true); } catch { return null; }
return null;
}
public static DataLinksManager DataLinks
{
get
{
if (null == _DataLinks)
{
StringCollection Coll = Autodesk.ProcessPower.DataLinks.DataLinksManager.GetLinkManagerNames();
if (Coll.Count > 0)
{
if (Coll[0] != string.Empty)
{
_DataLinks = Autodesk.ProcessPower.DataLinks.DataLinksManager.GetManager(Coll[0]);
}
}
}
return _DataLinks;
}
}
private static DataLinksManager _DataLinks;
public Dictionary<string, List<List<KeyValuePair<string, string>>>> Results { get; set; }
The slow performance here is because it attempts to read all the objects and check if it contains any attribute. As far as I know, the attributes exist only for block references(inserts). So if selection filters are used, we could get direct access to only those records based on the filter criteria.
I found a pretty easy example here using selection filter that selects all blocks with a particular name.
Copying a part of that code for reference. This selects only the block references. You can iterate from here.
TypedValue[] filterlist = new TypedValue[1];
filterlist[0] = new TypedValue(0, "INSERT");
SelectionFilter filter = new SelectionFilter(filterlist);
PromptSelectionResult selRes = ed.SelectAll(filter);
if (selRes.Value.Count != 0)
{
SelectionSet set = selRes.Value;
foreach (ObjectId id in set.GetObjectIds())
{
BlockReference oEnt = (BlockReference)tr.GetObject(id, OpenMode.ForWrite);
//do something with oEnt..;
}
}
If you can add complexities to your filter, you will need to iterate only a very small set.

How does a MultiDataTrigger know when a Condition in its ConditionCollection is updated?

I'm trying to make a behavior had a ConditionCollection similar to MultiDataTrigger. I've looked through the reflected code, but I can't seem to follow how the MultiDataTrigger knows when to go through it's logic to determine if the conditions are met.
Here's the code:
[ContentProperty("Setters")]
public sealed class MultiDataTrigger : TriggerBase, IAddChild
{
// Fields
private ConditionCollection _conditions = new ConditionCollection();
private SetterBaseCollection _setters;
// Methods
internal override bool GetCurrentState(DependencyObject container, UncommonField<HybridDictionary[]> dataField)
{
bool flag = base.TriggerConditions.Length > 0;
for (int i = 0; flag && (i < base.TriggerConditions.Length); i++)
{
flag = base.TriggerConditions[i].ConvertAndMatch(StyleHelper.GetDataTriggerValue(dataField, container, base.TriggerConditions[i].Binding));
}
return flag;
}
internal override void Seal()
{
if (!base.IsSealed)
{
base.ProcessSettersCollection(this._setters);
if (this._conditions.Count > 0)
{
this._conditions.Seal(ValueLookupType.DataTrigger);
}
base.TriggerConditions = new TriggerCondition[this._conditions.Count];
for (int i = 0; i < base.TriggerConditions.Length; i++)
{
if ((this._conditions[i].SourceName != null) && (this._conditions[i].SourceName.Length > 0))
{
throw new InvalidOperationException(SR.Get("SourceNameNotSupportedForDataTriggers"));
}
base.TriggerConditions[i] = new TriggerCondition(this._conditions[i].Binding, LogicalOp.Equals, this._conditions[i].Value);
}
for (int j = 0; j < this.PropertyValues.Count; j++)
{
PropertyValue value2 = this.PropertyValues[j];
value2.Conditions = base.TriggerConditions;
switch (value2.ValueType)
{
case PropertyValueType.Trigger:
value2.ValueType = PropertyValueType.DataTrigger;
break;
case PropertyValueType.PropertyTriggerResource:
value2.ValueType = PropertyValueType.DataTriggerResource;
break;
default:
throw new InvalidOperationException(SR.Get("UnexpectedValueTypeForDataTrigger", new object[] { value2.ValueType }));
}
this.PropertyValues[j] = value2;
}
base.Seal();
}
}
void IAddChild.AddChild(object value)
{
base.VerifyAccess();
this.Setters.Add(Trigger.CheckChildIsSetter(value));
}
void IAddChild.AddText(string text)
{
base.VerifyAccess();
XamlSerializerUtil.ThrowIfNonWhiteSpaceInAddText(text, this);
}
// Properties
[DesignerSerializationVisibility(DesignerSerializationVisibility.Content)]
public ConditionCollection Conditions
{
get
{
base.VerifyAccess();
return this._conditions;
}
}
[DesignerSerializationVisibility(DesignerSerializationVisibility.Content)]
public SetterBaseCollection Setters
{
get
{
base.VerifyAccess();
if (this._setters == null)
{
this._setters = new SetterBaseCollection();
}
return this._setters;
}
}
}
Basically, it doesn't. There is an internal StyleHelper class, which determines when to reevaluate the triggers. It does this based on the various bindings in the triggers.
Specifically, you can look at the StyleHelper. EvaluateOldNewStates method that takes a MultiDataTrigger.

Categories

Resources