Ninject 2.1 ActivationException : Error activating string - c#

I am confused about why I am receiving "Ninject.ActivationException : Error Activating string No matching bindings are available, and the type is not self-bindable" in random bindings. If I leave the binding for IMedia in place it will throw the ActivationException, but if I use the CallbackProvider it works. All of these classes are structured the same with a few different properties. I'm confused as to why ILocationType, IMedia, and IFarmDataContext throw ActivationException while the others do not. Any ideas?
/******************************
* Core Types
******************************/
Bind<IFarmDataContext>().ToProvider(new CallbackProvider<IFarmDataContext>(delegate { return new FarmDataContext(); }));
//Media
Bind<IMedia>().To<Media>(); //blows up
//Bind<IMedia>().ToProvider(new CallbackProvider<IMedia>(delegate { return new Media(); }));
Bind<IMediaType>().To<MediaType>();
Bind<IMediaRelated>().To<MediaRelated>();
//Location
Bind<ILocation>().To<Location>();
Bind<ILocationType>().ToProvider(new CallbackProvider<ILocationType>(delegate { return new LocationType(); }));
Bind<ILocationDetail>().To<LocationDetail>();

Ninject doesn't have a binding for the "String key" to inject in the Media .ctor; When it tries to create a type that depends on Media, it doesn't know how to resolve the dependency and throws. For most types, Ninject would try to create something for you, but string and value types are not self-bindable as we don't have a good default value for them and it can cause havoc on types that use different conventions with primitives.
You need add a parameter value in your bindings (.WithContructorArgument("key", someValue)) or use some kind of provider (which you have done).

Below are the IMedia interface and Media implementation. Media is a partial class with the primary class generated via a LinqToSql DBML file. This is the case for all of the types listed above in the list of bindings.
public interface IMedia : IValidationDictionary, IBaseDescriptor {
/// <summary>
/// Returns a specific Media object specifying
/// if you want the full or lite version
/// </summary>
/// <param name="id"></param>
/// <param name="isLite"></param>
/// <param name="context"></param>
/// <returns></returns>
IMedia Get(long id, bool isLite, DataContext context);
/// <summary>
/// Returns the lite version of the request Media object
/// </summary>
/// <param name="id"></param>
/// <param name="context"></param>
/// <returns></returns>
IMedia Get(long id, DataContext context);
/// <summary>
/// Returns a list of Media Objects
/// </summary>
/// <param name="isLite"></param>
/// <param name="context"></param>
/// <returns></returns>
ValidationList<IMedia> List(bool isLite, DataContext context);
/// <summary>
/// Returns a list of Media Objects with pagination capabilities
/// </summary>
/// <param name="isLite"></param>
/// <param name="skip"></param>
/// <param name="top"></param>
/// <param name="context"></param>
/// <returns></returns>
ValidationList<IMedia> List(bool isLite, int skip, int top, DataContext context);
}
public partial class Media : BaseDescriptor, IMedia {
#region Constructors
public Media(String key, IError error)
: base() {
AddError(key, error);
}
#endregion
#region Properties
public MediaType Type {
set { if (TypeID <= 0) { MediaType = value; } }
get { return MediaType; }
}
#endregion
#region Internal Methods
/// <summary>
/// Truncates relationships as appropriate to reduce over-the-wire size
/// </summary>
protected override void MakeLite() {
MediaRelateds = new EntitySet<MediaRelated>();
}
/// <summary>
/// Validates the values and returns true if there are no problems.
/// </summary>
override public bool Validate() {
this.ClearErrors();
if (this.TypeID <= 0) { this.AddError("TypeID", new Error(ErrorType.VALIDATION, "TypeID is missing or invalid")); }
if (string.IsNullOrEmpty(this.Path)) { this.AddError("Path", new Error(ErrorType.VALIDATION, "Path is missing or invalid")); }
if (this.CreatedOn.Year <= 1900) { this.AddError("CreatedOn", new Error(ErrorType.VALIDATION, "CreatedOn is missing or invalid")); }
if (this.CreatedBy <= 0) { this.AddError("CreatedBy", new Error(ErrorType.VALIDATION, "CreatedBy is missing or invalid")); }
if (this.UpdatedOn.Year <= 1900) { this.AddError("UpdatedOn", new Error(ErrorType.VALIDATION, "UpdatedOn is missing or invalid")); }
if (this.UpdatedBy <= 0) { this.AddError("UpdatedBy", new Error(ErrorType.VALIDATION, "UpdatedBy is missing or invalid")); }
if (!string.IsNullOrEmpty(this.Path) && this.Path.Length > 255) { this.AddError("Path", new Error(ErrorType.VALIDATION, "Path is longer than the maximum of 255 characters")); }
return (this.ErrorCount == 0);
}
#endregion
#region Public Methods
public ValidationList<IMedia> List(bool isLite, DataContext context) {
return List(isLite, 0, 0, context);
}
public ValidationList<IMedia> List(bool isLite, int skip, int top, DataContext context) {
if (context == null) { context = new DataContext(); }
var query = context.Medias.Where(x => x.DeletedOn == null);
List<Media> results;
if (skip > 0 || top > 0) {
if (top > 0) {
if (skip < 0) { skip = 0; }
results = query.OrderBy(x => x.ID).Skip(skip).Take(top).ToList();
} else {
results = query.OrderBy(x => x.ID).Skip(skip).ToList();
}
} else {
results = query.OrderBy(x => x.ID).ToList();
}
var finalResult = new ValidationList<IMedia>(new List<IMedia>());
foreach (var result in results) {
result.IsLite = isLite;
finalResult.Source.Add(result);
}
return finalResult;
}
public IMedia Get(long id, DataContext context) {
return Get(id, false, context);
}
public IMedia Get(long id, bool isLite, DataContext context) {
if (context == null) { context = new DataContext(); }
var results = context.Medias.Where(x => x.ID == id && x.DeletedOn == null).ToList();
var result = (results.Count > 0 ? results[0] : null);
if (result != null) {
result.IsLite = isLite;
}
return result;
}
#endregion
}

Related

StackOverFlow Exception: Could you help me about this issue? [duplicate]

