What's the best way to send generic repository via WCF? - c#

I have a repository like this :
public abstract class DbRepository : IDbRepository
{
public TEntity Insert<TEntity>(TEntity entity) where TEntity : class
{
_context.Entry(entity).State = EntityState.Added;
return entity;
}
public TEntity Update<TEntity>(TEntity entity) where TEntity : class
{
_context.Entry(entity).State = EntityState.Modified;
return entity;
}
}
Service Contract is like :
[ServiceContract]
public interface IDbRepository
{
[OperationContract]
TEntity Insert<TEntity>(TEntity entity) where TEntity : class;
[OperationContract]
TEntity Update<TEntity>(TEntity entity) where TEntity : class;
}
Now I know I cant send this via wcf, I have to make the open generic class clossed.
But the problem is I have many entities in my Domain data repository and I want it should be decided by the client what entity it is needed may be via reflection or predefined known types.
So my question :
Is there a smart or fake way to send these generics service via wcf ?
My Goal is I dont want to write this servicecontract for each and every entity.
Many thanks.
Edit: Guys have you seen this Here Tweak in app.config file below:
<endpoint
address="myAddress" binding="basicHttpBinding"
bindingConfiguration="myBindingConfiguration1"
contract="Contracts.IEntityReadService`1[[Entities.mySampleEntity, Entities]], Service.Contracts" />
Can somebody please explain this how this contract has been implemented.
Has anybody tried to implement this tweak in app.config file. I have tried but not working for me for now. Need helpful answer !

Have you look into WCF Data Services? This seems to be the route you want to go down without hand crafting the interfaces and plumbing yourself.
As you have stated, interfaces are not good over WCF. One particular flaw is the expectation of the IQueryable<T> over WCF, which does not work at all. Even IEnumerable<T> doesn't give the expected results all of the time.

