I have some collections in a mongo DB: autocomplete.brands and autocomplete.makes
Each of these collections have items based on the same pattern that have a Name property:
public class AutocompleteEntity
{
[BsonId]
[BsonRepresentation(BsonType.ObjectId)]
public string Id { get; set; } = ObjectId.GenerateNewId().ToString();
[BsonElement("Name")]
public string Name { get; set; }
[BsonElement("Culture")]
public string Culture { get; set; }
[BsonElement("Key")]
public string Key { get; set; }
}
I would like to use generics for searching in each of these collections, here is what I have.
The high-level service using repositories:
public class AutocompleteHandler : IAutocompleteHandler
{
private readonly IAutocompleteRepository<Brands> _brandsRepository;
private readonly IAutocompleteRepository<Makes> _makesRepository;
public AutocompleteHandler(
IAutocompleteRepository<Brands> brandsRepository,
IAutocompleteRepository<Makes> makesRepository
)
{
_brandsRepository = brandsRepository;
_makesRepository = makesRepository;
}
public async Task<ICollection<string>> SearchForBrand(string name, string culture)
{
return await _brandsRepository.Search(name, culture);
}
public async Task<ICollection<string>> SearchForMake(string name, string culture)
{
return await _makesRepository.Search(name, culture);
}
}
For this to work I defined a class for each repository type:
public interface IAutocompleteCollection
{
}
public class Brands : IAutocompleteCollection
{
}
public class Makes : IAutocompleteCollection
{
}
and the generic repository:
public class AutocompleteRepository<T> : IAutocompleteRepository<T> where T : IAutocompleteCollection
{
private readonly IMongoCollection<AutocompleteEntity> _collection;
public AutocompleteRepository(IMongoTenantResolver mongoResolver)
{
var name = typeof(T).Name;
var collectionName = $"autocomplete.{char.ToLowerInvariant(name[0]) + name.Substring(1)}";
_collection = mongoResolver
.GetMongoDbInstance()
.GetCollection<AutocompleteEntity>(collectionName);
}
public async Task<List<string>> Search(string name, string culture)
{
var filter = Builders<AutocompleteEntity>.Filter.Regex("Name", new BsonRegularExpression(name, "i"));
filter &= Builders<AutocompleteEntity>.Filter.Eq("Culture", culture);
var data = await _collection.FindAsync(filter);
var items = await data.ToListAsync();
return items.Select(i => i.Name).ToList();
}
}
My question
I am using the empty classes Brands and Makes in order to be able to resolve the collection by name in the generic repository:
var name = typeof(T).Name;
var collectionName = $"autocomplete.{char.ToLowerInvariant(name[0]) + name.Substring(1)}";
Is there a more elegant way to use generics without relying on empty classes/interface or is that the best way to use generics in my case?
Update1 :
After #RobertHarvey 's answer, I am adding this information. Generics usage is useful to me because I am using Dependency Injection and thus I can have this one-liner in Startup.cs:
services.AddTransient(typeof(IAutocompleteRepository<>), typeof(AutocompleteRepository<>));
I can then add many other autocomplete types without the need to register them individually in the Startup. Also because of DI usage, I am not sure how I can inject an enum value into the constructor.
public enum CollectionName
{
Brands,
Makes
}
// Constructor
public AutoCompleteRepository(IMongoTenantResolver resolver, CollectionName name)
{
// Name of collection is name.ToString(). Preserves static type checking,
// avoids empty classes and interfaces.
}
Related
I have the following structure:
public class LogicStatement : ILogicStatement
{
public string TestLogic { get; set; }
public string CompareLogic { get; set; }
public string Operator { get; set; }
public string Expression();
public bool Value();
}
public class Test : ITest
{
public int TestId { get; set; }
public int LiteralId { get; set; }
public string TestName { get; set; }
public string TestText { get; set; }
public string TestDisplayName { get; }
**public ILogicStatement LogicStatement { get; set; }**
public string Expression { get; set; }
public bool Value { get; set; }
}
public class Literal : ILiteral
{
some property members...
**public List<ITest> Tests {get; set;}**
some method members...
}
Note that the class Test has a member of type LogicStatement, and the class Literal has a member of type List.
Note also that all classes have properties and methods that share the same name: Expression, Value, Expression(), Value().
The value of Expression and Value (properties and methods) depend on values in the LogicStatement class.
Throughout the whole project, I use the Interface Type for to instantiate each object to adhere with Dependency Inversion. To support this, I use a factory-like design to create new instances of Test and LogicStatement.
Example:
public static class Factory
{
public static ILogicStatement CreateLogicStatement()
{
return new LogicStatement();
}
public static ITest CreateTest()
{
return new Test(CreateLogicStatement());
}
public static List<ITest> CreateTests()
{
return new List<ITest>();
}
//repeat the same for evey other class.
}
My goal is to have Expression() and Value() be calculated only once in the bottom level class (LogicStatement), and somehow get transfered to their counterpart properties in the higher level classes.
I'm getting the data from Dapper and it looks like all the nested objects are returned from the Dapper module correctly with the same nested structure I intended, and with the right values for all of their members. All of them but Expression, Expression(), Value, Value() are null.
my constructors look like this:
public LogicStatement()
{
Expression();
Value();
}
public Test(ILogicStatement logicStatement)
{
_logicStatement = logicStatement;
Expression = _logicStatement.Expression();
Value = _logicStatement.Value();
}
public Literal(ITest test)
{
_test = test;
Expression = _test.Expression;
Value = _test.Value;
}
and my main:
List<ILiteral> literals = Factory.CreateLiterals();
List<ITest> tests = Facotry.CreateTests();
List<ILogicStatement> logicStatements = Factory.CreateLogicStatements();
literals = GetDataFromDapper();
This last line seems to assign correct values to all other members on all hierarchies. But I cannot get Expression and Value to be anything other than null.
If I test LogicStatement.Expression() and LogicStatement.Value() standalone, they do return the expexted values. but starting at the first parent class Test, these properties are all null.
I think I'm doing something wrong in the way i'm instantiating my objects. Primarily because I'm not sure i understand basic best practices to write constructors.
Maybe I the desired behavior should be implemented through events, where the Test and Literal classes subscribe to changes in the Expression() and Value() methods (or rather to what calculates them). But I never used events and I'd like to know if this fundamentally can be acheived without them first.
My question: How do I make the Expression() Value() at the bottom level class "Fire up" whenever LogicStatement is instantiated, and then have the Expression and Value properties be assigned accordingly as a result.
In other words, I want the following to always be true:
test[i].Expression == literal[i].Expression == LogicStatement[i].Expression()
I'm a beginner in OOP. So any fundamental explanation is welcome.
As you are new to object oriented programming I would start with the basics and leave factories and adhering with Dependency Inversion and the interfaces away for later.
You could tell Dapper to split joined tables into multiple entities (see https://www.learndapper.com/relationships), but for learning OOP I would start doing everything manually.
Your class design does not look proper to me yet. Not sure what Expression and Value of the LogicStatement are, but if they are calculations based on the other properties, I would implement them as (just to show off with complicated words) lazy initialized cached getter properties that are invalidated in the setters of the relevant properties. That ensures you only calculate them once for as many reads you like but recalculate them on first read after one or multiple properties have been updated.
public class LogicStatement {
private string _testLogic;
private string _compareLogic;
private string _operator;
private string? _expression;
private bool? _value;
public LogicStatement(string testLogic, string compareLogic, string #operator) {
_testLogic = testLogic;
_compareLogic = compareLogic;
_operator = #operator;
}
public string TestLogic {
get {
return _testLogic;
}
set {
_testLogic = value;
InvalidateCachedValues();
}
}
public string CompareLogic {
get {
return _compareLogic;
}
set {
_compareLogic = value;
InvalidateCachedValues();
}
}
public string Operator {
get {
return _operator;
}
set {
_operator = value;
InvalidateCachedValues();
}
}
public string Expression {
get {
string? result = _expression;
if (result is null) {
_expression = result = BuildExpression();
}
return result;
}
}
public bool Value {
get {
bool? result = _value;
if (result is null) {
_value = result = EvaluateValue();
}
return result.Value;
}
}
private void InvalidateCachedValues() {
_expression = null;
_value = null;
}
private string BuildExpression() {
//Your logic goes here
throw new NotImplementedException();
}
private bool EvaluateValue() {
//Your logic goes here
throw new NotImplementedException();
}
}
Sorry, it got a bit bigger with the full properties.
In the other classes I would not copy the Value and the Expression but simply remove these properties as anybody can easily access them through the LogicStatement property:
public class Test {
public Test(int testId, int literalId, string testName, string testText, string testDisplayName, LogicStatement logicStatement) {
TestId = testId;
LiteralId = literalId;
TestText = testText;
TestDisplayName = testDisplayName;
LogicStatement = logicStatement;
}
public int TestId { get; }
public int LiteralId { get; }
public string TestName { get; }
public string TestText { get; }
public string TestDisplayName { get; }
public LogicStatement LogicStatement { get; }
}
and the Literal could look like this (I got a bit confused whether this class has one Test or a list of them, I stick to your constructor + properties that hint in the direction of a single one):
public class Literal {
private Test _test;
public Literal(string property1, int property2, Test test) {
Property1 = property1;
Property2 = property2;
_test = test;
}
public string Property1 { get; }
public int Property2 { get; }
public string Expression => _test.LogicStatement.Expression;
public bool Value => _test.LogicStatement.Value;
}
As you decided not to expose the Test in the Literal it makes sense to provide Expression and Value, otherwise they could also be removed (or kept for convenience).
I'd like to create a Web Api method that will accept JSON and a string with they name of the Type.
So far I have something like this:
public void Write(string typeName, string jsonData)
{
var myType = Type.GetType(typeName);
var fromJsonString = JsonConvert.DeserializeObject<OutgoingEnvelope<myType>>(jsonData);
}
OutgoingEnvelope would be defined as this:
public class OutgoingEnvelope<T>
{
public string TypeId { get; set; }
public OutgoingEnvelope()
{
Documents = new List<T>();
}
public List<T> Documents { get; set; }
}
Currently I'm getting the message:
'myType' is a variable but is used like a type.
Our ultimate goal is to be able to get JSON data and turn it into the appropriate class dynamically.
When you use a generic in that way the type must be known at compile time. It would be similar if you tried to create a new instance of myType using var instance = new myType();, this would also not compile for the same reason (more or less).
The deserializer on JsonConvert (see DeserializeObject) offers a non generic version that you could then cast later if you wanted to. This has some requirements though.
You have to define an interface for your container and optionally the generic type parameter
The generic interface parameter must be marked as covariant (out keyword)
This is a self contained example that executes, the only thing missing is getting the Type from a string instead of hard coded.
public class SomeController {
public void Write()
{
var objectToSerialize = new OutgoingEnvelope<SomeDocument>()
{
Documents = new List<SomeDocument>() {new SomeDocument() {Name = "Hi"}},
TypeId = "Some type"
};
var json = JsonConvert.SerializeObject(objectToSerialize);
// var myType = Type.GetType(typeName);
var myType = typeof(OutgoingEnvelope<SomeDocument>);
var fromJsonString = JsonConvert.DeserializeObject(json, myType) as IOutgoingEnvelope<IDocumentType>;
if(fromJsonString == null)
throw new NullReferenceException();
}
}
public interface IDocumentType
{
string Name { get; set; }
// known common members in the interface
}
public class SomeDocument : IDocumentType
{
public string Name { get; set; }
}
public interface IOutgoingEnvelope<T> where T : IDocumentType
{
string TypeId { get; set; }
IEnumerable<T> Documents { get; }
}
public class OutgoingEnvelope<T> : IOutgoingEnvelope<T> where T : IDocumentType
{
public string TypeId { get; set; }
public OutgoingEnvelope()
{
Documents = new List<T>();
}
public IEnumerable<T> Documents { get; set; }
}
try something like this:
var myType = Type.GetType(typeName);
var template = typeof(OutgoingEnvelope<>);
var typeToSet = template.MakeGenericType(myType);
var fromJsonString = JsonConvert.DeserializeObject<OutgoingEnvelope<typeToSet>>(jsonData);
it should work.
I have the following issue
Here is third party class which we are used(so i cannot change it)
public class ThirdPartyEmployee
{
public string F_Name { get; set; }
public string L_Name { get; set; }
public DateTime Date_of_birth { get; set; }
public string Telephone1 { get; set; }
public string Telephone2 { get; set; }
public string Position { get; set; }
//..... and so on
}
Also we have our own smaller and better Employee class
public class Employee
{
public string FirstName { get; set; }
public string LastName { get; set; }
public string MobileTelephone { get; set; }
}
Sometimes we need to convert third party class to our own. There is extension method for it
public static class ThirdPartyExtensions
{
public static Employee ConvertTo(this ThirdPartyEmployee thirdPartyEmployee)
{
var result = new Employee();
result.FirstName = thirdPartyEmployee.F_Name;
result.LastName = thirdPartyEmployee.L_Name;
result.MobileTelephone = thirdPartyEmployee.Telephone1;
return result;
}
}
Now about the issue. If somebody consider to add some other properties to Employee class he\she can forget to change ConvertTo method. How we can avoid it ? Ideally i would like to have some compilation errors ...
Any suggestions ?
If your Employee class is just a container, there's one simple approach:
public class Employee
{
private readonly string firstName;
public Employee(string firstName)
{
this.firstName = firstName;
}
}
Now your conversion method has no choice but to pass all the arguments, so you get a compiler error when the conversion method isn't updated.
Of course, this still isn't foolproof - if you also care about changing the arguments, this doesn't help much.
And now that we have Roslyn, with great integration in Visual Studio, you can actually make your own compiler errors using a Roslyn analyzer. If you're not afraid of getting your hands dirty, this would be a great opportunity to show how useful something like that can be. Sadly, it's not very easy to use right now, and needs "the right kind of thinking" to be wielded well. It will allow you to make rules like "a class conversion extension method must assign all properties in the resulting class", for example.
You can not create a compilation error with standard means. There may be Visual Studio plugins that allow you to do that.
But it may not be necessary: You could change the CopyTo method so that instead of hardcoding all the properties to be copied, it uses reflection to obtain a list of all public properties to copy.
Example code to start with:
FieldInfo[] myObjectFields = type.GetFields(BindingFlags.Public | BindingFlags.Instance);
foreach (FieldInfo fi in myObjectFields)
{
i.SetValue(destination, fi.GetValue(source));
}
To handle different property names: You could introduce an attribute which allows you to specify which property of ThirdPartyEmployee translates to which property of Employee. This can also be evaluated using reflection.
Example:
public class Employee
{
[CopyFromThirdPartyEmployee("F_Name")]
public string FirstName { get; set; }
[CopyFromThirdPartyEmployee("L_Name")]
public string LastName { get; set; }
[CopyFromThirdPartyEmployee("Telephone1")]
public string MobileTelephone { get; set; }
}
You could have the CopyTo method throw an exception when it finds a public property which does not have the required mapping attribute. That way you could be sure that every property also has the attribute - but that would be a runtime error, not a compile time error.
Another approach be to simply make Employee a wrapper for ThirdPartyEmployee:
public class Employee
{
private ThirdPartyEmployee _baseEmployee;
public Employee() { _baseEmployee = new ThirdPartyEmployee(); }
public Employee(ThirdPartyEmployee e) { _baseEmployee = e; }
public string FirstName
{
get { return _baseEmployee.F_Name; }
set { _baseEmployee.F_Name = value; }
}
...
}
That way you'd notice that if you can't access a property you haven't implemented it. The downside is that every employee would then be based on a ThirdPartyEmployee.
You can do this with the help of reflection, but dictionary for names mapping is needed:
public static class ThirdPartyExtensions
{
static Dictionary<string, string> map;
static ThirdPartyExtensions()
{
map = new Dictionary<string, string>{ {"F_Name", "FirstName"} /*and others*/};
}
public static Employee ConvertTo(this ThirdPartyEmployee thirdPartyEmployee)
{
var result = new Employee();
if(map.Count < typeof(Employee).GetProperties().Count())
throw new Exception("Forget to add mapping for new field!");
foreach(var prop in typeof(ThirdPartyEmployee).GetProperties())
if(map.ContainsKey(prop.Name))
{
var temp = typeof(Employee).GetProperty(map[prop.Name]);
temp.SetValue(result, prop.GetValue(thirdPartyEmployee));
}
return result;
}
}
Using Roslyn analyzers it's possible to produce compile-time (and IntelliSense) errors that go beyond the scope of the compiler. Here's a very simple implementation of an analyzer that checks that all the properties of a type returned by a method have been assigned. It doesn't take into account control flow (e.g. if).
[DiagnosticAnalyzer(LanguageNames.CSharp)]
public class AssignAllPropertiesAnalyzer : DiagnosticAnalyzer
{
private static readonly DiagnosticDescriptor Rule = new DiagnosticDescriptor("AssignAllPropertiesAnalyzer",
"All properties must be assigned.", "All properties of the return type must be assigned.", "Correctness",
DiagnosticSeverity.Warning, isEnabledByDefault: true);
public override ImmutableArray<DiagnosticDescriptor> SupportedDiagnostics => ImmutableArray.Create(Rule);
public override void Initialize(AnalysisContext context)
{
context.RegisterSyntaxNodeAction(AnalyzeMethod, SyntaxKind.MethodDeclaration);
}
private static void AnalyzeMethod(SyntaxNodeAnalysisContext context)
{
var methodNode = (MethodDeclarationSyntax)context.Node;
var methodSymbol = context.SemanticModel.GetDeclaredSymbol(methodNode);
if (methodSymbol.GetReturnTypeAttributes().Any(x => x.AttributeClass.Name == "AssignAllPropertiesAttribute"))
{
var properties = methodSymbol.ReturnType.GetMembers().OfType<IPropertySymbol>().Where(x => !x.IsReadOnly).ToList();
foreach (var assignmentNode in methodNode.DescendantNodes().OfType<AssignmentExpressionSyntax>())
{
var propertySymbol = context.SemanticModel.GetSymbolInfo(assignmentNode.Left).Symbol as IPropertySymbol;
if (propertySymbol != null)
{
properties.Remove(propertySymbol);
}
}
if (properties.Count > 0)
{
var diagnostic = Diagnostic.Create(Rule, methodSymbol.Locations[0]);
context.ReportDiagnostic(diagnostic);
}
}
}
The analyzer assumes an attribute named AssignAllProperties is applied to return type of a method. In the following example, ~~~~~~ marks the location where analyzer would produce a diagnostic.
class A
{
public string S { get; set; }
}
[return: AssignAllProperties]
public static A Create()
~~~~~~
{
return new A();
}
An analyzer can be installed both as a VSIX and as a NuGet package. I would recommend always using the NuGet approach - it would apply the analyzer for everyone consuming the code and would allow you to change the severity (e.g. to error), thus failing the compilation. To get started with building an analyzer library, install the Roslyn SDK and create an Analyzer with Code Fix C# project.
You cannot generate a compile error for that, but... I would move the conversion method to the Employee class. I suggest to avoid Extension methods that are dependend on data (like properties from other classes)
public class Employee
{
public string FirstName { get; set; }
public string LastName { get; set; }
public string MobileTelephone { get; set; }
public static Employee From(ThirdPartyEmployee employee)
{
var result = new Employee();
result.FirstName = thirdPartyEmployee.F_Name;
result.LastName = thirdPartyEmployee.L_Name;
result.MobileTelephone = thirdPartyEmployee.Telephone1;
return result;
}
}
This way you keep all functionality in the right class/file and it is clear for others if they add properties.
I'd like to create a generic method to get glass casted items of T.
What I have so far is:
private static List<T> GetChildren<T>(Item parentItem) where T: class {
var lstChildItems = parentItem.Children.Where(child => child.TemplateID.Equals(T.TemplateId)).ToList();
List<T> lstChildren = lstChildItems.Select(c => c.GlassCast<T>()).ToList();
return lstChildren;
}
In my example T.TemplateId can't be resolved because T is only marked as class. Does TemplateId exist in some kind of interface or what do I have to enter instead of class?
If you want to get the TypeConfiguration:
var ctx = new SitecoreContext();
var typeConfig = ctx.GlassContext.TypeConfigurations[typeof(T)];
var templateId = (config as SitecoreTypeConfiguration).TemplateId;
//ofc check for nulls, but you get the point
But I personally like to utilize the InferType possibilities:
public interface ISitecoreItem
{
[SitecoreChildren(InferType = true)]
IEnumerable<ISitecoreItem> Children { get; set; }
}
[SitecoreType]
public class News : ISitecoreItem
{
public string Title { get; set; }
public virtual IEnumerable<ISitecoreItem> Children { get; set; }
}
private static IEnumerable<T> GetChildren<T>(this Item parentItem) where T : ISitecoreItem
{
var parentModel = item.GlassCast<ISitecoreItem>();
return parentModel.Children.OfType<T>();
}
//usage:
var newsItems = parentItem.GetChildren<News>();
The InferType option will give you the most specific available Type that Glass can find. So anything deriving from ISitecoreItem can be fetched like this.
I have this form where there's a Postal Code field, in my ViewModel it looks something like this:
[RegularExpression(#"^\d{5}(-\d{4})?$")]
public string PostalCode { get; set; }
That regular expression accepts 5 digits postal codes, but now I need to support other countries where they use 8, 4 or 6 digits postal codes.
I have those custom regex in a database, but I can't pass non-static variables to an attribute in this way:
[RegularExpression(MyCustomRegex)]
public string PostalCode { get; set; }
What can I do? I tried creating a custom attribute, but in some point I needed to pass a non-static parameter, which is not possible.
Should I use reflection? Is there a cleaner way?
A better way may be to decouple the attribute from the regex.
public class PostalCodeAttribute : Attribute
{
public string Country { get; set; }
}
public interface IPostalCodeModel
{
string PostalCode { get; }
}
public class UsModel : IPostalCodeModel
{
[PostalCode(Country = "en-US")]
public string PostalCode { get; set; }
}
public class GbModel : IPostalCodeModel
{
[PostalCode(Country = "en-GB")]
public string PostalCode { get; set; }
}
Validator:
public class PostalCodeValidator
{
private readonly IRegularExpressionService _regularExpressionService;
public PostalCodeValidator(IRegularExpressionService regularExpressionService)
{
_regularExpressionService = regularExpressionService;
}
public bool IsValid(IPostalCodeModel model)
{
var postalCodeProperty = model.GetType().GetProperty("PostalCode");
var attribute = postalCodeProperty.GetCustomAttribute(typeof(PostalCodeAttribute)) as PostalCodeAttribute;
// Model doesn't implement PostalCodeAttribute
if(attribute == null) return true;
return ValidatePostalCode(_regularExpressionService, model, attribute.Country);
}
private static bool ValidatePostalCode(
IRegularExpressionService regularExpressionService,
IPostalCodeModel model,
string country
)
{
var regex = regularExpressionService.GetPostalCodeRegex(country);
return Regex.IsMatch(model.PostalCode, regex);
}
}
As indicated in several related questions (e.g. Pass instance of Class as parameter to Attribute constructor Lambda expression in attribute constructor) only compile time literals are allowed as arguments for an attribute.
I did think of a workaround that may or may not work. The idea is to create a custom attribute class that derives from the regular expression attribute and that performs a regex lookup on construction and passes the result to its base.
DISCLAIMER: I haven't actually tested it (and am not planning on doing so ;-).
[AttributeUsage(AttributeTargets.Field | AttributeTargets.Property, AllowMultiple = false)]
public class PostalCodeAttribute : RegularExpressionAttribute
{
private static ConcurrentDictionary<string, Func<string, string>> _resolverDict = new ConcurrentDictionary<string, Func<string, string>>();
private static string Resolve(string source)
{
Func<string, string> resolver = null;
if (!_resolverDict.TryGetValue(source, out resolver))
throw new InvalidOperationException(string.Format("No resolver for {0}", source));
return resolver(source);
}
public static void RegisterResolver(string source, Func<string, string> resolver)
{
_resolverDict.AddOrUpdate(source, resolver, (s, c) => resolver);
}
static PostalCodeAttribute()
{
// necessary to enable client side validation
DataAnnotationsModelValidatorProvider.RegisterAdapter(typeof(PostalCodeAttribute), typeof(RegularExpressionAttributeAdapter));
}
public PostalCodeAttribute(string patternSource)
: base(Resolve(patternSource))
{
}
}
/// ...
public void SomeIntializer()
{
PostalCodeAttribute.RegisterResolver("db_source", (s) => PostalCodeRegularExpressions.LookupFromDatabase());
}
public class SomeClassWithDataValidation
{
[PostalCode("db_source")]
public string PostalCode { get; set; }
}
Note that this will only work, if registration of a matching resolver function is done before any of these attributes are instantiated.