This question already has answers here:
Why does Property Set throw StackOverflow exception?
(3 answers)
Closed 2 years ago.
I designed a class implementing the Builder design pattern. But creating that class using its builder, it throws System Exception that is StackOverFlow Exception. I know its builder is recursive but I only call three times its building chain. What throwing I really don't understand and it doesn't come to me logically. Below is my class that involves its own builder.
SnirgeeDosya sn = new SnirgeeDosya.SnirgeeDosyaBuilder()
.setDosyaId(0)
.setDosyaAdi("Dosya1")
.setUserId(1214)
.setDBFactory(new DatabaseFactory()).build();
public class SnirgeeDosya:ISnirgeeDosyaOracleCommands
{
public int dosyaId { set { dosyaId = value; } get { return dosyaId; } }
public int userId { set { userId = value; } get { return userId; } }
public DateTime baslangicZaman { get { return DateTime.Now; } }
public String dosyaAdi { set { dosyaAdi = value; } get { return dosyaAdi; } }
private SnirgeeOracleDBWorker oracleDbWorker{ set { oracleDbWorker = value; } get { return oracleDbWorker; } }
/// <summary>
/// SnirgeeDosya olusturması sırasında kullanılır. ilgili parametreler girildikten sonra build edilip SnirgeeDosya elde edilir. Kullanım şekli aşağıdaki gibidir.
/// SnirgeeDosya sn = new SnirgeeDosya.SnirgeeDosyaBuilder().setDosyaAdi("Dosya1").setUserId(1214).setDBFactory(new DatabaseFactory()).build();
/// </summary>
public class SnirgeeDosyaBuilder
{
public int dosyaId { set { dosyaId = value; } get { return dosyaId; } }
public int userId { set { userId = value; } get { return userId; } }
public String dosyaAdi { set { dosyaAdi = value; } get { return dosyaAdi; } }
public SnirgeeOracleDBWorker oracleDbWorker { set { oracleDbWorker = value; } get { return oracleDbWorker; } }
/// <summary>
/// Dosya Builder içerinde dosya adının belirlenmesi.
/// </summary>
/// <param name="dosyaadi"></param>
/// <returns></returns>
public SnirgeeDosyaBuilder setDosyaAdi(String dosyaadi)
{
this.dosyaAdi = dosyaadi;
return this;
}
/// <summary>
/// Dosya Builder içerinde SnirgeeDosyasının kime ait olduğunu belirten metotdur.
/// </summary>
/// <param name="userid"></param>
/// <returns></returns>
public SnirgeeDosyaBuilder setUserId(int userid)
{
this.userId = userid;
return this;
}
/// <summary>
/// Dosya Builder içerinde SnirgeeDosyasının kime ait olduğunu belirten metotdur.
/// </summary>
/// <param name="userid"></param>
/// <returns></returns>
public SnirgeeDosyaBuilder setDosyaId(int dosyaId)
{
this.dosyaId = dosyaId;
return this;
}
/// <summary>
/// Snirgee Dosyasının içerisine Veritabanı işlemleri yapan classın eklenmesi.
/// Elementlerin yazılması ve okunması sırasında bu class içerisinde kullanılacaktır.
/// </summary>
/// <param name="db"></param>
/// <returns></returns>
public SnirgeeDosyaBuilder setDBFactory(DatabaseFactory db)
{
this.oracleDbWorker = new SnirgeeOracleDBWorker(db);
return this;
}
/// <summary>
/// dosyaAdı, kullanıcıId ve Veritabanı işlerini yapacak DatabaseFactory parametreleri atandıktan sonra Snirgee Dosyasını oluşturan methodtur.
/// Henüz veritabanı SnirgeeDosya tablosuna herhangi bir kayıt atanmaz.
/// </summary>
/// <returns></returns>
public SnirgeeDosya build()
{
return new SnirgeeDosya(this);
}
}
/// <summary>
/// SnirgeeDosya'sını oluşturan SnirgeeDosyaBuilder classı build metodu içerisinde kullanılır.
/// </summary>
/// <param name="formBuilder"></param>
private SnirgeeDosya(SnirgeeDosyaBuilder formBuilder)
{
this.dosyaId = formBuilder.dosyaId;
this.userId = formBuilder.userId;
this.dosyaAdi = formBuilder.dosyaAdi;
this.oracleDbWorker = formBuilder.oracleDbWorker;
}
}
Common mistake. Your property is calling itself in the getter/setter. So when you set dosyaId, it sets dosyaId, which sets dosyaId, which sets dosyaId, which... until the call stack is full and you get a StackOverflowException.
Change
public int dosyaId { set { dosyaId = value; } get { return dosyaId; } }
to
public int dosyaId { set; get; }
And the same for the other properties. I see no need for you to have a backing field based on what you've posted. In fact, I see no need for the set... methods either - you can just set the properties directly.
It's an infinite loop - in the property, you're setting the property, which calls the property setter, which sets the property, etc. until the exception.
Either create a member variable (with a different name, this is known as a backing field) that the property sets and gets, or, since these are very simple properties, make them auto-implemented:
public int dosyaId { get; set; }

c# - Can I refactor functions that have similar inputs but call different services into one generic function?