Is there a smart or fake way to send these generics service via wcf ?
My Goal is I dont want to write this servicecontract for each and
every entity. Many thanks.
hmm, why not?
Lets us try the following :
This interface is necessary as it will identify which objects can be used by your Repository.I don't know what your implementation of your T Entity is or how your CRUD operations work; however, just in case you don't have it covered, we're also going to add the methid GetPrimaryKeys.
public interface IRepositoryEntry
{
IList<String> GetPrimaryKeys();
}
So now we need a repository, since your biggest concern is that you don't want to rewrite code you should try something like this:
This implementation means that whatever our database entries are, they must support the default constructor. This is important for the implementation of this this interface :
public interface IRepository<T> where T : IRepositoryEntry, new()
{
event EventHandler<RepositoryOperationEventArgs> InsertEvent;
event EventHandler<RepositoryOperationEventArgs> UpdateEvent;
event EventHandler<RepositoryOperationEventArgs> DeleteEvent;
IList<String> PrimaryKeys { get; }
void Insert(T Entry);
void Update(T Entry);
void Delete(Predicate<T> predicate);
bool Exists(Predicate<T> predicate);
T Retrieve(Predicate<T> predicate);
IEnumerable<T> RetrieveAll();
}
Now we will make our service:
[ServiceContract]
public interface IDbRepository
{
[OperationContract]
object Insert(object entity);
[OperationContract]
object Update(object entity);
}
Notice no generics? That's important. Now we need to have a creative implementation for our Repository. I'm going to give two, one for Memory so unit testing can be done and the other for a database.
public class OracleRepository
{
const string User = "*";
const string Pass = "*";
const string Source = "*";
const string ConnectionString = "User Id=" + User + ";" + "Password=" + Pass + ";" + "Data Source=" + Source + ";";
public static IDbConnection GetOpenIDbConnection(){
//Not really important; however, for this example I Was using an oracle connection
return new OracleConnection(ConnectionString).OpenConnection();
}
protected IEnumerable<String> GetEntryPropertyNames(Type type){
foreach (var propInfo in type.GetProperties())
yield return propInfo.Name;
}
}
public class OracleRepository<T> : OracleRepository,IDisposable, IRepository<T> where T : IRepositoryEntry, new()
{
#region Public EventHandlers
public event EventHandler<RepositoryOperationEventArgs> InsertEvent;
public event EventHandler<RepositoryOperationEventArgs> UpdateEvent;
public event EventHandler<RepositoryOperationEventArgs> DeleteEvent;
#endregion
#region Public Properties
public IList<String> PrimaryKeys{ get { return primaryKeys.AsReadOnly(); } }
public IList<String> Properties { get; private set; }
public String InsertText { get; private set; }
public String UpdateText { get; private set; }
public String DeleteText { get; private set; }
public String SelectText { get; private set; }
#endregion
#region Private fields
List<String> primaryKeys;
IDbConnection connection;
IDbTransaction transaction;
bool disposed;
#endregion
#region Constructor(s)
public OracleRepository()
{
primaryKeys = new List<String>(new T().GetPrimaryKeys());
Properties = new List< String>(GetEntryPropertyNames(typeof(T))).AsReadOnly();
SelectText = GenerateSelectText();
InsertText = GenerateInsertText();
UpdateText = GenerateUpdateText();
DeleteText = GenerateDeleteText();
connection = GetOpenIDbConnection();
}
#endregion
#region Public Behavior(s)
public void StartTransaction()
{
if (transaction != null)
throw new InvalidOperationException("Transaction is already set. Please Rollback or commit transaction");
transaction = connection.BeginTransaction();
}
public void CommitTransaction()
{
using(transaction)
transaction.Commit();
transaction = null;
}
public void Rollback()
{
using (transaction)
transaction.Rollback();
transaction = null;
}
public void Insert(IDbConnection connection, T entry)
{
connection.NonQuery(InsertText, Properties.Select(p => typeof(T).GetProperty(p).GetValue(entry)).ToArray());
if (InsertEvent != null) InsertEvent(this, new OracleRepositoryOperationEventArgs() { Entry = entry, IsTransaction = (transaction != null) });
}
public void Update(IDbConnection connection, T entry)
{
connection.NonQuery(UpdateText, Properties.Where(p => !primaryKeys.Any(k => k == p)).Concat(primaryKeys).Select(p => typeof(T).GetProperty(p).GetValue(entry)).ToArray());
if (UpdateEvent != null) UpdateEvent(this, new OracleRepositoryOperationEventArgs() { Entry = entry, IsTransaction = (transaction != null) });
}
public void Delete(IDbConnection connection, Predicate<T> predicate)
{
foreach (var entry in RetrieveAll(connection).Where(new Func<T, bool>(predicate)))
{
connection.NonQuery(DeleteText, primaryKeys.Select(p => typeof(T).GetProperty(p).GetValue(entry)).ToArray());
if (DeleteEvent != null) DeleteEvent(this, new OracleRepositoryOperationEventArgs() { Entry = null, IsTransaction = (transaction != null) });
}
}
public T Retrieve(IDbConnection connection, Predicate<T> predicate)
{
return RetrieveAll(connection).FirstOrDefault(new Func<T, bool>(predicate));
}
public bool Exists(IDbConnection connection, Predicate<T> predicate)
{
return RetrieveAll(connection).Any(new Func<T, bool>(predicate));
}
public IEnumerable<T> RetrieveAll(IDbConnection connection)
{
return connection.Query(SelectText).Tuples.Select(p => RepositoryEntryBase.FromPlexQueryResultTuple(new T(), p) as T);
}
#endregion
#region IRepository Behavior(s)
public void Insert(T entry)
{
using (var connection = GetOpenIDbConnection())
Insert(connection, entry);
}
public void Update(T entry)
{
using (var connection = GetOpenIDbConnection())
Update(connection, entry);
}
public void Delete(Predicate<T> predicate)
{
using (var connection = GetOpenIDbConnection())
Delete(connection, predicate);
}
public T Retrieve(Predicate<T> predicate)
{
using (var connection = GetOpenIDbConnection())
return Retrieve(connection, predicate);
}
public bool Exists(Predicate<T> predicate)
{
using (var connection = GetOpenIDbConnection())
return Exists(predicate);
}
public IEnumerable<T> RetrieveAll()
{
using (var connection = GetOpenIDbConnection())
return RetrieveAll(connection);
}
#endregion
#region IDisposable Behavior(s)
public void Dispose()
{
Dispose(true);
GC.SuppressFinalize(this);
}
#endregion
#region Protected Behavior(s)
protected virtual void Dispose(Boolean disposing)
{
if(disposed)
return;
if (disposing)
{
if(transaction != null)
transaction.Dispose();
if(connection != null)
connection.Dispose();
}
disposed = true;
}
#endregion
#region Private Behavior(s)
String GenerateInsertText()
{
String statement = "INSERT INTO {0}({1}) VALUES ({2})";
//Do first entry here becasse its unique input.
String columnNames = Properties.First();
String delimiter = ", ";
String bph = ":a";
String placeHolders = bph + 0;
//Start # 1 since first entry is already done
for (int i = 1; i < Properties.Count; i++)
{
columnNames += delimiter + Properties[i];
placeHolders += delimiter + bph + i;
}
statement = String.Format(statement, typeof(T).Name, columnNames, placeHolders);
return statement;
}
String GenerateUpdateText()
{
String bph = ":a";
String cvpTemplate = "{0} = {1}";
String statement = "UPDATE {0} SET {1} WHERE {2}";
//Can only set Cols that are not a primary Keys, Get those Columns
var Settables = Properties.Where(p => !PrimaryKeys.Any(k => k == p)).ToList();
String cvp = String.Format(cvpTemplate, Settables.First(), bph + 0);
String condition = String.Format(cvpTemplate, PrimaryKeys.First(), bph + Settables.Count);
//These are the values to be set | Start # 1 since first entry is done above.
for (int i = 1; i < Settables.Count; i++)
cvp += ", " + String.Format(cvpTemplate, Settables[i], bph + i);
//This creates the conditions under which the values are set. | Start # 1 since first entry is done above.
for (int i = Settables.Count + 1; i < Properties.Count; i++)
condition += " AND " + String.Format(cvpTemplate, PrimaryKeys[i - Settables.Count], bph + i);
statement = String.Format(statement, typeof(T).Name, cvp, condition);
return statement;
}
String GenerateDeleteText()
{
String bph = ":a";
String cvpTemplate = "{0} = {1}";
String statement = "DELETE FROM {0} WHERE {1}";
String condition = String.Format(cvpTemplate, PrimaryKeys.First(), bph + 0);
for (int i = 1; i < PrimaryKeys.Count; i++)
condition += " AND " + String.Format(cvpTemplate, PrimaryKeys[i], bph + i);
statement = String.Format(statement, typeof(T).Name, condition);
return statement;
}
String GenerateSelectText()
{
String statement = "SELECT * FROM {0}";
statement = String.Format(statement, typeof(T).Name);
return statement;
}
#endregion
#region Destructor
~OracleRepository()
{
Dispose(false);
}
#endregion
}
The second implementation for in memory operation is this :
public class InMemoryRepository<T> : IRepository<T> where T : IRepositoryEntry, new()
{
//RepositoryEntryBase,
public event EventHandler<RepositoryOperationEventArgs> InsertEvent;
public event EventHandler<RepositoryOperationEventArgs> UpdateEvent;
public event EventHandler<RepositoryOperationEventArgs> DeleteEvent;
public IList<String> PrimaryKeys { get; protected set; }
List<T> data;
public InMemoryRepository()
{
PrimaryKeys = new List<String>(new T().GetPrimaryKeys());
data = new List<T>();
}
public void Insert(T Entry)
{
if (Get(Entry) != null)
throw new Exception("Duplicate Entry - Identical Key already exists");
data.Add(Entry);
if (InsertEvent != null)
InsertEvent(this, new RepositoryOperationEventArgs() { Entry = Entry });
}
public void Update(T Entry)
{
var obj = Get(Entry);
if (obj == null)
throw new Exception("Object does not exist");
obj = Entry;
if (UpdateEvent != null)
UpdateEvent(this, new RepositoryOperationEventArgs() { Entry = obj });
}
public void Delete(Predicate<T> predicate)
{
data.RemoveAll(predicate);
if (DeleteEvent != null)
DeleteEvent(this, new RepositoryOperationEventArgs() { Entry = null });
}
public bool Exists(Predicate<T> predicate)
{
return data.Exists(predicate);
}
public T Retrieve(Predicate<T> predicate)
{
return data.FirstOrDefault(new Func<T, bool>(predicate));
}
public IEnumerable<T> RetrieveAll()
{
return data.ToArray();
}
T Get(T Entry)
{
//Returns Entry based on Identical PrimaryKeys
Type entryType = typeof(T);
var KeyPropertyInfo = entryType.GetProperties().Where(p => PrimaryKeys.Any(p2 => p2 == p.Name));
foreach (var v in data)
{
//Assume the objects are identical by default to prevent false positives.
Boolean AlreadyExists = true;
foreach (var property in KeyPropertyInfo)
if (!property.GetValue(v).Equals(property.GetValue(Entry)))
AlreadyExists = false;
if (AlreadyExists)
return v;
}
return default(T);
}
}
Whew, that was a lot of code. Now there are a few non standard functions. These are what they all are:
public static class IDbConnectionExtensions
{
public static IDbCommand CreateCommand(this IDbConnection Conn, string CommandText, params object[] Parameters)
{
var Command = Conn.CreateCommand();
Command.CommandText = CommandText;
foreach (var p in Parameters ?? new object[0])
Command.Parameters.Add(Command.CreateParameter(p));
return Command;
}
public static IDbDataParameter CreateParameter(this IDbCommand Command, object Value)
{
var Param = Command.CreateParameter();
Param.Value = Value;
return Param;
}
public static PlexQueryResult Query(this IDbConnection conn, String CommandText, params object[] Arguments)
{
using (var Comm = conn.CreateCommand(CommandText, Arguments))
using (var reader = Comm.ExecuteReader(CommandBehavior.KeyInfo))
return new PlexQueryResult(reader);
}
public static int NonQuery(this IDbConnection conn, String CommandText, params object[] Arguments)
{
using (var Comm = conn.CreateCommand(CommandText, Arguments))
return Comm.ExecuteNonQuery();
}
public static IDbConnection OpenConnection(this IDbConnection connection)
{
connection.Open();
return connection;
}
}
Now, how we tie everything together is simple, this one I'm writing off the top of my head with no editor so please bear with me :
Lets say we have the following class which inherits from IRepostoryEntry:
//Feel free to ignore RepostoryEntryBase
public class COMPANIES : RepositoryEntryBase, IRepositoryEntry
{
public string KEY { get; set; } //KEY VARCHAR2(20) N
public int COMPANY_ID { get; set; } //COMPANY_ID NUMBER(10) N
public string DESCRIPTION { get; set; }//DESCRIPTION VARCHAR2(100) N
public COMPANIES() : base ()
{
primaryKeys.Add("COMPANY_ID");
}
}
public abstract class DbRepository : IDbRepository
{
public Dictionary<Type,IRepository> Repositories { get;set; }
public DbRepository(){
Repositories = new Dictionary<Type,IRepository>();
Repositories .add(typeof(COMPANIES)),new OracleRepository<COMPANIES>());
}
public object Insert(object entity)
{
if(!(entity is IRepositoryEntry))
throw new NotSupportedException("You are bad and you should feel bad");
if(!Repositories.ContainsKey(entity.GetType()))
throw new NotSupportedException("Close but no cigar");
Dictionary[entity.GetType()].Insert(entity);
}
//You can add additional operations here:
}
That had to be the longest answer I ever wrote:
I built this DLL to get my jump started on this method of storing data; however, its really intended for Oracle. That said, its easy to adapt to your needs.

