I'm working on a software that uses a SQLite local database in Xamarin.
I'm using Microsoft's Todo sample as a base.
https://learn.microsoft.com/ja-jp/xamarin/xamarin-forms/data-cloud/data/databases
In this sample, only Todo is stored, so the only class that accesses SQLite is TodoItemDatabase.
In the software we are building, we are planning to access multiple tables such as Todo, Memo, Diary, and so on.
In that case, we need to create TodoItemDatabase, MemoItemDatabase, and DiaryItemDatabase separately for each of them.
So, I decided to use generic classes in this case.
public class BaseDatabase<T>
{
static SQLiteAsyncConnection Database;
public static readonly AsyncLazy<ItemDatabase> Instance = new AsyncLazy<ItemDatabase>(async () =>
{
File.Delete(Constants.DatabasePath);
var instance = new ItemDatabase();
try
{
CreateTableResult result = await Database.CreateTableAsync<T>();
}
catch(Exception exception)
{
var error = exception.Message;
}
return instance;
});
public BaseDatabase()
{
Database = new SQLiteAsyncConnection(Constants.DatabasePath, Constants.Flags);
}
public Task<List<T>> GetItemsAsync()
{
return Database.Table<T>().ToListAsync();
}
public Task<List<T>> GetItemsNotDoneAsync()
{
return Database.QueryAsync<T>("SELECT * FROM [Item] WHERE [Done] = 0");
}
public Task<T> GetItemAsync(string id)
{
return Database.Table<T>().Where(i => i.Id == id).FirstOrDefaultAsync();
}
public Task<int> SaveItemAsync(T item)
{
if (item.Id == null)
{
return Database.InsertAsync(item);
}
else
{
return Database.UpdateAsync(item);
}
}
public Task<int> DeleteItemAsync(T item)
{
return Database.DeleteAsync(item);
}
}
However, when I replaced Task<List> with Task<List> and Task with Task as class BaseDatabase to make it a generic class, two errors occurred.
The first one is that
T must be a generic type or a non-abstract type with a constructor without public parameters to be used as parameter T in method SQLiteAsyncConnection.Table().
T does not contain an Id definition and no accessible extension method ItemId was found that accepts the first argument of type T.
How to solve these two problems?
Please let me know how to solve these two problems in the code of the generic class.
'T' must be a non-abstract type with a public parameterless
constructor in order to use it as parameter 'T' in the generic type or
method 'SQLiteAsyncConnection.CreateTableAsync(CreateFlags)
Just as the tip mentioned,in your code, T must be a generic type or a non-abstract type with a constructor for funtion SQLiteAsyncConnection.CreateTableAsync
without public parameters
CreateTableResult result = await Database.CreateTableAsync<T>();
So, you need use a common class to replace the T here.
we are planning to access multiple tables such as Todo, Memo, Diary,
and so on. In that case, we need to create TodoItemDatabase,
MemoItemDatabase, and DiaryItemDatabase separately for each of them
In a database, you can create many tables inside it instead of creating a database for a every table.
In summary, you can create a table one by one inside of your database.
You can refer to the following code:
public class TodoItemDatabase
{
static SQLiteAsyncConnection Database;
public static readonly AsyncLazy<TodoItemDatabase> Instance = new AsyncLazy<TodoItemDatabase>(async () =>
{
var instance = new TodoItemDatabase();
CreateTableResult result = await Database.CreateTableAsync<TodoItem>();
CreateTableResult result_memo = await Database.CreateTableAsync<Memo>();
CreateTableResult result_diary = await Database.CreateTableAsync<Diary>();
return instance;
});
public TodoItemDatabase()
{
Database = new SQLiteAsyncConnection(Constants.DatabasePath, Constants.Flags);
}
/* query funtion */
public Task<List<TodoItem>> GetItemsAsync()
{
return Database.Table<TodoItem>().ToListAsync();
}
public Task<List<Memo>> GetItemsAsync_memo()
{
return Database.Table<Memo>().ToListAsync();
}
// other code
}
Related
I created a specialization for a generic like code below:
public class BaseGeneric<T>
{
public static T DoStuff()
=> default;
}
public class SpecializedFromBaseGeneric : BaseGeneric<int>
{
public static new int DoStuff()
=> 789;
}
Now to call the DoStuff() method I would like to use var result = BaseGeneric<int>.DoStuff();
When I run this code, result is 0 instead of 789. The debugger shows that the call will enter the DoStuff() from public class BaseGeneric<T> instead of SpecializedFromBaseGeneric.
What am I doing wrong?
Later edit
I also tried to create specialization in the below format but that does not even compile:
public class BaseGeneric<T> where T : int
{
public static T DoStuff()
=> 789;
}
I want to do several specializations and use the call similar to the one specified above for int data type BaseGeneric<int>.DoStuff(). And for each specialization use the same syntax where only data type is changed but different implementation is used (eg: for string that would be BaseGeneric<string>.DoStuff()). How to achieve this behaviour?
public class BaseGeneric<T> where T : IConvertible
{
public static T DoStuff()
{
if (typeof(T) == typeof(int))
{
return (T)(object)789;
}
if (typeof(T) == typeof(string))
{
return (T)(object)"ss";
}
return default(T);
}
}
However, as the asnwer here suggests, this is brittle, and doesn't cover every possible usage.
var result = BaseGeneric<int>.DoStuff()
This one calls the function of a base class. It returns new T(), that equals new int(), that equals zero.
var result = SpecializedFromBaseGeneric.DoStuff()
This one will return what you need.
I am learning generics in C#. So this may be simple for experienced folks.
I have 71 different models and I want to be able to generically store data from CSV into them.
The processing part is not super hard, I have this method signature:
private static async Task ProcessFileAsync<T>(string currentFile) where T : class, new()
The hard part is calling it. I have one CSV file for each model that I want to place data into. The Name of the CSV file is identical to the Model's name (ex: Product.csv would correspond to the Product model).
Ideally, I would like to just send the name in the caller, but I am getting a "X is a variable but is used like a type" Compiler error.
I could have a massive switch statement to solve this issue, but that seems relatively wasteful.
Any assistance would be appreciated.
Put another way, I could do the following:
switch(justFName)
{
case "Address":
_ = ProcessFileAsync<Address>(ci.FullName);
break;
case "Currency":
_ = ProcessFileAsync<Currency>(ci.FullName);
break;
...
...
...And so on
...
...
default:
//No method for this file name
break;
}
instead I would like to have something like this:
_ = ProcessFileAsync<justFName>(ci.FullName);
If you can somehow determine all classes you need to handle from your assembly (personally I like to mark them with specially created attribute), then Reflection and Expression Trees to the rescue:
public class Address { }
public class MethodHolder // Just dummy class to hold your process action
{
public static async Task ProcessFileAsync<T>(string currentFile) where T : class, new()
{
Console.WriteLine(currentFile);
}
}
public static class Processor
{
private static readonly Dictionary<string, Action<string>> _dict;
static Processor()
{
var types = typeof(Address).Assembly.GetTypes()
// filter your types correctly here somehow
// JIC do not forget to verify that they satisfy
// your generic constraints
.Where(t => t.Name == "Address");
_dict = types.ToDictionary(t => t.Name, BuildAction);
}
private static Action<string> BuildAction(Type t)
{
var method = typeof(MethodHolder).GetMethod(nameof(MethodHolder.ProcessFileAsync))
.MakeGenericMethod(t);
var param = Expression.Parameter(typeof(string));
return Expression.Lambda<Action<string>>(
Expression.Call(method, param),
param)
.Compile();
}
// TODO: add some nice handling for keys not in dictionary
public static void Process(string key, string value) => _dict[key](value);
}
And usage: Processor.Process(nameof(Address), "testfilename"); (nameof just for the sake of example)
The #GuruStron's answer is very good. Another way of achieving what you need is only using Reflection. However, like #GuruStrong suggest, it's good for you that include an annotation in the classes where the search will be performed, or put them in a single assembly. The following code works only if these classes are in the same assembly.
#region These classes must be in the same assembly
public class Address {
}
public class Currency {
}
#endregion
class Program {
static async Task Main(string[] args) {
var justFName = "Currency";
var fullName = "name";
var type = typeof(Address).Assembly.GetTypes()
.Single(x => x.Name == justFName);
var method = typeof(Program).GetMethod(nameof(ProcessFileAsync),
BindingFlags.NonPublic | BindingFlags.Static)
.MakeGenericMethod(type);
await (Task)method.Invoke(null, new object[] { fullName });
Console.ReadLine();
}
private static async Task ProcessFileAsync<T>(string currentFile) where T : class, new() {
Console.WriteLine(currentFile);
}
}
I have a class Decision, with Fluent interface, that I use as follows:
Decision.Name("MyDecision").SetRule(new MyRule());
Internally Decision has a method Run that calls MyRule IsSatisfied method and pass 2 arguments: Data and DateType.
I would also like to be able to use the following alternative syntax to define the Rule:
Decision.Name("MyDecision").SetRule((data, dataType) => {
// Rule definition
// Return boolean result
});
How can I do this?
Definition Class
public class Definition {
private String _name;
private Rule rule;
public Definition(String name) {
_name = name;
}
public static Definition Name(String name) {
return new Definition(name);
}
public Definition SetRule(Rule rule) {
_rule = rule;
return this;
}
public bool Run(Data data, DataType dataType) {
return _rule.IsSatisfied(data, dataType);
}
}
Rule class
public abstract class Rule {
public abstract Boolean IsSatisfied(Data data, DataType dataType);
}
Your are expecting to receive a function or a delegate and convert it into a new Rule() instance, that is an abstract class.
The first problem is: you can not instantiate a abstract class. So unless you add a Type as parameter to SetRule OR make Rule class not abstract. It will not be possible.
The second problem is: It's not a problem, but a warning. Your Rule class has only one method. And your Definition class has one one Rule. If this is your real code, it will be more clear if the IsSatisfied method was inside the Definition class.
But your question is: How can I do this?
Definition.Name("MyDecision").SetRule((data, dataType) => {
// Rule definition
// Return boolean result
});
I'll provide an example about how to do this using Rule class not abstract.
First you need to make your rule class concrete an able to receive a function or an delegate in the constructor.
I'll use Func<> as Ian Kemp already provided an solution with delegate. I suggest you to use delegate for this kind of scenarios because it's better for maintainability.
public class Rule {
protected Func<Data, DataType, Boolean> _isSatisfied {get; set;}
// Deafult constructor is optional. I keep it just rise a correct type of exception in case any child class call IsSatisfied without set it first.
public Rule()
{
_isSatisfied = (data, dataType) => { throw new NotImplementedException(); }; // Or any default behavior you want.
}
// this contructor will provide the behavior you want.
public Rule(Func<Data, DataType, Boolean> isSatisfied)
{
_isSatisfied = isSatisfied;
}
// This method now is only a wrapper to our Func<>
public virtual Boolean IsSatisfied(Data data, DataType dataType)
{
return _isSatisfied(data, dataType);
}
}
Then add a new method on Definition class that receive our Func and create the rule.
public class Definition
{
private String _name;
private Rule _rule;
public Definition(String name) {
_name = name;
}
public static Definition Name(String name) {
return new Definition(name);
}
public Definition SetRule(Rule rule) {
_rule = rule;
return this;
}
// Works like a charm
public Definition SetRule(Func<Data, DataType, Boolean> func) {
_rule = new Rule(func);
return this;
}
public bool Run(Data data, DataType dataType) {
return _rule.IsSatisfied(data, dataType);
}
}
And if you and to create a specific Rule that overrides and implements its own IsSatisfied method you can do it like this.
public class MyRule : Rule {
public override Boolean IsSatisfied(Data data, DataType dataType)
{
Console.WriteLine("MyRule: Dataname:{0}, DataTypeName: {1}", data.DataName, dataType.DataTypeName);
return false;
}
}
And this is an small sample:
public class Program
{
public static void Main()
{
var data = new Data() { DataName = "My Data Name" };
var dataType = new DataType() { DataTypeName = "My Data Type Name" };
try
{
// Log-> MyRule: Dataname:My Data Name1, DataTypeName: My Data Type Name
Definition.Name("MyDecision").SetRule(new MyRule()).Run(data, dataType);
// Log -> Func: Dataname:My Data Name1, DataTypeName: My Data Type Name
Definition.Name("MyDecision").SetRule((dataArg, dataTypeArg) =>
{
Console.WriteLine("Func: Dataname:{0}, DataTypeName: {1}", dataArg.DataName, dataTypeArg.DataTypeName);
return false;
}).Run(data, dataType);
}
catch(Exception ex)
{
Console.WriteLine("Error: ", ex.Message);
}
}
}
You can run this sample on DotNetFiddle if you want.
If you are trying to create a generic rules engine I believe that you should give us more information about how you gonna implement your rules (On a new question).
You can also take a look on how this open sources rules engines works.
NRules
RulesChain
A nondestructive approach to achieve this is to create an extension method for Rule class. By the term nondestructive I mean an approach that does not modify the original architecture/concept of your code. In order to create an extension method, you have to first declare a static class. Then you have to define a method whose first parameter is pointing to the type of the target class (using this keyword). That said; you can implement what you need the following way:
public static class RuleExtensions {
public static Boolean SetRule (this Rule rule, Func<(Definition definition, Boolean result)> definitionFn) {
var fn = definitionFn();
rule.SetRule(fn.definition);
return fn.result;
}
}
Define a delegate typed to take in (Data data, DataType dataType) and return a bool, an internal (could also be private) class that wraps and executes that delegate, and finally a method on Definition to create and assign a new instance of the class.
public delegate bool RuleEvaluator(Data data, DataType dataType);
internal class InlineRule : Rule
{
private RuleEvaluator _ruleEvaluator;
public InlineRule(RuleEvaluator ruleEvaluator)
{
_ruleEvaluator = ruleEvaluator;
}
public override bool IsSatisfied(Data data, DataType dataType)
=> _ruleEvaluator(data, dataType);
}
public class Definition
{
... code as before...
public Definition SetRule(RuleEvaluator ruleEvaluator)
{
_rule = new InlineRule(ruleEvaluator);
return this;
}
}
I have a basic asynchronous method in one class, which returns an object.
In some of the flows it may fail and I want to report it back.
But I can only return the object.
I tried nullable object, but got the error:
MyObject must be a non-nullable value type in order to use it as parameter 'T' in the generic type or method 'System.Nullable'
I assume I can use exception, but I wanted something simpler in the calling function.
I also cannot use ref or out in async methods.
Is there a way to report back some kind of true/false of success or failure?
public static async Task<MyObject> getObject()
{
if (NotOkFromSomeReason())
{
//Not sure what to do here
}
return await myDataBase.FindAsync(something);
}
I call it using:
MyObject object = await getObject();
// I want something simple as if(object)...
If returning null is an option, you can do:
public static Task<MyObject> getObject()
{
if (NotOkFromSomeReason())
{
return Task.FromResult<MyObject>(null);
}
return myDataBase.FindAsync(something);
}
PS: I'm supposing there's no other code which uses await in this function, so I've removed async. Otherwise you can just use the async qualifier and return null directly. Since it's returning Task objects, it's still awaitable from the outside
Then you could check for null outside:
MyObject myObject = await getObject();
if(myObject == null)
{
}
This will only work if null is not a possible "correct" result
You could otherwise use exceptions
Just wrap it into yet another class that will return both desired instance & status code - like:
public class StatusReport
{
public boolean Ok { get; set; }
public MyObject Instance { get; set; }
}
public static async Task<StatusReport> getObject()
{
if (NotOkForSomeReason())
{
return new StatusReport { Ok = false };
}
return new StatusReport { Ok = true, Instance = await myDataBase.FindAsync(something) };
}
I have the following method
public static async void CheckAndInsert<T>(T obj)
{
var data = AppDelegate.Self.InstanceLive.LoadAllAsync<T>().Result.ToList();
if (data.Count != 0)
{
var theData = data.FirstOrDefault(t => t.id == obj.id);
if (theData == null)
await StoreData(theData);
else
{
if (theData.__updatedAt != obj.__updatedAt)
await UpdateData(theData);
}
}
}
The database contains tables generated from an Azure database, so are guaranteed to always have an id.
In theory, this code should work, but on compilation, I'm getting an error that
Type T does not contain a definition for 'id' and no extension method 'id' of type 'T'
Is there a way to get this code running?
The trouble here is that with this generic method declaration T can be any type, including those that do not provide an Id member. Obviously, your code would break in this case, hence it is disallowed by the compiler.
To get around this, you need to provide generic type constraints to constrain T such that you can be sure it has an Id property.
public static async void CheckAndInsert<T>(T obj) where T:IIdentity
and have your model classes implement IIdentity which might look something like
public interface IIdentity
{
int Id{get;}
}