I am looking for a way to combine x amount of very similar CRUD functions into one without having to use x amount of if else statements to check the type of a generic.
I have Web API controllers that I want to make calls from like this:
Service.Get<FooModel>(number, type, part, version);
This is to prevent having to have an extremely similar function for 40+ API endpoints. The issue is when I receive this in my service, I have to check the type of the generic given and compare with those 40+ object types in the one function. All of the models currently inherit from a base inherited model.
Current generic function
(Create, Update, Delete functions are similar):
public T Get<T>(string documentNr, string type, string part, string version) where T : InheritedModel, new()
{
try
{
T model = new T();
if (typeof(T) == typeof(InheritedModel))
{
using (var repo = new InheritedModelConsumer(ref _helper))
{
model = (T)repo.Get(documentNr, type, part, version);
}
}
else if (typeof(T) == typeof(FooModel))
{
using (var repo = new FooModelConsumer(ref _helper))
{
model = (T)(object)repo.Get(documentNr, type, part, version);
}
}
else if (typeof(T) == typeof(ComponentModel))
{
using (var repo = new ComponentModelConsumer(ref _helper))
{
model = (T)(object)repo.Get(documentNr, type, part, version);
}
}
else if (typeof(T) == typeof(BarModel))
{
using (var repo = new BarModelConsumer(ref _helper))
{
model = (T)(object)repo.Get(documentNr, type, part, version);
}
}
... and so on
... and so on
...
else
throw new Exception("Type T structure not defined");
return model;
}
catch (Exception)
{
throw;
}
finally
{
_helper.Dispose();
}
}
This does work, but if it is possible I am looking for something where I can say at run time, "oh I have this object of Type T, and well since I know the functions all have the same inputs I'm going to instantiate this consumer of Type TConsumer, call consumer.Get(inputs), and then return an object of T to whatever API controller called me."
Edit
Example of a simple consumer class in use
internal sealed class FooConsumer : RepositoryConsumer<Foo, FooRepository, FooFilter>
{
public FooConsumer(ref SqlHelper helper) : base(ref helper) { }
public List<Foo> GetAll(string token)
{
return _repo.Get().Where(x => Extensions.StringContainsToken(x.AccountName, token)).ToList();
}
}
Repository Consumer that all consumers inherit from .
T is the model, K is the Repository (custom ORM class), and O is Filter for the WHERE clause the ORM executes.
public abstract class RepositoryConsumer<T, K, O> : IDisposable, IRepositoryConsumer<T> where T : class, new() where K : Repository<T, O>, new() where O : QueryFilter, new()
{
/// <summary>
/// Repository instance
/// </summary>
protected K _repo;
/// <summary>
/// Only constructor avaialble. MUst pass SqlHelper instance for transaction support
/// </summary>
/// <param name="sql"></param>
public RepositoryConsumer(ref SqlHelper sql)
{
_repo = Activator.CreateInstance(typeof(K), new object[] { sql }) as K;
}
/// <summary>
/// Allow consumer initializations in using statements
/// </summary>
public void Dispose()
{
}
/// <summary>
/// Create instance of T
/// </summary>
/// <param name="data"></param>
/// <returns></returns>
public virtual int Create(T data)
{
return _repo.Create(data);
}
/// <summary>
/// Bulk create instances of T
/// </summary>
/// <param name="contract"></param>
/// <returns></returns>
public virtual int Create(BaseBulkable<T> contract)
{
return _repo.BulkCreate(contract);
}
/// <summary>
/// Get an instance of T based on a single PK field id
/// </summary>
/// <param name="id"></param>
/// <returns></returns>
public virtual T Get(long id)
{
return _repo.Get(id);
}
/// <summary>
/// Gets all instances of T
/// </summary>
/// <returns></returns>
public virtual List<T> GetAll()
{
return _repo.Get();
}
/// <summary>
/// Updates an instance of T
/// </summary>
/// <param name="data"></param>
/// <returns></returns>
public virtual int Update(T data)
{
return _repo.Update(data);
}
/// <summary>
/// Updates an instance of T based on a single PK field id
/// </summary>
/// <param name="id"></param>
/// <param name="data"></param>
/// <returns></returns>
public virtual int Update(long id, T data)
{
return _repo.Update(id, data);
}
/// <summary>
/// Deletes an instance of T
/// </summary>
/// <param name="data"></param>
/// <returns></returns>
public virtual int Delete(T data)
{
return _repo.Delete(data);
}
/// <summary>
/// Deletes an instance of T based on a single PK field id
/// </summary>
/// <param name="id"></param>
/// <returns></returns>
public virtual int Delete(long id)
{
return _repo.Delete(id);
}
}

Invalid Column Name or Identity_Insert off

