I receive data from a DB. There will be only one model at a time which I call currentModel from now on. The type of model received must be determined at runtime. Therefore, I implemented an Interface for the models IDataModel. Each possible model implements IDataModel. There is a variable which holds the currentModel. In order to make it generic I defined it as IDataModel. Here is the code so far.
public async Task<List<T>> LoadData<T, U>(string sql, U parameters, string connectionString)
{
using (IDbConnection connection = new SqlConnection(connectionString))
{
var rows = await connection.QueryAsync<T>(sql, parameters);
return rows.ToList();
}
}
private IDataModel currentModel;
private List<IDataModel> queryResult;
if (someCondition)
{
currentModel = newSpecificModel();
sql = someQuery;
}
else ....
queryResult = await _data.LoadData<IDataModel, dynamic>(sql, new {}, config.GetConnectionString("default"));
if (queryResult.Count == 1)
{
currentModel= queryResult.First();
}
else
{
// Do error handling here if more than one row is returned from the query
}
Now when I try to access some property I get an error: IDataModel does not contain a definition for someProperty
currentModel.someProperty
How do I determine the current implementation at runtime?
I mean I could create a variable for each possible model. But is there a more elegant way?
EDIT
Nailed the problem down to that this wont work:
List<Interface> = List<Implementation>
See this post.
Whats an approach to solve this?
Have a look at the curiously recurring template pattern; it is useful for creating child objects with the right type.
Maybe also look at this discussion.
Related
I looked at https://stackoverflow.com/a/5174773/787958 and the method resolution is not working as expected in my instance.
I have JSON coming into a method that I want to create a typed object from and then validate it.
var type = Type.GetType($"{Namespace.Models}.{typeName.FirstCharToUpper()}");
var model = JsonConvert.DeserializeObject(json, type);
var validator = new Validator();
var isValid = await validator.ValidateAsync(model);
The Validator class looks as such:
public class Validator : IValidator
{
public Task<Boolean> ValidateAsync(Object model) { return Task.FromResult(true); }
public Task<Boolean> ValidateAsync(User user)
{
if (user.Id == null || user.Id == Guid.Empty)
return Task.FromResult(false);
if (String.IsNullOrWhiteSpace(user.Name))
return Task.FromResult(false);
return Task.FromResult(true);
}
}
Even though at runtime, type is User, it does not call the ValidateAsync(User user) method, instead it calls ValidateAsync(Object model) method.
But, if the type is not dynamic at compile time and instead I do:
var model = JsonConvert.DeserializeObject<User>(json);
It properly calls the ValidateAsync(User user) method.
Is there a proper way to have a dynamic type at compile time, but known at runtime, and have it call the expected "more specific" method?
UPDATE
Having any "future coder" add to the switch statement was not too much of an ask imo, since they would be in that class adding their new ValidateAsync(NewModel model) method anyways. Utilizing dynamic was more along the lines of how I would like the code to be as it would not bloat the code with a possibly larger and larger switch statement. But, I was worried about performance impact. So I ran a Postman runner 250 times with the switch statement (just User or default) and 250 times with dynamic. The differences in performance was negligible.
Average response time with switch: 2.668ms
Average response time with dynamic: 2.816ms
So, I'm going to go with the dynamic solution. Thanks!
Overload resolution happens at the compile time, not in the runtime. model created by var model = JsonConvert.DeserializeObject(json, type); will be of object type so compiler will expectedly select ValidateAsync(Object model) overload.
You may try using dynamic for late bound (runtime) resolution:
dynamic model = JsonConvert.DeserializeObject(json, type);
var validator = new Validator();
var isValid = await validator.ValidateAsync(model);
But note that it comes with some performance cost.
Another option is type testing and casting. For example with pattern matching can look like this:
var isValid = model switch
{
User user => await validator.ValidateAsync(user),
_ => await validator.ValidateAsync(model)
}
The type of method resolution you're thinking of happens at compile time.
If you want to resolve the method at run time you have to write the code for it yourself, e.g.
public Task<Boolean> ValidateAsync(Object model)
{
return (model is User) ? ValidateAsync((User)model) : Task.FromResult(true);
}
[INTRO]
I know there are about a zillion QA about generics and reflections everywhere, but it's becoming a blackhole to me, and I'm only getting more lost the more I read!!
What i need to do is simple, and I'm amazed that it hasn't been addressed before.
[SAMPLE] Consider the following snippit:
public async Task<string> generateJsonSchema(string model)
{
try
{
string modelName = "Models." + model;
Type t = Type.GetType(modelName, false);
JsonSchema4 schema = await JsonSchema4.FromTypeAsync<t>();
return schema.ToJson();
}
catch (Exception ex)
{
Logger.WriteToLogFile(ex.ToString(), "exception");
return "";
}
}
[PROBLEM] Now the main problem is that variable t is evaluated at runtime, thus, JsonSchema4.FromTypeAsync<t>() throws the error 't' is a variable but is used like a type when trying to build compile time
Whoever used JsonSchema4 will understand what I'm trying to achieve here.
Instead of creating a generate function for each of my models, or make a switch/if-else logic,
[QUESTION]
How to make it receive the model name as a string parameter, and convert the string-model-name to model-type and pass it to jSonSchema4 method.
The problem here is that, as you say, t is evaluated as runtime.
I also ran into this Problem and solved it by creating a MethodInfo of the method I wanted to invoke, in your case JsonSchema4.FromTypeAsync<t>().
So basically this is want may fix the problem:
var methodInfo = typeof(JsonSchema4).GetMethod("FromTypeAsync", new Type[] { }); //Get the "normal method info", the overload without parameters
var methodInfoWithType = methodInfo.MakeGenericMethod(t); //now you have a method with your desired parameter t as TypeParameter
Task<JsonSchema4> task = methodInfoWithType.Invoke(null, null) as Task<JsonSchema4>; //now you can cast the result from invoke as Task to keep the Async-Await functionality
var schema = await task;
return schema.ToJson();
I have this method in C# that looks like I should really refactor it . Should I use a design pattern ? Too much repetition is what I see NOW and especially as MORE conditional if statements get added
Change to a method?
public void CreateOrUpdateReportDefinition(ReportGroupSubReport reportGroupSubReport, bool isNew, int report)
{
if (report == 1)
{
var entity = _envyUnitOfWork.ReportDefinitions.GetById(reportGroupSubReport.Id) ?? new ReportDefinition();
if (isNew)
entity.SetNew();
_envyUnitOfWork.ReportDefinitions.InsertOrUpdate(entity, true);
}
else if (report == 2)
{
var entity = _envyUnitOfWork.TraxReports.GetById(reportGroupSubReport.Id) ?? new TraxReport();
if (isNew)
entity.SetNew();
_envyUnitOfWork.TraxReports.InsertOrUpdate(entity, true);
}
Mapper.Map(reportGroupSubReport, entity);
_envyUnitOfWork.Commit();
}
So what I would do is to put every single conditional behavior into separate method. To avoid repetition you could use the template method pattern. You should also declare your report variable before all if statements so that it would be accessible for the Mapper.Map().
Edit:
Ok, so assuming that both _envyUnitOfWork.TraxReports and _envyUnitOfWork.ReportDefinitions share some common generic interface (which I named Repository in code) defining GetById and InsertOrUpdate methods, you do not need to use any design patterns - simple generic method will do the job. Here is example of the code:
private void createOrUpdateReportDefinition<Report>(ReportGroupSubReport reportGroupSubReport, bool isNew, Repository<Report> repository/*, Action<Report> insertOrUpdate*/) where Report : new()
{
var entity = repository.GetById(reportGroupSubReport.Id) ?? new Report();
if (isNew)
entity.SetNew();
repository.InsertOrUpdate(entity, true);//insertOrUpdate(entity, true);
Mapper.Map(reportGroupSubReport, entity);
_envyUnitOfWork.Commit();
}
public void CreateOrUpdateReportDefinition(ReportGroupSubReport reportGroupSubReport, bool isNew, int report)
{
if (report == 1)
{
createOrUpdateReportDefinition(reportGroupSubReport, isNew, _envyUnitOfWork.ReportDefinitions/*, _envyUnitOfWork.ReportDefinitions.InsertOrUpdate*/);
}
else if (report == 2)
{
createOrUpdateReportDefinition(reportGroupSubReport, isNew, _envyUnitOfWork.TraxReports/*, _envyUnitOfWork.TraxReports.InsertOrUpdate*/);
}
}
Please take under consideration this code is dealing only with one issue in your code which was the code duplication, there are still few things you could improve like removing int report parameter from the method either by dividing it into few methods or at least by replacing it with some enum with meaningful name. I would also suggest the same (dividing the methods) for the bool isNew parameter, since using it means your function doesn't do one thing only which is against single responsibility principle from SOLID - you can read more about it here.
The following refactoring pattern might give you some clues as to how you could manage the proliferating conditional problem: https://sourcemaking.com/refactoring/replace-conditional-with-polymorphism
Edit: The easiest way to do this would be defining an Interface that both TraxReports and ReportDefinition implement that has a InsertOrUpdate method.
I have created a PhoneBook style application; on my phonebook object I have a local member _site which is used as a filter, since there are approximately 1000 phone numbers, split across 12 sites within my organisation. Only one site will be retrieved at a time using this method.
This was my original method. The GUI has several methods for reordering the data, so I left it as an IQueryable because I would like to defer SQL to allow for filtering to be done on the SQL server rather than on the client PC.
Works
public IQueryable<PhoneNumber> GetPhoneDirectory()
{
PhoneBookDataContext db = new PhoneBookDataContext())
return db.PhoneNumbers.Where(d => d.Site == _site);
}
However, I am also trying to keep to 'best practise' in terms of using statements.
Doesn't Work
public IQueryable<PhoneNumber> GetPhoneDirectory()
{
using (PhoneBookDataContext db = new PhoneBookDataContext())
{
return db.PhoneNumbers.Where(d => d.Site == _site);
}
}
Now as pointed out by #justanotheruseryoumay, this will cause an exception because the datacontext is disposed by the time the objects are accessed.
I guess what I am asking is, how can I make sure my data context is disposed nicely, when I cannot use a 'using' statement and don't strictly know when the context is done with.
If you want to return IQueryable you can make your class that contains the GetPhoneDirectory disposable, make the PhoneBookDataContext a field, and dispose it in your dispose method.
You will then put the onus on the caller to dispose his instance of your class.
E.g.
public class MyClass : IDisposable
{
PhoneBookDataContext db;
public MyClass()
{
db = new PhoneBookDataContext();
}
public IQueryable<PhoneNumber> GetPhoneDirectory()
{
return db.PhoneNumbers.Where(d => d.Site == _site);
}
public void Dispose()
{
if (db != null)
{
db.Dispose();
db = null;
}
}
}
// Caller
using(var myClass = new MyClass())
{
var queryable = myClass.GetPhoneDirectory();
...
}
The execution of the query will still be deferred and the PhoneBookDataContext will still be properly Disposed because using is interpreted by the compile as a try/finally. When you actually execute the query it will result in a runtime error because the PhoneBookDataContext no longer exists. I would suggest doing a .ToList() on your query and returning it that way. If you want to change the order after you return it then you can still do LINQ on it as you please.
EDIT:
Another thing you could do is to create the using with the PhoneBookDataContext in the calling method and pass in the context. The context is really going to be used in that method anyway and you can keep it around as long as you need it and stick with the good using format.
Yes; It is bad design because your IQueryable<PhoneNumber> will be evaluated only when you call a method that cause it to be evaluated, like ToList() or when you iterate it with foreach.
in your code you are returning a IQueryable<PhoneNumber> which is not evaluated yet, and before the caller get any chance to execute it, it's internals that has the responsibility for yielding the records to you (db); is already disposed.
Just as a suggestion:
public IEnumerable<PhoneNumber> GetPhoneDirectory()
{
using (PhoneBookDataContext db = new PhoneBookDataContext())
{
return db.PhoneNumbers.Where(d => d.Site == _site).ToList();
}
}
Or relocate the db object to somewhere else in your design (Unit Of Work and Repository are nice patterns to get a look at IMHO).
i have a problem with db4o and I wanna know is it feature or bug.
Let's see some code
private interface IInterface {}
private class SimpleObject : IInterface
{}
[TestMethod, Ignore]
public void _()
{
var replicableServer = Db4oFactory.OpenServer(Path.GetFullPath(#"testdb"), 777);
try
{
replicableServer.GrantAccess("user", "user");
var client2 = Db4oFactory.OpenClient("127.0.0.1", 777, "user", "user");
var client1 = Db4oFactory.OpenClient("127.0.0.1", 777, "user", "user");
client1.Store(new SimpleObject());
client1.Commit();
var query = client2.Query();
query.Constrain(typeof(IInterface));
Assert.AreEqual(1, query.Execute().Count);
}
finally
{
replicableServer.Close();
}
}
Here we have failed assert. But if we change type in constraint to SimpleObject, all would work fine. This is strange and I can't find reason to this.
Thank guys. But we solved this riddle. Problem was, that db4o saving information about object and what interface it implement, only after first save.
So, we simple saved all our empty objects to base, before work with it.
I don't know exactly how db4o works, but having written a document db, I can say it's unlikely that db4o stores the entire type hierarchy. It will only keep track of the type that was used to store the data. This is really only for serialization in my case.
To make something like this work, you would likely need to build your own index. When an object is stored, for each type in the hierarchy (except Object and IDisposable I expect) update a record like new TypeIndex { IndexedType = typeof(IInterface), ActualKey = ... }.