I have the following interface and class
public interface IOwner
{
int Owner_pkid { get; set; }
string Name { get; set; }
}
public class Owner : IOwner
{
public Owner()
{
}
public int Owner_pkid { get; set; }
public string Name { get; set; }
}
I then have the following Data Access Methods in a separate class
public List<IOwner> GetAllOwners()
{
var sql = "SELECT owner_pkid, name from dbo.Owners ";
using (SqlConnection conn = new SqlConnection(ConfigurationManager.ConnectionStrings["DefaultConnection"].ConnectionString))
using (SqlCommand cmd = new SqlCommand(sql, conn))
{
cmd.CommandType = CommandType.Text;
List<IOwner> owners = new List<IOwner>();
conn.Open();
using (SqlDataReader reader = cmd.ExecuteReader())
BindResultSet<IOwner>(reader, owners);
return owners;
}
}
private void BindResultSet<T>(SqlDataReader reader, List<T> items) where T : new()
{
int counter = 0;
if (!reader.IsClosed)
{
while (reader.Read())
{
T record = GetNextDataObject<T>(items, counter);
counter++;
BindRecord<T>(reader, record);
}
}
}
private T GetNextDataObject<T>(List<T> items, int pointer) where T : new()
{
if (pointer < items.Count)
{
return items[pointer];
}
else
{
items.Add(new T());
return items[items.Count - 1];
}
}
private void BindRecord<IOwner>(SqlDataReader reader, IOwner owner)
{
owner.Name = (string)reader["name"];
owner.Owner_pkid = (int)reader["owner_pkid"];
}
I am getting 2 separate errors with this code:
In the GetAllOwners method I am getting an error with the call to BindResultSet which says
IOwner must be a non-abstract type with a parameterless constructor in order to be used here
I have a parameterless constrctor on the implementing class - don't think i can add one to the interface
In the final BindRecord method I am getting an error whereby the two property names are not recognized. This is possibly as a result of the first issue
The problem is that you cannot do new on an interface. So "new T" where T is IOwner will not work.
There are a number of different ways to fix this. The cleanest approach would be to add a factory interface whose only purpose is to create IOwner objects, say IOwnerFactory. Instantiate the factory at the top level and pass it down to the methods that need to create objects.
So instead of item.Add(new T()) use item.Add(ownerFactory.Create())
Related
I've been developing an application that uses various data connections (like database, rest api calls, json config files) in C#. I'm currently struggling to create a sensible data access layer abstraction that would enable to switch between these easily. Each of these require a different connection settings and also work differently.
I've looked at the example of the Repository pattern, but this doesn't really suit my needs. I want to be able to define some query pattern, which I can parametrize and that query will be able to handle the parameters. Example of what I currently have:
public interface IQuery<TResult>
{
}
public interface IQueryHandler<TQuery, TResult>
where TQuery : IQuery<TResult>
{
TResult Handle(TQuery query);
}
public class DatabaseQuery<TResult> : IQuery<IEnumerable<TResult>>
{
public string ConnectionString { get; set; }
public string CommandText { get; set; }
}
public class DatabaseConnection<TQuery, TResult> : IQueryHandler<TQuery, IEnumerable<TResult>>
where TQuery : DatabaseQuery<TResult>
{
public IEnumerable<TResult> Handle(TQuery query)
{
var results = new List<TResult>();
using (var connection = new SqlConnection(query.ConnectionString))
using (var command = new SqlCommand(query.CommandText, connection))
{
connection.Open();
using (var reader = command.ExecuteReader())
{
while (reader.Read())
{
results.Add(...
}
}
}
return results;
}
}
public class JsonQuery<TResult> : IQuery<IEnumerable<TResult>>
{
public string FileLocation { get; set; }
public Func<TResult, bool> Condition { get; set; }
}
public class JsonConnection<TQuery, TResult> : IQueryHandler<TQuery, IEnumerable<TResult>>
where TQuery : JsonQuery<TResult>
{
public IEnumerable<TResult> Handle(TQuery query)
{
var text = File.ReadAllText(query.FileLocation);
return Deserialize<TResult>(text).Results.Where(query.Condition);
}
}
public interface IQueryBuilder<TQuery, TParameters>
{
TQuery Build(TParameters parameters);
}
public class GetAccountsByStatusAndBalanceHigherThanQueryParameters
{
public string Status { get; set; }
public decimal Balance { get; set; }
}
public class GetAccountsByStatusAndBalanceHigherThan_DatabaseQueryBuilder :
IQueryBuilder<DatabaseQuery<Account>, GetAccountsByStatusAndBalanceHigherThanQueryParameters>
{
public DatabaseQuery<Account> Build(GetAccountsByStatusAndBalanceHigherThanQueryParameters parameters)
{
return new DatabaseQuery<Account>()
{
ConnectionString = "connString",
CommandText = $"SELECT * FROM Accounts WHERE Status = {parameters.Status} AND Balance = {parameters.Balance}"
};
}
}
public class GetAccountsByStatusAndBalanceHigherThan_JsonQueryBuilder
: IQueryBuilder<JsonQuery<Account>, GetAccountsByStatusAndBalanceHigherThanQueryParameters>
{
public JsonQuery<Account> Build(GetAccountsByStatusAndBalanceHigherThanQueryParameters parameters)
{
return new JsonQuery<Account>()
{
FileLocation = "fileLocation",
Condition = acc => acc.Status == parameters.Status && acc.Balance > parameters.Balance
};
}
}
public class GetAccountsByStatusAndBalanceHigherThanQuery : IQuery<IEnumerable<Account>>
{
public string Status { get; set; }
public decimal Balance { get; set; }
}
public class GetAccountsByStatusAndBalanceHigherThanQueryHandler :
IQueryHandler<GetAccountsByStatusAndBalanceHigherThanQuery, IEnumerable<Account>>
{
private readonly IQueryBuilder<JsonQuery<Account>, GetAccountsByStatusAndBalanceHigherThanQueryParameters>
_queryBuilder;
private readonly IQueryHandler<JsonQuery<Account>, IEnumerable<Account>> _connection;
public GetAccountsByStatusAndBalanceHigherThanQueryHandler(
IQueryBuilder<JsonQuery<Account>, GetAccountsByStatusAndBalanceHigherThanQueryParameters> queryBuilder,
IQueryHandler<JsonQuery<Account>, IEnumerable<Account>> connection)
{
_queryBuilder = queryBuilder;
_connection = connection;
}
public IEnumerable<Account> Handle(GetAccountsByStatusAndBalanceHigherThanQuery query)
{
var jsonQuery = _queryBuilder.Build(new GetAccountsByStatusAndBalanceHigherThanQueryParameters
{
Status = query.Status,
Balance = query.Balance
});
return _connection.Handle(jsonQuery);
}
}
So there are two connections - one database and one Json file connection. I have put the settings to the connections into queries - and while database connection requires connection string and SQL command, the Json connection requires file location and some filtering on the results. The problem is in the last query handler - GetAccountsByStatusAndBalanceHigherThanQueryHandler. I need to make that depend on specific connection, otherwise I can't get it to compile. What I want is to make sure that I can change the connection by just changing the injected parameters and all will work correctly.
Could you please advise on how to make sure that I can change the connections easily and also whether this architecture is good at all?
I think you may be overcomplicating this. It looks to me like you have two data sources to retrieve the same data - one is a json data source and a database. This fits pretty neatly into the land of "one interface, multiple implementations". You'll also need a class to sort out which implementation you should use (this is a perfect use case for a factory). The complicated querying logic you're trying to pass into the constructors you could pass as method arguments.
Code would look something like this:
public interface IAccountDataAccess
{
IEnumerable<Account> GetAccountsHigherThanBalance(Status status, Balance balance);
// etc.
}
public class JsonAccountDataAccess : IAccountDataAccess
{
private string jsonFilePath;
public JsonAccountDataAccess(string _jsonFilePath)
{
jsonFilePath = _jsonFilePath;
}
public IEnumerable<Account> GetAccountsHigherThanBalance(Status status, Balance balance)
{
// read json file, extract data, etc.
}
}
public class DatabaseAccountDataAccess : IAccountDataAccess
{
private string connectionString;
public DatabaseAccountDataAccess(string _connectionString)
{
connectionString = _connectionString;
}
public IEnumerable<Account> GetAccountsHigherThanBalance(Status status, Balance balance)
{
// read database, extract data, etc.
}
}
Here's the tricky part - you need a factory to spit out the correct implementation given some input. I'm not sure how you're planning to decide whether to use JSON or the database, but assuming that your client class knows:
public class AccountDataAccessFactory
{
public IAccountDataAccess GetDataAccess(bool useDatabase)
{
if(useDatabase) // you could use arbitrarily complex logic here
return new DatabaseAccountDataAccess(connString);
else
return new JsonAccountDataAccess(jsonFilePath);
}
}
Your client class could use it like this:
var factory = new AccountDataAccessFactory();
var dataAccess = factory.GetDataAccess(true);
var accounts = dataAccess.GetAccountsHigherThanBalance(status, balance);
I have the following data class and VM class:
public interface IData
{
string Name
{
get;
}
}
public class DataPartial: IData
{
public DataPartial()
{
}
public string Name => "Data partial";
}
public class DataFull : IData
{
public string Name => "Data full";
public DataFull()
{
}
}
public interface IVM
{
IData Data { get; }
}
public interface IVM_partial: IVM
{
new DataPartial Data { get; }
}
public class VM_Partial : IVM_partial
{
public VM_Partial()
{
Data = new DataPartial();
}
public DataPartial Data { get; set; }
IData IVM.Data => Data;
}
public interface IVM_Total:IVM_partial
{
new DataFull Data { get; }
}
public class VM_Total : IVM_Total
{
public VM_Total(IVM_partial dataA)
{
Data = new DataFull();
DataA_interface = dataA;
}
public IVM_partial DataA_interface { get; }
public DataFull Data { get; private set; }
DataPartial IVM_partial.Data => DataA_interface.Data;
IData IVM.Data => Data;
}
public static class RunVM<T, VM>
where T: class, IData
where VM :class, IVM
{
public static T RunMe(VM hi)
{
var vmA = (hi as VM); //how to force-cast this to the VM type??!!
return (T)vmA.Data;
}
}
class Program
{
static void Main(string[] args)
{
VM_Partial partialData = new VM_Partial();
var VMClass = new VM_Total(partialData);
RunVM<DataFull, IVM_Total>.RunMe(VMClass);
RunVM<DataPartial, IVM_partial>.RunMe(VMClass); //here it throws exception because I can't force cast the IVM to IVM_partial
}
}
At the method RunVM<DataPartial, IVM_partial>.RunMe(VMClass);, I want it to return me the DataPartial object, which I know it's there in the object VMClass, but I cannot get it done.
I will get an InvalidCastException when I am at the RunMe method, because the parameter hi is always VMClass, and I can never get it to behave like IVM_partial class. In other words, I can't cast hi to a more basic interface IVM_partial.
How to cast hi to a more basic interface IVM_partial? Is it possible at all, and if not, why not?
It's not the cast that's the problem - it's that you expect the compiler (or runtime) to pick up on the fact that the cast is to a type that declares a new Data property.
This line in RunMe:
return (T)vmA.Data;
... will always use the Data property declared by IVM, because that's the only property the compiler knows about when it's compiling that method. It doesn't matter that you're casting to another interface that contains a new Data property... the cast is about an execution-time check; it doesn't change which Data property the method uses.
It's unclear to me exactly what you're trying to achieve here, but I strongly suspect that you'll need to change tack significantly - maybe by adding another generic type parameter into the mix, maybe by using polymorphism more, or maybe changing the design more radically.
First Look at my code
namespace HealthClub
{
public partial class frmTrainerMaster : Form
{
DataTable dt = new DataTable();
frmHome Home = new frmHome();
public frmTrainerMaster()
{
InitializeComponent();
}
}
private void frmTrainerMaster_Load(object sender, EventArgs e)
{
FillValues("UserNameText");
}
public void FillValues(string UserName)
{
DataTable DT;
SqlCommand cmd = new SqlCommand();
try
{
cmd.Connection = Connections.Connection[UserName];
cmd.CommandType = CommandType.StoredProcedure;
cmd.CommandText = "TrainerMaster_pro";
cmd.Parameters.AddWithValue("Option", "FillValues".Trim());
if (Connections.Connection[UserName].State == ConnectionState.Closed)
Connections.Connection[UserName].Open();
SqlDataAdapter adp = new SqlDataAdapter(cmd);
DT = new DataTable();
adp.Fill(DT);
lblId___.Text = DT.Rows[0][0].ToString();
}
catch (Exception ex)
{
MessageBox.Show(ex.ToString());
}
finally
{
cmd.Parameters.Clear();
cmd.Dispose();
Connections.Connection[UserName].Close();
}
}
}
Now I am calling FillValues() from another class like this
class AnotherClass
{
public void TestMethod(string FormName)
{
Type tp = Type.GetType("HealthClub." + FormName);
object myobj = Activator.CreateInstance(tp);
MethodInfo method = myobj.GetType().GetMethod("FillValues");
object[] parametersArray = new object[] { UserName };
method.Invoke(myobj, parametersArray);
}
}
If you look at the FillValues(), I am assigning the database value to a label. When I am calling it in my first class in page load it's working fine.
But when I am Invoking the medthod from second class, Method invokes but database value does not assign to the label.
What extra effort I need to do ?
There is a class and there is an instance. This is very basic concept you need to understand (not just in C#, but in any objective-oriented language).
// class example
class FrmTrainerMaster { public int SomeProperty { get; set;} }
When you create new instance:
// creates one window
var frmTrainerMasterInstanceOne = new FrmTrainerMaster();
frmTrainerMasterInstanceOne.SomeProperty = 1;
// creates second window
var frmTrainerMasterInstanceTwo = new FrmTrainerMaster();
frmTrainerMasterInstanceTwo.SomeProperty = 2;
Instances are SEPARATE - so at this point querying
// will return 1
Console.Out.WriteLine(frmTrainerMasterInstanceOne.SomeProperty);
// will return 2
Console.Out.WriteLine(frmTrainerMasterInstanceTwo.SomeProperty);
With reflection var myobj = Type.GetType("HealthClub.FrmTrainerMaster"); is equal to var myobj = new FrmTrainerMaster(); so by doing anything with myobj, you still can't affect frmTrainerMasterInstanceOne or frmTrainerMasterInstanceTwo.
What do you need is actually method how to pass reference to instance (of FrmTrainerMaster class) to place where you need it (lets call it AnotherForm), there is no magic list of all instances for given class unless you explicitly build it.
public partial class FrmTrainerMaster : Form
{
public void FillValues(string userName) { ... }
}
One way is via constructor injection (generally proffered - since at time when object (AnotherForm) is constructed you have it in valid state (i.e. with all dependencies initialized).
public class AnotherForm : Form {
private readonly FrmTrainMaster _frmTrainMaster;
public AnotherForm(FrmTrainMaster frmTrainMaster) {
if (frmTrainMaster == null) {
throw new ArgumentNullException(nameof(frmTrainMaster));
}
_frmTrainMaster = frmTrainMaster;
}
}
Or via setter injection:
public class AnotherForm : Form {
private FrmTrainMaster _frmTrainMaster;
public FrmTrainMaster MasterForm { set { _frmTrainMaster = value; } }
}
Either way the reflection is not necessary at all. At any place in your AnotherForm you can just call
class AnotherForm : Form {
...
public void FooMethodThatCallFillValues() {
_frmTrainMaster.FillValues("...");
}
}
I'm trying to create a generic data retrieval process. What I have currently works, but there is a part of it that doesn't seem right and I'm hoping there is a better way to accomplish it.
So the idea is that I have classes for each table in the database, here is an example of a class:
public class CMCGRGRGROUP : IFacetsObject<CMCGRGRGROUP>
{
public int GRGR_CK { get; set; }
public string GRGR_NAME { get; set; }
public string GRGR_ADDR1 { get; set; }
public IEnumerable<CMCGRGRGROUP> ToObject(DataTable table)
{
return table.AsEnumerable().Select(row =>
{
return new CMCGRGRGROUP
{
GRGR_CK = Convert.ToInt32(row["GRGR_CK"]),
GRGR_NAME = row["GRGR_NAME"].ToString(),
GRGR_ADDR1 = row["GRGR_ADDR1"].ToString()
};
});
}
}
You'll notice that the class implements an interface of its own type. The interface simply defines a method called ToObject, which is used to convert a datatable to a class of that particular type:
public interface IFacetsObject<T>
{
IEnumerable<T> ToObject(DataTable obj);
}
Now, here is the method that I am using to execute a query:
public IEnumerable<T> ExecuteQuery<T>(string sql, IFacetsObject<T> obj) where T : new()
{
using (var conn = new AseConnection(_conn))
{
conn.Open();
var cmd = new AseCommand(sql, conn);
var dt = new DataTable();
var da = new AseDataAdapter(sql, conn);
da.Fill(dt);
return obj.ToObject(dt); //this is the interface method
}
}
So the main question is:
How can the generic method know that T should implement IFacetsObject<T>? That way I don't have to pass IFacetsObject<T> as a parameter. Ideally, I could change the return line to be something like this:
return T.ToObject(dt);
And call it like this:
var result = ExecuteQuery<CMCGRGRGROUP>(sql).Take(5);
Instead of like this:
var result = ExecuteQuery<CMCGRGRGROUP>(sql, new CMCGRGRGROUP()).Take(5);
I'll admit that I'm not terribly familiar with generics yet so there may be something within the implementation that isn't right.
You can add a constraint on your ExecuteQuery method. You already have one: requiring that T be newable. You'd declare it like:
public IEnumerable<T> ExecuteQuery<T>(string sql, IFacetsObject<T> obj)
where T : IFacetsObject<T>, new()
{
using (var conn = new AseConnection(_conn))
{
conn.Open();
var cmd = new AseCommand(sql, conn);
var dt = new DataTable();
var da = new AseDataAdapter(sql, conn);
da.Fill(dt);
return obj.ToObject(dt); //this is the interface method
}
}
So it now knows T is an IFacetsObject<T>. You could now do:
public IEnumerable<T> ExecuteQuery<T>(string sql)
where T : IFacetsObject<T>, new()
{
using (var conn = new AseConnection(_conn))
{
conn.Open();
var cmd = new AseCommand(sql, conn);
var dt = new DataTable();
var da = new AseDataAdapter(sql, conn);
da.Fill(dt);
return new T().ToObject(dt); //this is the interface method
}
}
Which IMO is still pretty ugly.
EDIT Response:
Note that you cannot call T.ToObject - an interface cannot define a static method. The workaround is the use of new to create a new instance of T and call the instance method.
You need a generic constraint on your interface. Declare it like this:
public interface IFacetsObject<T> where T : IFacetsObject<T>
{
IEnumerable<T> ToObject(DataTable obj);
}
In order to get this to work, you also have to change your declaration like this:
public IEnumerable<T> ExecuteQuery<T>(string sql, IFacetsObject<T> obj)
where T : IFacetsObject<T>, new()
I'd like to somehow pass a parameter into a list when I'm instantiating a new list of certain classes.
More specifically, I'd like to do something like the following:
List<FirstClass>(dataTable);
List<SecondClass>(dataTable);
If the first line of code is called, the constructor will deal with dataTable in a certain way than if the latter is called (FirstClass has different fields).
What I've Tried
namespace DeeMacsNamespace
{
public class FirstClass
{
public String Title { get; set; }
public String Url { get; set; }
}
public class FirstClass : List<FirstClass>
{
public FirstClass(DataTable table)
{
foreach (DataRow row in table.Rows)
{
this.Add(new FirstClass()
{
Title = (String)row["Title"]
});
}
}
}
}
I'm assuming (or at least hoping), the above will work. But how do I then most efficiently reuse this code that reads from a DataTable in a really similar constructor for another list of a certain class? And how do I incorporate a conditional statement to check whether the constructor is from the FirstClass or SecondClass type? I would like to avoid rewriting this for a similar constructor for SecondClass.
If I've understood you correctly, then use something like this:
class MyCollection<T> : Collection<T>
{
public MyCollection(DataTable dataTable, Func<DataRow, T> itemsFactory)
: base(dataTable.Rows.Cast<DataRow>().Select(row => itemsFactory(row)).ToList())
{
}
}
var firstClassCollection = new MyCollection<FirstClass>(dataTable, row => new FirstClass
{
Title = (String)row["Title"],
Url = (String)row["Url"]
});
class FirstClass <T>
{
if (typeof(T) == typeof(FirstClass))
{
// ... snip
}
}
Then have all other classes inherit from FirstClass.
There's some unanswered questions regarding your intent. That being said this generic setup may fit the bill:
public interface ISomeInterface
{
String Title { get; set; }
String Url { get; set; }
}
public class SecondClass : ISomeInterface
{
public String Title { get; set; }
public String Url { get; set; }
}
public class FirstClass : ISomeInterface
{
public String Title { get; set; }
public String Url { get; set; }
}
public class SomeClassCollection<T> : List<T> where T: ISomeInterface, new()
{
public SomeClassCollection(DataTable table)
{
foreach (DataRow row in table.Rows)
{
this.Add(new T()
{
Title = (String)row["Title"]
});
}
}
}
private static void Main()
{
var table = new DataTable();
var collection = new SomeClassCollection<FirstClass>(table);
}
You could write an extension method such as:
public static class DataTableEx
{
public static IList<T> CreateList<T>(this DataTable dt,
Func<DataRow,T> selector)
{
return dt.Rows.Cast<DataRow>().Select(selector).ToList();
}
}
Then use it as follows:
IList<string> myList =
dataTable.CreateList(r => new FirstClass{Title = (string)r["Title"]});
List<FirstClass> MyList1 = dataTable.Rows.Select(Row =>
new FirstClass()
{
Title = (String)Row["Title"]
}
).ToList();
List<SecondClass> MyList2 = dataTable.Rows.Select(Row =>
new SecondClass() { the way you create second class } ).ToList();
First of all, you don't want to have the two classes have the same name (the FirstClass and the ListOfFirstClass).
Second of all, your question is a bit unclear, but I believe you're trying to turn a DataTable into a list of First/SecondClass.
If you have access to the DataTable class, you can have it implement the IEnumerable interface.
Using Linq, you can do comething like:
using System.Linq;
public class DataTable : IEnumerable<T>
{
IEnumerable<T> IEnumerable<T>.GetEnumerator()
{
return from row in rows
where FindIfRowIsOfClass<T>(row)
select new T(row);
}
}
You'll have to implement the generic IEnumerable method as well, and fill out the FindIfRowIsOfClassT method. This will most likely be done by finding out if it has the right fields.
The result is the ability to do
List<FirstClass> listOfFirstClass = new List<FirstClass>(dataTable.GetEnumerator<FirstClass>);
I'm not 100% sure, but you may actually be able to get away with not calling GetEnumerator explicitly, List might do that for you.
If you don't have access to that, you can do it manually:
var enumerableFirstClass = from row in dataTable.rows
where <insert row is FirstClass check>
select new FirstClass(){Title = (string)row["Title"]};
List<FirstClass> listOfFirstClass = new List<FirstClass>(enumerableFirstClass);
Hope this helps.
Happy coding!