My suggestion is to not fight WCF constraints and possibly making your solution more complex than necessary. Instead, try using code generators or rollout your own to generate the numerous service contracts your application requires.

In your current implementation, you do not have your OperationContract attributes set on your contract interface.
Try something like this:
public abstract class DbRepository : IDbRepository
{
[OperationalContract(Name="Insert")]
public TEntity Insert<TEntity>(TEntity entity) where TEntity : class
{
_context.Entry(entity).State = EntityState.Added;
return entity;
}
[OperationalContract(Name="Update")]
public TEntity Update<TEntity>(TEntity entity) where TEntity : class
{
_context.Entry(entity).State = EntityState.Modified;
return entity;
}
}
It probably seems redundant, but I believe the generics mess with operational names accidentally and you are required to specify them.

Will WCF generate a WSDL for that contract and allow you to host the service? Is the problem you have just down to serialization and known types? If so you may want to look at the SharedTypeResolver in this blog post. It's a pretty simple and awesome piece of magic that allows you to transparently pass any subclass of a data contract without having to declare it, so long as the type is shared between both client and server.
You could then dispense with the generics and simply talk about things as TEntity. Internally in the service you could map the call to your generic service implementations; think of the WCF service as a non-generic facade to expose your generic classes. The caller will know what type to expect because they gave it to you in the first place, so could cast. You could provide a client that puts a generic wrapper around this if casting offends.

Since you are using a BasicHttpBinding, I am going to assume you are sending this over the web. I'm also going to assume that you are using SOAP/XML. If this is the case try something like this:
[ServiceContract]
public interface IDbRepository
{
[OperationContract]
XElement Insert(XElement entity);
[OperationContract]
XElement Update(XElement entity);
}
Now all you have to do is parse the XML you receive and return whatever XML you see fit! I did something similar where I had an abstract base class that has 2 methods one to generate XML to represent the object and one to parse XML to populate the object's properties. One drawback to this is that your interface implementation will still need to know about all the types of objects in the class hierarchy.

Related

Getting startes Repository pattern with Dapper (WinForms) [duplicate]