I have been going rounds all morning with CF EF. I think I have all issues resolved but one and I'm sure this issue is due to my lack of knowledge / understanding.
The problem is focused around one table and the mapping of said table so I'll give the definitions first then explain the problems. Here is how the table is defined:
Now I have defined the class representation as:
public partial class ExternalForumCredentials : BaseEntity
{
public virtual int Customer_Id { get; set; }
public virtual int ExternalForumBoardId { get; set; }
public virtual string Username { get; set; }
public virtual string Password { get; set; }
}
and the mapping:
public ExternalForumCredentialsMap()
{
this.ToTable("ExternalForumCredentials");
this.HasKey(ef => ef.Id);
this.HasKey(ef => ef.Customer_Id);
this.HasKey(ef => ef.ExternalForumBoardId);
this.Property(ef => ef.Username).IsRequired().HasMaxLength(100);
this.Property(ef => ef.Password).IsRequired().HasMaxLength(100);
}
Now I am not showing the controller code as I think the problem lies in my ef config. HOWEVER if I am wrong simply tell me what I need to add and I will do so immediately.
So...given that ID, Customer_Id, and ExternalForumBoardId are primary keys I defined the mapping as seen above.
With this configuration all goes well right up to an insert...then it crashes saying I am trying to do an IDENTITY_INSERT. OK that makes sense as I am assigning a value to Customer_Id and ExternalForumBoardId in the controller and since the mapping has them as keys there is a conflict.
So I change the mapping to:
public ExternalForumCredentialsMap()
{
this.ToTable("ExternalForumCredentials");
this.HasKey(ef => ef.Id);
this.Property(ef => ef.Customer_Id);
this.Property(ef => ef.ExternalForumBoardId);
this.Property(ef => ef.Username).IsRequired().HasMaxLength(100);
this.Property(ef => ef.Password).IsRequired().HasMaxLength(100);
}
Now before I can even perform the insert I get an error: Invalid column name Customer_Id and of course with my limited knowledge I don't understand because I have define Customer_Id.
I have also tried HasRequired in the mapping but that won't even compile saying "the type int must be a reference type in order to use it as parameter..."
The other mapping options such as Ignore don't seem to make sense in this context.
Any help explaining what I am doing wrong would be much appreciated.
Db Context Interface:
public interface IDbContext
{
IDbSet<TEntity> Set<TEntity>() where TEntity : BaseEntity;
int SaveChanges();
IList<TEntity> ExecuteStoredProcedureList<TEntity>(string commandText, params object[] parameters)
where TEntity : BaseEntity, new();
/// <summary>
/// Creates a raw SQL query that will return elements of the given generic type. The type can be any type that has properties that match the names of the columns returned from the query, or can be a simple primitive type. The type does not have to be an entity type. The results of this query are never tracked by the context even if the type of object returned is an entity type.
/// </summary>
/// <typeparam name="TElement">The type of object returned by the query.</typeparam>
/// <param name="sql">The SQL query string.</param>
/// <param name="parameters">The parameters to apply to the SQL query string.</param>
/// <returns>Result</returns>
IEnumerable<TElement> SqlQuery<TElement>(string sql, params object[] parameters);
/// <summary>
/// Executes the given DDL/DML command against the database.
/// </summary>
/// <param name="sql">The command string</param>
/// <param name="timeout">Timeout value, in seconds. A null value indicates that the default value of the underlying provider will be used</param>
/// <param name="parameters">The parameters to apply to the command string.</param>
/// <returns>The result returned by the database after executing the command.</returns>
int ExecuteSqlCommand(string sql, int? timeout = null, params object[] parameters);
}
}
and:
public static class DbContextExtensions {
/// <summary>
/// Loads the database copy.
/// </summary>
/// <typeparam name="T"></typeparam>
/// <param name="context">The context.</param>
/// <param name="currentCopy">The current copy.</param>
/// <returns></returns>
public static T LoadDatabaseCopy<T>(this IDbContext context, T currentCopy) where T : BaseEntity {
return InnerGetCopy(context, currentCopy, e => e.GetDatabaseValues());
}
private static T InnerGetCopy<T>(IDbContext context, T currentCopy, Func<DbEntityEntry<T>, DbPropertyValues> func) where T : BaseEntity {
//Get the database context
DbContext dbContext = CastOrThrow(context);
//Get the entity tracking object
DbEntityEntry<T> entry = GetEntityOrReturnNull(currentCopy, dbContext);
//The output
T output = null;
//Try and get the values
if (entry != null) {
DbPropertyValues dbPropertyValues = func(entry);
if(dbPropertyValues != null) {
output = dbPropertyValues.ToObject() as T;
}
}
return output;
}
/// <summary>
/// Gets the entity or return null.
/// </summary>
/// <typeparam name="T"></typeparam>
/// <param name="currentCopy">The current copy.</param>
/// <param name="dbContext">The db context.</param>
/// <returns></returns>
private static DbEntityEntry<T> GetEntityOrReturnNull<T>(T currentCopy, DbContext dbContext) where T : BaseEntity {
return dbContext.ChangeTracker.Entries<T>().Where(e => e.Entity == currentCopy).FirstOrDefault();
}
private static DbContext CastOrThrow(IDbContext context) {
DbContext output = (context as DbContext);
if(output == null) {
throw new InvalidOperationException("Context does not support operation.");
}
return output;
}
/// <summary>
/// Loads the original copy.
/// </summary>
/// <typeparam name="T"></typeparam>
/// <param name="context">The context.</param>
/// <param name="currentCopy">The current copy.</param>
/// <returns></returns>
public static T LoadOriginalCopy<T>(this IDbContext context, T currentCopy) where T : BaseEntity {
return InnerGetCopy(context, currentCopy, e => e.OriginalValues);
}
}
and the implementation:
public class ObjectContext : DbContext, IDbContext
{
public ObjectContext(string nameOrConnectionString)
: base(nameOrConnectionString)
{
//((IObjectContextAdapter) this).ObjectContext.ContextOptions.LazyLoadingEnabled = true;
}
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
//dynamically load all configuration
System.Type configType = typeof(LanguageMap); //any of your configuration classes here
var typesToRegister = Assembly.GetAssembly(configType).GetTypes()
.Where(type => !String.IsNullOrEmpty(type.Namespace))
.Where(type => type.BaseType != null && type.BaseType.IsGenericType && type.BaseType.GetGenericTypeDefinition() == typeof(EntityTypeConfiguration<>));
foreach (var type in typesToRegister)
{
dynamic configurationInstance = Activator.CreateInstance(type);
modelBuilder.Configurations.Add(configurationInstance);
}
//...or do it manually below. For example,
//modelBuilder.Configurations.Add(new LanguageMap());
base.OnModelCreating(modelBuilder);
}
/// <summary>
/// Attach an entity to the context or return an already attached entity (if it was already attached)
/// </summary>
/// <typeparam name="TEntity">TEntity</typeparam>
/// <param name="entity">Entity</param>
/// <returns>Attached entity</returns>
protected virtual TEntity AttachEntityToContext<TEntity>(TEntity entity) where TEntity : BaseEntity, new()
{
//little hack here until Entity Framework really supports stored procedures
//otherwise, navigation properties of loaded entities are not loaded until an entity is attached to the context
var alreadyAttached = Set<TEntity>().Local.Where(x => x.Id == entity.Id).FirstOrDefault();
if (alreadyAttached == null)
{
//attach new entity
Set<TEntity>().Attach(entity);
return entity;
}
else
{
//entity is already loaded.
return alreadyAttached;
}
}
public string CreateDatabaseScript()
{
return ((IObjectContextAdapter)this).ObjectContext.CreateDatabaseScript();
}
public new IDbSet<TEntity> Set<TEntity>() where TEntity : BaseEntity
{
return base.Set<TEntity>();
}
public IList<TEntity> ExecuteStoredProcedureList<TEntity>(string commandText, params object[] parameters) where TEntity : BaseEntity, new()
{
//HACK: Entity Framework Code First doesn't support doesn't support output parameters
//That's why we have to manually create command and execute it.
//just wait until EF Code First starts support them
//
//More info: http://weblogs.asp.net/dwahlin/archive/2011/09/23/using-entity-framework-code-first-with-stored-procedures-that-have-output-parameters.aspx
bool hasOutputParameters = false;
if (parameters != null)
{
foreach (var p in parameters)
{
var outputP = p as DbParameter;
if (outputP == null)
continue;
if (outputP.Direction == ParameterDirection.InputOutput ||
outputP.Direction == ParameterDirection.Output)
hasOutputParameters = true;
}
}
var context = ((IObjectContextAdapter)(this)).ObjectContext;
if (!hasOutputParameters)
{
//no output parameters
var result = this.Database.SqlQuery<TEntity>(commandText, parameters).ToList();
for (int i = 0; i < result.Count; i++)
result[i] = AttachEntityToContext(result[i]);
return result;
//var result = context.ExecuteStoreQuery<TEntity>(commandText, parameters).ToList();
//foreach (var entity in result)
// Set<TEntity>().Attach(entity);
//return result;
}
else
{
//var connection = context.Connection;
var connection = this.Database.Connection;
//Don't close the connection after command execution
//open the connection for use
if (connection.State == ConnectionState.Closed)
connection.Open();
//create a command object
using (var cmd = connection.CreateCommand())
{
//command to execute
cmd.CommandText = commandText;
cmd.CommandType = CommandType.StoredProcedure;
// move parameters to command object
if (parameters != null)
foreach (var p in parameters)
cmd.Parameters.Add(p);
//database call
var reader = cmd.ExecuteReader();
//return reader.DataReaderToObjectList<TEntity>();
var result = context.Translate<TEntity>(reader).ToList();
for (int i = 0; i < result.Count; i++)
result[i] = AttachEntityToContext(result[i]);
//close up the reader, we're done saving results
reader.Close();
return result;
}
}
}
/// <summary>
/// Creates a raw SQL query that will return elements of the given generic type. The type can be any type that has properties that match the names of the columns returned from the query, or can be a simple primitive type. The type does not have to be an entity type. The results of this query are never tracked by the context even if the type of object returned is an entity type.
/// </summary>
/// <typeparam name="TElement">The type of object returned by the query.</typeparam>
/// <param name="sql">The SQL query string.</param>
/// <param name="parameters">The parameters to apply to the SQL query string.</param>
/// <returns>Result</returns>
public IEnumerable<TElement> SqlQuery<TElement>(string sql, params object[] parameters)
{
return this.Database.SqlQuery<TElement>(sql, parameters);
}
/// <summary>
/// Executes the given DDL/DML command against the database.
/// </summary>
/// <param name="sql">The command string</param>
/// <param name="timeout">Timeout value, in seconds. A null value indicates that the default value of the underlying provider will be used</param>
/// <param name="parameters">The parameters to apply to the command string.</param>
/// <returns>The result returned by the database after executing the command.</returns>
public int ExecuteSqlCommand(string sql, int? timeout = null, params object[] parameters)
{
int? previousTimeout = null;
if (timeout.HasValue)
{
//store previous timeout
previousTimeout = ((IObjectContextAdapter) this).ObjectContext.CommandTimeout;
((IObjectContextAdapter) this).ObjectContext.CommandTimeout = timeout;
}
var result = this.Database.ExecuteSqlCommand(sql, parameters);
if (timeout.HasValue)
{
//Set previous timeout back
((IObjectContextAdapter) this).ObjectContext.CommandTimeout = previousTimeout;
}
//return result
return result;
}
}
You have another class that has a relationship with ExternalForumCredentials and it hasn't be configured properly. EntityFramework will try its best to guess your conventions, so it's guessing that there is a Customer_Id that it can connect to.
Go to any maps that are using ExternalForumCredentials and configure them properly. For example:
this.HasMany(ef => ef.ExternalForumCredentials)
.WithRequired()
.HasForeignKey(ef => ef.Customer_Id);