This question already has answers here:
How to implement Generic Repository Design Pattern with Dapper?
(3 answers)
Closed 7 months ago.
I want to start using Dapper and also try to use Repository pattern.
But I don't understand how to do.
I tried this, but this is with EF Repository Pattern C#
So can anybody tell me a simple example for a gerneric repository pattern and the usage of Dapper?
What I tried/found:
IGenericRepository.cs
public interface IGenericRepository<T>
{
Task<IEnumerable<T>> GetAllAsync();
Task DeleteRowAsync(Guid id);
Task<T> GetAsync(Guid id);
Task<int> SaveRangeAsync(IEnumerable<T> list);
Task UpdateAsync(T t);
Task InsertAsync(T t);
}
GenericRepository.cs
public abstract class GenericRepository<T> : IGenericRepository<T> where T : class
{
private readonly string _tableName;
protected GenericRepository(string tablename)
{
_tableName = tablename;
}
private MySqlConnection SqlConnection()
{
return new MySqlConnection("xxx");
}
private IDbConnection CreateConnection()
{
var conn = SqlConnection();
conn.Open();
return conn;
}
private IEnumerable<PropertyInfo> GetProperties => typeof(T).GetProperties();
public async Task DeleteRowAsync(Guid id)
{
using (var connection = CreateConnection())
{
await connection.ExecuteAsync($"DELETE FROM {_tableName} WHERE Id=#Id", new { Id = id });
}
}
public async Task<IEnumerable<T>> GetAllAsync()
{
using (var connection = CreateConnection())
{
return await connection.QueryAsync<T>($"SELECT * FROM {_tableName}");
}
}
public async Task InsertAsync(T t)
{
var insertQuery = GenerateInsertQuery();
using (var connection = CreateConnection())
{
await connection.ExecuteAsync(insertQuery, t);
}
}
public async Task<int> SaveRangeAsync(IEnumerable<T> list)
{
var inserted = 0;
var query = GenerateInsertQuery();
using (var connection = CreateConnection())
{
inserted += await connection.ExecuteAsync(query, list);
}
return inserted;
}
private string GenerateInsertQuery()
{
var insertQuery = new StringBuilder($"INSERT INTO {_tableName} ");
insertQuery.Append("(");
var properties = GenerateListOfProperties(GetProperties);
properties.ForEach(prop => { insertQuery.Append($"[{prop}],"); });
insertQuery
.Remove(insertQuery.Length - 1, 1)
.Append(") VALUES (");
properties.ForEach(prop => { insertQuery.Append($"#{prop},"); });
insertQuery
.Remove(insertQuery.Length - 1, 1)
.Append(")");
return insertQuery.ToString();
}
private string GenerateUpdateQuery()
{
var updateQuery = new StringBuilder($"UPDATE {_tableName} SET ");
var properties = GenerateListOfProperties(GetProperties);
properties.ForEach(property =>
{
if (!property.Equals("Id"))
{
updateQuery.Append($"{property}=#{property},");
}
});
updateQuery.Remove(updateQuery.Length - 1, 1);
updateQuery.Append(" WHERE Id=#Id");
return updateQuery.ToString();
}
private static List<string> GenerateListOfProperties(IEnumerable<PropertyInfo> listOfProperties)
{
return (from prop in listOfProperties
let attributes = prop.GetCustomAttributes(typeof(DescriptionAttribute), false)
where attributes.Length <= 0 || (attributes[0] as DescriptionAttribute)?.Description != "ignore"
select prop.Name).ToList();
}
public async Task UpdateAsync(T t)
{
var updateQuery = GenerateUpdateQuery();
using (var connection = CreateConnection())
{
await connection.ExecuteAsync(updateQuery, t);
}
}
public async Task<T> GetAsync(Guid id)
{
using (var connection = CreateConnection())
{
var result = await connection.QuerySingleOrDefaultAsync<T>($"SELECT * FROM {_tableName} WHERE Id=#Id", new { Id = id });
if (result == null)
throw new KeyNotFoundException($"{_tableName} com o id {id} não foi encontrado");
return result;
}
}
}
Person.cs
public class Person
{
public Guid Id { get; set; }
public string Name { get; set; }
public int Age { get; set; }
}
PersonRepository.cs
public class PersonRepository : GenericRepository<Person>
{
public PersonRepository(string tablename) : base(tablename) { }
}
Now my problem:
What should I do to fill a dataGridView with all persons?
Is there a need to change something in the PersonRepository.cs file? What?
Ans how does the code in my form look like? e.g. dataGridView1.DataSource = ... and also filter this?
And for example, if I add another Class Car, what points I have to do?
Add another CarRepository.cs and Car.cs? Anything else?
Kind regards!
First of all you need to understand the differences between EF and Dapper. Basically Dapper is a micro-ORM that has minimal features compared with EF. Entity Framework has a lot of features included along with performance improvements and others (more details here).
As the dapper is a Micro-ORM you don't have the same features available in EF. You can use dapper.contrib that has some abstractions that helps you with generic repository.
I have an example in my github an application that use dapper in repository (without dapper.contrib) with clean arch.

Azure Table Generics - Operation not supported on ExecuteQuery

I'm trying to create a generic implementation for Azure Tables. The ploblem is that when I use the ExecuteQuery function it always return the following error to me:
Error = Unable to evaluate the expression. Operation not supported.
Unknown error: 0x80070057.
I do can run the Execute function for TableOperation Delete, Update, Create, Retrieve for example
That's the classes I created on my project:
Base class
public abstract class TableEntityBase : TableEntity
{
private string TableName { get; set; }
public TableEntityBase(string tableName)
{
TableName = tableName;
}
public string GetTableName() => TableName;
}
Then its Interface
public interface ITableEntityBase<T> where T : TableEntityBase
{
TableResult InsertOrMerge(T entity);
TableResult Delete(T id);
IEnumerable<T> GetByExpression(string query);
IEnumerable<T> GetAll();
}
And the classes for the tables I have
public class Mapping : TableEntityBase
{
public Mapping() :
base(EntityLogicalName)
{
}
private const string EntityLogicalName = "Mapping";
public string Source { get; set; }
}
public interface IMapping : ITableEntityBase<Mapping>
{
}
At least, my service class
public class TableEntityBaseServices<T> : ITableEntityBase<T> where T : TableEntityBase, new()
{
protected CloudTable _cloudTable;
protected string tableName = ((T)Activator.CreateInstance(typeof(T))).GetTableName();
public TableEntityBaseServices()
{
IConfiguration appSettings = AppSettings.GetAppSettings().GetSection("ConnectionStrings");
_cloudTable = CloudStorageAccountExtensions.CreateCloudTableClient(CloudStorageAccount.Parse(appSettings.GetSection("AzureConfig").Value)).GetTableReference(tableName);
_cloudTable.CreateIfNotExistsAsync();
}
//...Other methods that work well
IEnumerable<T> ITableEntityBase<T>.GetByExpression(string query)
{
return _cloudTable.ExecuteQuery<T>(new TableQuery<T>().Where(query)); //Error here: Unable to evaluate the expression. Operation not supported.
}
}
The Mapping service then is:
public class MappingServices : TableEntityBaseServices<Mapping>, IMapping { }
The method call should be simple
static async Task Main(string[] args)
{
var serviceProvider = new ServiceCollection()
.AddSingleton<IMapping, MappingServices>()
.BuildServiceProvider();
IMapping _mappingService = serviceProvider.GetRequiredService<IMapping>();
try
{
IEnumerable<Mapping> mappings = _mappingService.GetByExpression(TableQuery.GenerateFilterCondition("PartitionKey", QueryComparisons.Equal, "test1"));
}
catch (Exception e)
{
throw e;
}
}
I saw this answer to a question but in my case I don't know what I need to do, since I already define the new() on my service class. Where did I messed up?
Thanks in advance :)
Please use the package Microsoft.Azure.Cosmos.Table 1.0.8 if you're not.
I'm testing with your code, no error occurs. The test result as below:

Get needed properties of a class

I have about 15 entities for which I have to get id and name properties. First, I check whether the properties can be pulled from local cache. If not, then I fetch them by code from DB and save in cache.
Here's my code just for two entities:
public class GetParamsService : IGetParamsService
{
private readonly IMemoryCache _cache;
private readonly MemoryCacheEntryOptions _cacheOptions;
private readonly IDealTypeRepository _dealTypeRepository;
private readonly ICurrencyRepository _currencyRepository;
public GetParamsService(IMemoryCache memoryCache,
IDealTypeRepository dealTypeRepository,
ICurrencyRepository currencyRepository)
{
_cache = memoryCache;
_cacheOptions = new MemoryCacheEntryOptions()
.SetSlidingExpiration(TimeSpan.FromHours(2));
_dealTypeRepository = dealTypeRepository;
_currencyRepository = currencyRepository;
}
public async Task<(int idDealType, string dealTypeName)> GetDealTypeParams(
string dealTypeCode)
{
if (!_cache.TryGetValue(CacheKeys.IdDealType, out int idDealType)
| !_cache.TryGetValue(CacheKeys.DealTypeName, out string dealTypeName))
{
var dealType = await _dealTypeRepository
.Get(x => x.Code == dealTypeCode, dealTypeCode);
idDealType = dealType.IdDealType;
dealTypeName = dealType.Name;
_cache.Set(CacheKeys.IdDealType, idDealType, _cacheOptions);
_cache.Set(CacheKeys.DealTypeName, dealTypeName, _cacheOptions);
}
return (idDealType, dealTypeName);
}
public async Task<(int idCurrency, string currencyName)> GetCurrencyParams(
string currencyCode)
{
if (!_cache.TryGetValue(CacheKeys.IdCurrency, out int idCurrency)
| !_cache.TryGetValue(CacheKeys.CurrencyName, out string currencyName))
{
var currency = await _currencyRepository
.Get(x => x.Code == currencyCode, currencyCode);
idCurrency = currency.IdCurrency;
currencyName = currency.Name;
_cache.Set(CacheKeys.IdCurrency, idCurrency, _cacheOptions);
_cache.Set(CacheKeys.CurrencyName, currencyName, _cacheOptions);
}
return (idCurrency, currencyName);
}
}
So, the methods GetDealTypeParams and GetCurrencyParams are pretty much the same and I want to make one generic method instead of many similar methods. I guess it makes sense.
The problem is that I don't know how to get "the right properties for given entity" in CacheKeys class:
public static class CacheKeys
{
public static string IdDealType => "_IdDealType";
public static string DealTypeName => "_DealTypeName";
public static string IdCurrency => "_IdCurrency";
public static string CurrencyName => "_CurrencyName";
// ...
}
Every repository is inheritted from GenericRepository with Get method:
public class DealTypeRepository : GenericRepository<DealTypeEntity>, IDealTypeRepository
{
public DealTypeRepository(DbContextOptions<MyContext> dbContextOptions)
: base(dbContextOptions)
{
}
}
public class GenericRepository<TEntity> where TEntity : class
{
private readonly DbContextOptions<MyContext> _dbContextOptions;
public GenericRepository(DbContextOptions<MyContext> dbContextOptions)
{
_dbContextOptions = dbContextOptions;
}
public async Task<TEntity> Get(Expression<Func<TEntity, bool>> predicate, string code)
{
try
{
using (var db = new MyContext(_dbContextOptions))
{
using (var tr = db.Database.BeginTransaction(
IsolationLevel.ReadUncommitted))
{
var entity = await db.Set<TEntity>().AsNoTracking()
.FirstAsync(predicate);
tr.Commit();
return entity;
}
}
}
catch (Exception e)
{
throw new Exception("Error on getting entity by code: {code}");
}
}
}
Can you please guide me how can I retrieve the needed properties of CacheKeys class to write a generic method ? I guess it can be easily done with reflection.
UPDATE:
I'm not sure whether I have to try one generic method as every entity has Id property with its own name (for example, IdDealType for dealType, IdCurrency for currency)
Before I start: This solution assumes that all your entities follow the naming conventions you showed on your sample code.
First, it would be better if you had a repository exclusive to this service, where it would be possible to query by any entity type. There, you would use your convention to get those properties names, and you could query them by using EF.Property. As all your queries seem to be on the Code column, we can also simplify the parameters on that repo's method.
public class ParamRepository : IParamRepository
{
private readonly DbContextOptions<MyContext> _dbContextOptions;
public ParamRepository(DbContextOptions<MyContext> dbContextOptions)
{
_dbContextOptions = dbContextOptions;
}
public async Task<(int id, string name)> GetParamsByCode<TEntity>(string code) where TEntity : class
{
string entityName = typeof(TEntity).Name;
string idProp = $"Id{entityName}";
string nameProp = $"{entityName}Name";
try
{
using (var db = new MyContext(_dbContextOptions))
{
var entity = await db.Set<TEntity>().AsNoTracking()
.Where(p => EF.Property<string>(p, "Code") == code)
.Select(p => new { Id = EF.Property<int>(p, idProp), Name = EF.Property<string>(p, nameProp)})
.FirstAsync();
return (id: entity.Id, name: entity.Name);
}
}
catch (Exception e)
{
throw new Exception("Error on getting entity by code: {code}");
}
}
}
You would also need to refactor your cache keys to be created from conventions:
public static class CacheKeys
{
public static string GetIdKey<TEntity>() => $"_Id{typeof(TEntity).Name}";
public static string GetNameKey<TEntity>() => $"_{typeof(TEntity).Name}Name";
}
Then, it gets easy on the GetParamsService:
public class GetParamsService
{
private readonly IMemoryCache _cache;
private readonly MemoryCacheEntryOptions _cacheOptions;
private readonly IParamRepository _paramRepository;
public GetParamsService(IMemoryCache memoryCache,
IParamRepository paramRepository)
{
_cache = memoryCache;
_cacheOptions = new MemoryCacheEntryOptions()
.SetSlidingExpiration(TimeSpan.FromHours(2));
_paramRepository = paramRepository;
}
public async Task<(int id, string name)> GetParams<TEntity>(string code) where TEntity : class
{
string cacheIdKey = CacheKeys.GetIdKey<TEntity>();
string cacheNameKey = CacheKeys.GetNameKey<TEntity>();
if (!_cache.TryGetValue(cacheIdKey, out int cacheId)
| !_cache.TryGetValue(cacheNameKey, out string cacheName))
{
var param = await _paramRepository.GetParamsByCode<TEntity>(code);
cacheId = param.id;
cacheName = param.name;
_cache.Set(cacheIdKey, cacheId, _cacheOptions);
_cache.Set(cacheNameKey, cacheName, _cacheOptions);
}
return (cacheId, cacheName);
}
}

Create a Singleton Factory for a Class that takes parameters / arguements

First of all I read this on an article - which basically tells me I should not be using a singleton at all -
Most commonly, singletons don't allow any parameters to be specified when creating the instance - as otherwise a second request for an instance but with a different parameter could be problematic! (If the same instance should be accessed for all requests with the same parameter, the factory pattern is more appropriate.)
Since I need parameters, and same instances with same parameters - I concluded I need a factory pattern.
But I was unable to find a good factory pattern implementation anywhere.
Kindly direct me if you find any good c# singleton factory pattern implementation with parameters
Ok I am going to try and be very specific here... hope this explains my situation.
Alternate methods are most welcome. I just combined a lot of implementations - my understanding may be off.
So I have a class 'A'. It is a class used to connect to a database - Database connection.
The connection needs 4 parameters & the constraints are:
I need to have multiple connections possible - with different databases (parameters differ)
I need only 1 instance of a specific connection - a singleton with parameters which are same (in my understanding)
I will need a factory model as per the article mentioned above and also to limit the number of connections, close the connection after a timeout etc.
On this basis I need a singleton factory with paramenters/arguements... I assume
So the class A is going to look something like this
<which access modifier ?> Class A {
private Class A(string hostname, string port, string username, string pw_hash) {
//create a new instance with the specified parameters
}
//other methods on the connection
protected void close() {
//close the connection
}
}
public class AFactory//should it inherit class A?? {
private IList<A> connections = new List<A>();
private AFactory()
{
//do something
}
private static readonly Lazy<AFactory> lazy
= new Lazy<AFactory>(() => new AFactory());
public static AFactory Instance { get { return lazy.Value; } }
public A getA(string hostname, string service, string username, string pw_hash)
{
foreach (A a in A)
{
if (a.hostname == hostname && a.service == service && a.username == username)
return a;
}
A d = new A(hostname, service, username, pw_hash);
connections.Add(d);
return d;
}
Now this works well and good as long as the class A constructor is public - but It kind of defeats the purpose of a singleton.
What do I need to do to get this code to work.
I need only 1 instance of class A for the specified parameters.
Thanks
Indrajit
Factory is used to generate object rather than manage object. I think a DB connection manager is more suitable in your situation. You can declare the manager as singleton. For individual connection you can use internal class/struct.
See below example:
class DBConnectionManager
{
struct Connection
{
public string Hostname;
public string ServerName;
public string UserName;
public string Password;
public void Connect()
{
}
public void Close()
{
}
}
private static s_instance;
public static DBConnectionManager Instance
{
get {return s_instance; }
}
private List<Connection> m_connections;
public Connection GetConnection(string hostname, string serverName, string userName, string password)
{
// if already exist in m_connections
// return the connection
// otherwise create new connection and add to m_connections
}
public void CloseConnection(string hostname, string serverName, string userName, string password)
{
// if find it in m_connections
// then call Close()
}
public void CloseAll()
{
//
}
}
So I have done this and it works... can you tell me if it is correct. And also is it Thread-Safe?
public Class A
{
private A(string hostname, string port, string username, string pw_hash) {
//create a new instance with the specified parameters
}
//other methods on the connection
protected void close() {
//close the connection
}
public class AFactory
{
private IList<A> connections = new List<A>();
private AFactory()
{
//do something
}
private static readonly Lazy<AFactory> lazy
= new Lazy<AFactory>(() => new AFactory());
public static AFactory Instance { get { return lazy.Value; } }
public A getA(string hostname, string service, string username, string pw_hash)
{
foreach (A a in connections)
{
if (a.hostname == hostname && a.service == service && a.username == username)
return a;
}
A d = new A(hostname, service, username, pw_hash);
connections.Add(d);
return d;
}
}
}
I am using it like this:
A.AFactory fact = A.AFactory.Instance;
A conn = fact.getA(a, b, c, d);
A conn2 = fact.getA(e, f, g, h);
Is there something glaringly wrong with this implementation?
you could try this:
public static class Singlett<Param,T>
where T : class
{
static volatile Lazy<Func<Param, T>> _instance;
static object _lock = new object();
static Singlett()
{
}
public static Func<Param, T> Instance
{
get
{
if (_instance == null)
{
_instance = new Lazy<Func<Param, T>>(() =>
{
lock (Singlett<Param,T>._lock)
{
try
{
ConstructorInfo constructor = null;
Type[] methodArgs = { typeof(Param) };
constructor = typeof(T).GetConstructor(BindingFlags.Instance | BindingFlags.NonPublic, null, methodArgs, null);// Binding flags excludes public constructors.
if (constructor == null)
{
constructor = typeof(T).GetConstructor(BindingFlags.Public, null, methodArgs, null);
if (constructor == null)
return delegate(Param o) { return (T)Activator.CreateInstance(typeof(T), new object[] { o }); };
}
return delegate(Param o) { return (T)constructor.Invoke(new object[] { o }); };
}
catch (Exception exception)
{
throw exception;
}
}
});
}
return _instance.Value;
}
}
}
then to use it:
instead of
int i = 10;
MyClass class = new MyClass(i);
you can write:
int i = 10;
MyClass class = Singlett<int,MyClass>.Instance(i);
Try this:
This interface is exposed from the factory initializer and contains the exposed methods and properties.
public interface IDatabase
{
string ConnectionString { get; set; }
IDataReader ExecuteSql(string sql);
}
Factory base abstract class where you can perform common features to different types of database factories.
public abstract class FactoryBase
{
public FactoryBase() { }
public abstract IDatabase GetDataLayer();
}
Concrete sql class that contains your calls. Have a look at the ExecuteSql method. The connection is self contained in the command so you don't have to worry about opening and closing and disposing of it.
public class SQL : IDatabase
{
private string m_ConnectionString = string.Empty;
public string ConnectionString
{
get { return m_ConnectionString; }
set { m_ConnectionString = value; }
}
public IDataReader ExecuteSql(string sql)
{
using (var command = new SqlCommand(sql, new SqlConnection(ConnectionString)) { CommandType = CommandType.Text, CommandText = sql, CommandTimeout = 0 })
{
if (command.Connection.State != ConnectionState.Open) command.Connection.Open();
return command.ExecuteReader();
}
}
}
Sql factory class that creates an instance of the Sql concrete class.
class SQLFactory : FactoryBase
{
public override IDatabase GetDataLayer()
{
return new SQL();
}
}
The factory initializer class that a developer will use to pass in a type of factory and it will return the IDatabase.
public static class FactoryInitializer
{
public static IDatabase LoadFactory<T>(string connectionstring) where T : FactoryBase, new()
{
var factory = new T();
var data = factory.GetDataLayer();
data.ConnectionString = connectionstring;
return data;
}
}
Then use it as:
var factory = FactoryInitializer.LoadFactory<SQLFactory>(connectionString);
factory.ExecuteSql("SELECT ...");
You can then create may be an OracleFactory and an Oracle concrete class and use it the same way.