Generic Entity Framework and DTO repository where lambda issue

Im trying to implement a layered application using entity framework, and DTO's to pass objects between layers.
I have been following through some code on line to set up a generic respository but I have run in to a couple of methods I cant solve.
Im using EntitiesToDTOs to generate my DTO's from the edmx file, but unfortunately they dont have a base class (there are too many to update manually)
To Set the scene I have the following 2 generic classes:
IRepository:
public interface IRepository<TEntity> : IDisposable
{
/// <summary>
/// Creates a new empty entity.
/// </summary>
TEntity Create();
/// <summary>
/// Creates the existing entity.
/// </summary>
TEntity Create(TEntity entity);
/// <summary>
/// Updates the existing entity.
/// </summary>
TEntity Update(TEntity entity);
/// <summary>
/// Delete an entity using its primary key.
/// </summary>
void Delete(long id);
/// <summary>
/// Delete the given entity.
/// </summary>
void Delete(TEntity entity);
/// <summary>
/// Deletes the existing entity.
/// </summary>
void Delete(Expression<Func<TEntity, bool>> where);
/// <summary>
/// Finds one entity based on provided criteria.
/// </summary>
TEntity FindOne(Expression<Func<TEntity, bool>> where = null);
/// <summary>
/// Finds one entity based on its Identifier.
/// </summary>
TEntity FindById(long id);
/// <summary>
/// Finds entities based on provided criteria.
/// </summary>
IQueryable<TEntity> FindAll(Expression<Func<TEntity, bool>> where = null);
/// <summary>
/// Finds other related entities based of type T for queries.
/// </summary>
IQueryable<T> Set<T>() where T : class;
/// <summary>
/// Save any changes to the TContext
/// </summary>
bool SaveChanges();
}
Generic Implementation:
public class Repository<TEntity, TContext> : IRepository<TEntity>, IDisposable
where TEntity : class
where TContext : DbContext
{
protected TContext Context;
public Repository(DbContext dbContext)
{
Context = dbContext as TContext;
}
public virtual TEntity Create()
{
return Context.Set<TEntity>().Create();
}
public virtual TEntity Create(TEntity entity)
{
return Context.Set<TEntity>().Add(entity);
}
public virtual TEntity Update(TEntity entity)
{
Context.Entry(entity).State = EntityState.Modified;
return entity;
}
public virtual void Delete(long id)
{
var item = Context.Set<TEntity>().Find(id);
Context.Set<TEntity>().Remove(item);
}
public virtual void Delete(TEntity entity)
{
Context.Set<TEntity>().Remove(entity);
}
public virtual void Delete(Expression<Func<TEntity, bool>> where)
{
var objects = Context.Set<TEntity>().Where(where).AsEnumerable();
foreach (var item in objects)
{
Context.Set<TEntity>().Remove(item);
}
}
public virtual TEntity FindById(long id)
{
return Context.Set<TEntity>().Find(id);
}
public virtual TEntity FindOne(Expression<Func<TEntity, bool>> where = null)
{
return FindAll(where).FirstOrDefault();
}
public IQueryable<T> Set<T>() where T : class
{
return Context.Set<T>();
}
public virtual IQueryable<TEntity> FindAll(Expression<Func<TEntity, bool>> where = null)
{
return null != where ? Context.Set<TEntity>().Where(where) : Context.Set<TEntity>();
}
public virtual bool SaveChanges()
{
return 0 < Context.SaveChanges();
}
/// <summary>
/// Releases all resources used by the Entities
/// </summary>
public void Dispose()
{
if (null != Context)
{
Context.Dispose();
}
}
}
The class used to convert between and Order record and an Order DTO:
/// <summary>
/// Assembler for <see cref="Order"/> and <see cref="OrderDTO"/>.
/// </summary>
public static partial class OrderAssembler
{
/// <summary>
/// Invoked when <see cref="ToDTO"/> operation is about to return.
/// </summary>
/// <param name="dto"><see cref="OrderDTO"/> converted from <see cref="Order"/>.</param>
static partial void OnDTO(this Order entity, OrderDTO dto);
/// <summary>
/// Invoked when <see cref="ToEntity"/> operation is about to return.
/// </summary>
/// <param name="entity"><see cref="Order"/> converted from <see cref="OrderDTO"/>.</param>
static partial void OnEntity(this OrderDTO dto, Order entity);
/// <summary>
/// Converts this instance of <see cref="OrderDTO"/> to an instance of <see cref="Order"/>.
/// </summary>
/// <param name="dto"><see cref="OrderDTO"/> to convert.</param>
public static Order ToEntity(this OrderDTO dto)
{
if (dto == null) return null;
var entity = new Order();
entity.OrderID = dto.OrderID;
entity.SupplierID = dto.SupplierID;
entity.Special = dto.Special;
entity.RequestedBy = dto.RequestedBy;
entity.RequestedFor = dto.RequestedFor;
entity.Urgent = dto.Urgent;
entity.OrderStatus = dto.OrderStatus;
entity.DeliveryAddressID = dto.DeliveryAddressID;
entity.OrderDate = dto.OrderDate;
entity.Deleted = dto.Deleted;
entity.SentToSage = dto.SentToSage;
entity.Cancelled = dto.Cancelled;
entity.InvoiceAddressID = dto.InvoiceAddressID;
entity.SageOrderID = dto.SageOrderID;
entity.SageDatabaseID = dto.SageDatabaseID;
entity.DeliveryDate = dto.DeliveryDate;
entity.SupplierReference = dto.SupplierReference;
entity.Analysis1 = dto.Analysis1;
entity.Analysis2 = dto.Analysis2;
entity.Analysis3 = dto.Analysis3;
entity.Analysis4 = dto.Analysis4;
entity.Analysis5 = dto.Analysis5;
entity.Analysis6 = dto.Analysis6;
entity.OrderDiscount = dto.OrderDiscount;
entity.SageDatabaseName = dto.SageDatabaseName;
entity.SupplierName = dto.SupplierName;
entity.RequestedByName = dto.RequestedByName;
entity.DeliveryAddressName = dto.DeliveryAddressName;
entity.NetValue = dto.NetValue;
entity.DepartmentID = dto.DepartmentID;
entity.PODocumentNo = dto.PODocumentNo;
entity.ConstructRelated = dto.ConstructRelated;
entity.Archived = dto.Archived;
entity.UpdateStatus = dto.UpdateStatus;
entity.UpdatedDate = dto.UpdatedDate;
entity.UpdatedUser = dto.UpdatedUser;
entity.WarehouseID = dto.WarehouseID;
entity.ExchangeRate = dto.ExchangeRate;
entity.CurrencySymbol = dto.CurrencySymbol;
entity.SupplierEmailAddress = dto.SupplierEmailAddress;
entity.SupplierContactName = dto.SupplierContactName;
entity.SupplierTelephone = dto.SupplierTelephone;
entity.SupplierFax = dto.SupplierFax;
entity.HasAttachments = dto.HasAttachments;
entity.HasAnalysisValues = dto.HasAnalysisValues;
entity.SYSTraderAnalysisValueID = dto.SYSTraderAnalysisValueID;
entity.InternalOrder = dto.InternalOrder;
entity.DeliveryPostalName = dto.DeliveryPostalName;
entity.DeliveryAddressLine1 = dto.DeliveryAddressLine1;
entity.DeliveryAddressLine2 = dto.DeliveryAddressLine2;
entity.DeliveryAddressLine3 = dto.DeliveryAddressLine3;
entity.DeliveryAddressLine4 = dto.DeliveryAddressLine4;
entity.DeliveryPostCode = dto.DeliveryPostCode;
entity.InvoicePostalName = dto.InvoicePostalName;
entity.InvoiceAddressLine1 = dto.InvoiceAddressLine1;
entity.InvoiceAddressLine2 = dto.InvoiceAddressLine2;
entity.InvoiceAddressLine3 = dto.InvoiceAddressLine3;
entity.InvoiceAddressLine4 = dto.InvoiceAddressLine4;
entity.InvoicePostCode = dto.InvoicePostCode;
entity.DeliveryContactName = dto.DeliveryContactName;
entity.InvoiceContactName = dto.InvoiceContactName;
entity.DeliveryTelephoneNo = dto.DeliveryTelephoneNo;
entity.DeliveryFaxNo = dto.DeliveryFaxNo;
entity.InvoiceTelephoneNo = dto.InvoiceTelephoneNo;
entity.InvoiceFaxNo = dto.InvoiceFaxNo;
entity.CheckForNewDocuments = dto.CheckForNewDocuments;
entity.EmailSent = dto.EmailSent;
entity.DocumentNoPrefix = dto.DocumentNoPrefix;
dto.OnEntity(entity);
return entity;
}
/// <summary>
/// Converts this instance of <see cref="Order"/> to an instance of <see cref="OrderDTO"/>.
/// </summary>
/// <param name="entity"><see cref="Order"/> to convert.</param>
public static OrderDTO ToDTO(this Order entity)
{
if (entity == null) return null;
var dto = new OrderDTO();
dto.OrderID = entity.OrderID;
dto.SupplierID = entity.SupplierID;
dto.Special = entity.Special;
dto.RequestedBy = entity.RequestedBy;
dto.RequestedFor = entity.RequestedFor;
dto.Urgent = entity.Urgent;
dto.OrderStatus = entity.OrderStatus;
dto.DeliveryAddressID = entity.DeliveryAddressID;
dto.OrderDate = entity.OrderDate;
dto.Deleted = entity.Deleted;
dto.SentToSage = entity.SentToSage;
dto.Cancelled = entity.Cancelled;
dto.InvoiceAddressID = entity.InvoiceAddressID;
dto.SageOrderID = entity.SageOrderID;
dto.SageDatabaseID = entity.SageDatabaseID;
dto.DeliveryDate = entity.DeliveryDate;
dto.SupplierReference = entity.SupplierReference;
dto.Analysis1 = entity.Analysis1;
dto.Analysis2 = entity.Analysis2;
dto.Analysis3 = entity.Analysis3;
dto.Analysis4 = entity.Analysis4;
dto.Analysis5 = entity.Analysis5;
dto.Analysis6 = entity.Analysis6;
dto.OrderDiscount = entity.OrderDiscount;
dto.SageDatabaseName = entity.SageDatabaseName;
dto.SupplierName = entity.SupplierName;
dto.RequestedByName = entity.RequestedByName;
dto.DeliveryAddressName = entity.DeliveryAddressName;
dto.NetValue = entity.NetValue;
dto.DepartmentID = entity.DepartmentID;
dto.PODocumentNo = entity.PODocumentNo;
dto.ConstructRelated = entity.ConstructRelated;
dto.Archived = entity.Archived;
dto.UpdateStatus = entity.UpdateStatus;
dto.UpdatedDate = entity.UpdatedDate;
dto.UpdatedUser = entity.UpdatedUser;
dto.WarehouseID = entity.WarehouseID;
dto.ExchangeRate = entity.ExchangeRate;
dto.CurrencySymbol = entity.CurrencySymbol;
dto.SupplierEmailAddress = entity.SupplierEmailAddress;
dto.SupplierContactName = entity.SupplierContactName;
dto.SupplierTelephone = entity.SupplierTelephone;
dto.SupplierFax = entity.SupplierFax;
dto.HasAttachments = entity.HasAttachments;
dto.HasAnalysisValues = entity.HasAnalysisValues;
dto.SYSTraderAnalysisValueID = entity.SYSTraderAnalysisValueID;
dto.InternalOrder = entity.InternalOrder;
dto.DeliveryPostalName = entity.DeliveryPostalName;
dto.DeliveryAddressLine1 = entity.DeliveryAddressLine1;
dto.DeliveryAddressLine2 = entity.DeliveryAddressLine2;
dto.DeliveryAddressLine3 = entity.DeliveryAddressLine3;
dto.DeliveryAddressLine4 = entity.DeliveryAddressLine4;
dto.DeliveryPostCode = entity.DeliveryPostCode;
dto.InvoicePostalName = entity.InvoicePostalName;
dto.InvoiceAddressLine1 = entity.InvoiceAddressLine1;
dto.InvoiceAddressLine2 = entity.InvoiceAddressLine2;
dto.InvoiceAddressLine3 = entity.InvoiceAddressLine3;
dto.InvoiceAddressLine4 = entity.InvoiceAddressLine4;
dto.InvoicePostCode = entity.InvoicePostCode;
dto.DeliveryContactName = entity.DeliveryContactName;
dto.InvoiceContactName = entity.InvoiceContactName;
dto.DeliveryTelephoneNo = entity.DeliveryTelephoneNo;
dto.DeliveryFaxNo = entity.DeliveryFaxNo;
dto.InvoiceTelephoneNo = entity.InvoiceTelephoneNo;
dto.InvoiceFaxNo = entity.InvoiceFaxNo;
dto.CheckForNewDocuments = entity.CheckForNewDocuments;
dto.EmailSent = entity.EmailSent;
dto.DocumentNoPrefix = entity.DocumentNoPrefix;
entity.OnDTO(dto);
return dto;
}
/// <summary>
/// Converts each instance of <see cref="OrderDTO"/> to an instance of <see cref="Order"/>.
/// </summary>
/// <param name="dtos"></param>
/// <returns></returns>
public static List<Order> ToEntities(this IEnumerable<OrderDTO> dtos)
{
if (dtos == null) return null;
return dtos.Select(e => e.ToEntity()).ToList();
}
/// <summary>
/// Converts each instance of <see cref="Order"/> to an instance of <see cref="OrderDTO"/>.
/// </summary>
/// <param name="entities"></param>
/// <returns></returns>
public static List<OrderDTO> ToDTOs(this IEnumerable<Order> entities)
{
if (entities == null) return null;
return entities.Select(e => e.ToDTO()).ToList();
}
}
My implementation of an 'OrderRepository':
public class OrderRepository : IRepository<OrderDTO>
{
private Repository<Order, WAPEntities> _repository;
public OrderRepository()
{
_repository = new Repository<Order, WAPEntities>(new WAPEntities());
}
public void Dispose()
{
_repository.Dispose();
}
public OrderDTO Create()
{
return _repository.Create().ToDTO();
}
public OrderDTO Create(OrderDTO entity)
{
return _repository.Create(entity.ToEntity()).ToDTO();
}
public OrderDTO Update(OrderDTO entity)
{
return _repository.Update(entity.ToEntity()).ToDTO();
}
public void Delete(long id)
{
_repository.Delete(id);
}
public void Delete(OrderDTO entity)
{
_repository.Delete(entity.ToEntity());
}
public void Delete(Expression<Func<OrderDTO, bool>> where)
{
// I have tried this but it wont work
var resultBody = Expression.Convert(where.Body, typeof(OrderDTO));
var result = Expression.Lambda<Func<Order, bool>>(resultBody, where.Parameters);
_repository.Delete(result);
}
public OrderDTO FindOne(System.Linq.Expressions.Expression<Func<OrderDTO, bool>> where = null)
{
//Here the same issue with the Where clause
throw new NotImplementedException();
}
public OrderDTO FindById(long id)
{
return _repository.FindById(id).ToDTO();
}
public IQueryable<OrderDTO> FindAll(System.Linq.Expressions.Expression<Func<OrderDTO, bool>> where = null)
{
//Here the same issue with the Where clause
throw new NotImplementedException();
}
public IQueryable<T> Set<T>() where T : class
{
return _repository.Set<T>();
}
public bool SaveChanges()
{
return _repository.SaveChanges();
}
}
As you can see, for most of my Order Repository methods, I can convert from Entities to DTOs, and from DTOs to entities pretty easily.
The ones im struggling with is how to convert that lamda where clause to convert from a lambda that takes my Order DTO class, and calls entity.ToDTO() on it and converts to a lambda for the order entity.
The methods I cant work out are:
public void Delete(Expression<Func<OrderDTO, bool>> where)
public OrderDTO FindOne(System.Linq.Expressions.Expression<Func<OrderDTO, bool>> where = null)
public IQueryable<OrderDTO> FindAll(System.Linq.Expressions.Expression<Func<OrderDTO, bool>> where = null)
Is this even possible?
Your problem here is that you're trying to treat your DTO's as if they were entities. They're not.
Entities are Entities.. They're the objects that are mapped in your data context. DTO's are just simple, dumb objects used to transfer data, as their name suggests. If your DTO's were exactly the same as your Entities, there would be no reason to have them both.
When it comes to your lambda's.. you're talking two different things. A lambda expression against a DTO is not the same as a lambda expression against an entity. While you can modify an expression tree, it's not fun and is a lot of work.
I have to question why you are introducing this extra abstraction if all you're going to do is treat it exactly like you would your entities. If your abstraction provides no value, I would avoid it... unless you expect that you will be changing database technology later.. in which case maybe it has some benefit.. but even if that's the case you're still treating your DTO's as if they're EF objects, which means doing a logical translation to whatever new technology you're using.
You should think about the scenarios you intend to use the lambda for. I'm guessing most likely it's to deal with collections of objects. In which case, why not just make a functions that take a collection of DTO's and then translate that to your Lambda.. that would be a lot easier.
The real benefit of this is that if you change your data model, you only have to change your repository. When you start using lambdas all over in your code, every time you change something you have to go find everywhere you're using that lambda. It's highly beneficial to centralize your lambda code.