Entity framework uses a lot of memory

Here is a image from the ANTS memory profiler. It seens that there are a lot of objects hold in memory. How can I find what I am doing wrong?
**UPDATE**
Here is my repository classes:
public class Repository<T> : IRepository<T> where T : class, IDataEntity
{
ObjectContext _context;
IObjectSet<T> _objectSet;
readonly string _entitySetName;
readonly string[] _keyNames;
private ObjectContext Context
{
get
{
if (_context == null)
{
_context = GetCurrentUnitOfWork<EFUnitOfWork>().Context;
}
return _context;
}
}
private IObjectSet<T> ObjectSet
{
get
{
if (_objectSet == null)
{
_objectSet = this.Context.CreateObjectSet<T>();
}
return _objectSet;
}
}
public TUnitOfWork GetCurrentUnitOfWork<TUnitOfWork>() where TUnitOfWork : IUnitOfWork
{
return (TUnitOfWork)UnitOfWork.Current;
}
public virtual IEnumerable<T> GetQuery()
{
return ObjectSet;
}
public virtual IEnumerable<T> GetQuery(params Expression<Func<T, object>>[] includes)
{
return ObjectSet.IncludeMultiple(includes);
}
public virtual IEnumerable<T> GetQuery(
IEnumerable<Expression<Func<T, bool>>> filters,
Func<IQueryable<T>, IOrderedQueryable<T>> orderBy,
IEnumerable<Expression<Func<T, object>>> includes)
{
IQueryable<T> _query = ObjectSet;
if (filters != null)
{
foreach (var filter in filters)
{
_query = _query.Where(filter);
}
}
if (includes != null && includes.Count() > 0)
{
_query = _query.IncludeMultiple(includes.ToArray());
}
if (orderBy != null)
{
_query = orderBy(_query);
}
return _query;
}
public virtual IPaged<T> GetQuery(
IEnumerable<Expression<Func<T, bool>>> filters,
Func<IQueryable<T>, IOrderedQueryable<T>> orderBy,
int pageNumber, int pageSize,
IEnumerable<Expression<Func<T, object>>> includes)
{
IQueryable<T> _query = ObjectSet;
if (filters != null)
{
foreach (var filter in filters)
{
_query = _query.Where(filter);
}
}
if (orderBy != null)
{
_query = orderBy(_query);
}
IPaged<T> page = new Paged<T>(_query, pageNumber, pageSize, includes);
return page;
}
public virtual void Insert(T entity)
{
this.ObjectSet.AddObject(entity);
}
public virtual void Delete(T entity)
{
if (entity is ISoftDeletable)
{
((ISoftDeletable)entity).IsDeleted = true;
//Update(entity);
}
else
{
this.ObjectSet.DeleteObject(entity);
}
}
public virtual void Attach(T entity)
{
ObjectStateEntry entry = null;
if (this.Context.ObjectStateManager.TryGetObjectStateEntry(entity, out entry) == false)
{
this.ObjectSet.Attach(entity);
}
}
public virtual void Detach(T entity)
{
ObjectStateEntry entry = null;
if (this.Context.ObjectStateManager.TryGetObjectStateEntry(entity, out entry) == true)
{
this.ObjectSet.Detach(entity);
}
}
}
Now, if I have class A that holds records from table A, I also create class:
public class ARepository:BaseRepository<A> {
// Implementation of A's queries and specific db operations
}
Here is my EFUnitOfWork class:
public class EFUnitOfWork : IUnitOfWork, IDisposable
{
public ObjectContext Context { get; private set; }
public EFUnitOfWork(ObjectContext context)
{
Context = context;
context.ContextOptions.LazyLoadingEnabled = true;
}
public void Commit()
{
Context.SaveChanges();
}
public void Dispose()
{
if (Context != null)
{
Context.Dispose();
}
GC.SuppressFinalize(this);
}
}
And UnitOfWork class:
public static class UnitOfWork
{
private const string HTTPCONTEXTKEY = "MyProj.Domain.Business.Repository.HttpContext.Key";
private static IUnitOfWorkFactory _unitOfWorkFactory;
private static readonly Hashtable _threads = new Hashtable();
public static void Commit()
{
IUnitOfWork unitOfWork = GetUnitOfWork();
if (unitOfWork != null)
{
unitOfWork.Commit();
}
}
public static IUnitOfWork Current
{
get
{
IUnitOfWork unitOfWork = GetUnitOfWork();
if (unitOfWork == null)
{
_unitOfWorkFactory = ObjectFactory.GetInstance<IUnitOfWorkFactory>();
unitOfWork = _unitOfWorkFactory.Create();
SaveUnitOfWork(unitOfWork);
}
return unitOfWork;
}
}
private static IUnitOfWork GetUnitOfWork()
{
if (HttpContext.Current != null)
{
if (HttpContext.Current.Items.Contains(HTTPCONTEXTKEY))
{
return (IUnitOfWork)HttpContext.Current.Items[HTTPCONTEXTKEY];
}
return null;
}
else
{
Thread thread = Thread.CurrentThread;
if (string.IsNullOrEmpty(thread.Name))
{
thread.Name = Guid.NewGuid().ToString();
return null;
}
else
{
lock (_threads.SyncRoot)
{
return (IUnitOfWork)_threads[Thread.CurrentThread.Name];
}
}
}
}
private static void SaveUnitOfWork(IUnitOfWork unitOfWork)
{
if (HttpContext.Current != null)
{
HttpContext.Current.Items[HTTPCONTEXTKEY] = unitOfWork;
}
else
{
lock(_threads.SyncRoot)
{
_threads[Thread.CurrentThread.Name] = unitOfWork;
}
}
}
}
Here is how I use this:
public class TaskPriceRepository : BaseRepository<TaskPrice>
{
public void Set(TaskPrice entity)
{
TaskPrice taskPrice = GetQuery().SingleOrDefault(x => x.TaskId == entity.TaskId);
if (taskPrice != null)
{
CommonUtils.CopyObject<TaskPrice>(entity, ref taskPrice);
}
else
{
this.Insert(entity);
}
}
}
public class BranchRepository : BaseRepository<Branch>
{
public IList<Branch> GetBranchesList(Guid companyId, long? branchId, string branchName)
{
return Repository.GetQuery().
Where(b => companyId == b.CompanyId).
Where(b => b.IsDeleted == false).
Where(b => !branchId.HasValue || b.BranchId.Equals(branchId.Value)).
Where(b => branchName == null || b.BranchName.Contains(branchName)).
ToList();
}
}
[WebMethod]
public void SetTaskPrice(TaskPriceDTO taskPrice)
{
TaskPrice tp = taskPrice.ToEntity();
TaskPriceRepository rep = new TaskPriceRepository();
rep.Set(tp);
UnitOfWork.Commit();
}
[WebMethod]
public IList<Branch> GetBranchesList()
{
BranchRepository rep = new BranchRepository();
return rep.GetBranchesList(m_User.UserCompany.CompanyId, null, null).ToList();
}
I hope this is enough info to help me solving the problem. Thanks.
UPDATE 2
There is also UnitOfWorkFactory that initializes UnitOfWork:
public class UnitOfWorkFactory : IUnitOfWorkFactory
{
private static Func<ObjectContext> _objectContextDelegate;
private static readonly Object _lockObject = new object();
public static void SetObjectContext(Func<ObjectContext> objectContextDelegate)
{
_objectContextDelegate = objectContextDelegate;
}
public IUnitOfWork Create()
{
ObjectContext context;
lock (_lockObject)
{
context = _objectContextDelegate();
}
return new EFUnitOfWork(context);
}
}
In order to use this, in the application startup I use structuremap:
ObjectFactory.Initialize(x =>
{
x.For<IUnitOfWorkFactory>().Use<UnitOfWorkFactory>();
x.For(typeof(IRepository<>)).Use(typeof(Repository<>));
});
I have a hunch you don't dispose the context.
I suggest disposing the context whenever you done interacting with database.
Use using statement whenever you create the context.
[Edit]
As far as I can see, you cache and don't dispose your EFUnitOfWork object. It is disposable, which is correct, but I don't see when disposable is called. Seems like you hold a reference to the context for all application run time. Moreover, you create and hold one context per thread, which will make it even worse.
I can't tell you for sure where you should put Dispose or using, as I don't know the usages.
You could put it probably to your Commit method, but I don't know if the Commit called only once during database interaction session.
Also, your design might be overcomplicated.
If I were you, I would:
Find the way to dispose the context using current code, as a short-term solution
Simplify the design, as the long-term solution
If I had time I would do long-term solution right away.
But again, I can't tell if the complexity of your design is justified, as I don't know how big your application is and what it does and what the requirements are.
Couple of things come to my mind:
You aren't probably Disposing the ObjectContext. Make sure all your database codes are within using(var context = CreateObjectContext()) block
You have an N-tier architecture and you are passing entities from the data access layer to upper layer without Detaching the entities from ObjectContext. You need to call ObjectContext.Detach(...)
You are most likely returning a full collection of entities, instead of returning a single enity for single Get operations. For ex, you have queries like from customer in context.Customers select customer instead of doing from customer in context.Customers select customer.FirstOrDefault()
I have had hard time making Entity Framework to work in an N-tier application. It's just not suitable for using in N-tier apps as is. Only EF 4.0 is. You can read about all my adventure in making EF 3 work in an N-tier app.
http://www.codeproject.com/KB/linq/ef.aspx
Does this answer your question?
Do you clear the ObjectContext once in a while. If you keep an ObjectContext alive for a long time this will consume memory related to the size of the EntityDataModel and the number of Entities loaded into this ObjectContext.
I had the same problem in a class which uses dependency injection, so the using() option was not an alternative. My solution was to add DbContextOptions<Context> to the constructor and as a private field to the class. Then, you can call
_db.Dispose();
_db = new BlockExplorerContext(_dBContextOptions);
at appropriate times. This fixed my problem where I was running out of RAM and the application was killed by the OS.

Categories

Resources