Stored Procedure loses connection

I have an ASP.NET MVC project in which the model is managed through .NET entities and it seems that some times it loses the connection, but this happens only on stored procedures.
I get the following error:
Execution of the command requires an open and available connection. The connection's current state is broken.
Why is this happening?
Code
public ObjectResult<Categories> GetCategoriesStructure() {
return ObjectContext.getCategoriesStructure();
}
var catss = GetCategoriesStructure().ToList();
this exception occurs when I am trying to assign the List to catss variable
Object Context Instantiation
public abstract class ObjectContextManager {
/// <summary>
/// Returns a reference to an ObjectContext instance.
/// </summary>
public abstract TObjectContext GetObjectContext<TObjectContext>()
where TObjectContext : ObjectContext, new();
}
public abstract class BaseDAO<TObjectContext, TEntity> : IBaseDAO<TObjectContext, TEntity>
where TObjectContext : System.Data.Objects.ObjectContext, new()
where TEntity : System.Data.Objects.DataClasses.EntityObject {
private ObjectContextManager _objectContextManager;
/// <summary>
/// Returns the current ObjectContextManager instance. Encapsulated the
/// _objectContextManager field to show it as an association on the class diagram.
/// </summary>
private ObjectContextManager ObjectContextManager {
get { return _objectContextManager; }
set { _objectContextManager = value; }
}
/// <summary>
/// Returns an ObjectContext object.
/// </summary>
protected internal TObjectContext ObjectContext {
get {
if (ObjectContextManager == null)
this.InstantiateObjectContextManager();
return ObjectContextManager.GetObjectContext<TObjectContext>();
}
}
/// <summary>
/// Default constructor.
/// </summary>
public BaseDAO() { }
/// <summary>
/// Instantiates a new ObjectContextManager based on application configuration settings.
/// </summary>
private void InstantiateObjectContextManager() {
/* Retrieve ObjectContextManager configuration settings: */
Hashtable ocManagerConfiguration = ConfigurationManager.GetSection("ObjectContextManagement.ObjectContext") as Hashtable;
if (ocManagerConfiguration != null && ocManagerConfiguration.ContainsKey("managerType")) {
string managerTypeName = ocManagerConfiguration["managerType"] as string;
if (string.IsNullOrEmpty(managerTypeName))
throw new ConfigurationErrorsException("The managerType attribute is empty.");
else
managerTypeName = managerTypeName.Trim().ToLower();
try {
/* Try to create a type based on it's name: */
Assembly frameworkAssembly = Assembly.GetAssembly(typeof(ObjectContextManager));
Type managerType = frameworkAssembly.GetType(managerTypeName, true, true);
/* Try to create a new instance of the specified ObjectContextManager type: */
this.ObjectContextManager = Activator.CreateInstance(managerType) as ObjectContextManager;
} catch (Exception e) {
throw new ConfigurationErrorsException("The managerType specified in the configuration is not valid.", e);
}
} else
throw new ConfigurationErrorsException("ObjectContext tag or its managerType attribute is missing in the configuration.");
}
/// <summary>
/// Persists all changes to the underlying datastore.
/// </summary>
public void SaveAllObjectChanges() {
this.ObjectContext.SaveChanges();
}
/// <summary>
/// Adds a new entity object to the context.
/// </summary>
/// <param name="newObject">A new object.</param>
public virtual void Add(TEntity newObject) {
this.ObjectContext.AddObject(newObject.GetType().Name, newObject);
}
/// <summary>
/// Deletes an entity object.
/// </summary>
/// <param name="obsoleteObject">An obsolete object.</param>
public virtual void Delete(TEntity obsoleteObject) {
this.ObjectContext.DeleteObject(obsoleteObject);
}
public void Detach(TEntity obsoleteObject) {
this.ObjectContext.Detach(obsoleteObject);
}
/// <summary>
/// Updates the changed entity object to the context.
/// </summary>
/// <param name="newObject">A new object.</param>
public virtual void Update(TEntity newObject) {
ObjectContext.ApplyPropertyChanges(newObject.GetType().Name, newObject);
ObjectContext.Refresh(RefreshMode.ClientWins, newObject);
}
public virtual TEntity LoadByKey(String propertyName, Object keyValue) {
IEnumerable<KeyValuePair<string, object>> entityKeyValues =
new KeyValuePair<string, object>[] {
new KeyValuePair<string, object>(propertyName, keyValue) };
// Create the key for a specific SalesOrderHeader object.
EntityKey key = new EntityKey(this.ObjectContext.GetType().Name + "." + typeof(TEntity).Name, entityKeyValues);
return (TEntity)this.ObjectContext.GetObjectByKey(key);
}
#region IBaseDAO<TObjectContext,TEntity> Members
public bool validation(TEntity newObject) {
return newObject.GetType().Name.ToString() == "Int32";
}
#endregion
}
Without knowing how you are instantiating your ObjectContext, I'll throw something in the answer bucket here.
This is how I do my Entity Framework commands and connections (for small simple projects at least):
using (MyEntities context = new MyEntities())
{
return context.getCategoriesStructure();
}
You can also optionally pass in a connection string when instantiating your context (if not, it will use the one in your app.config):
new MyEntities("...connection string...")
If this does not help your issue, please help us understand your code a little better by posting how you are creating your ObjectContext. You could at least attempt to do it this way to see if it works; that will tell you whether it is an issue with your connection string or not.

Categories

Resources