Generic Entity Framework and DTO repository where lambda issue - c#

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.

Related

How to build a dynamic command object?

I'll try to make this as clear as possible.
A Plugin architecture using reflection and 2 Attributes and an abstract class:
PluginEntryAttribute(Targets.Assembly, typeof(MyPlugin))
PluginImplAttribute(Targets.Class, ...)
abstract class Plugin
Commands are routed to a plugin via an interface and a delegate:
Ex: public delegate TTarget Command<TTarget>(object obj);
Using extension methods with Command<> as the target, a CommandRouter executes the delegate on the correct target interface:
Ex:
public static TResult Execute<TTarget, TResult>(this Command<TTarget> target, Func<TTarget, TResult> func) {
return CommandRouter.Default.Execute(func);
}
Putting this together, I have a class hard-coded with the command delegates like so:
public class Repositories {
public static Command<IDispatchingRepository> Dispatching = (o) => { return (IDispatchingRepository)o; };
public static Command<IPositioningRepository> Positioning = (o) => { return (IPositioningRepository)o; };
public static Command<ISchedulingRepository> Scheduling = (o) => { return (ISchedulingRepository)o; };
public static Command<IHistographyRepository> Histography = (o) => { return (IHistographyRepository)o; };
}
When an object wants to query from the repository, practical execution looks like this:
var expBob = Dispatching.Execute(repo => repo.AddCustomer("Bob"));
var actBob = Dispatching.Execute(repo => repo.GetCustomer("Bob"));
My question is this: how can I create such a class as Repositories dynamically from the plugins?
I can see the possibility that another attribute might be necessary. Something along the lines of:
[RoutedCommand("Dispatching", typeof(IDispatchingRepository)")]
public Command<IDispatchingRepository> Dispatching = (o) => { return (IDispatchingRepository)o; };
This is just an idea, but I'm at a loss as to how I'd still create a dynamic menu of sorts like the Repositories class.
For completeness, the CommandRouter.Execute(...) method and related Dictionary<,>:
private readonly Dictionary<Type, object> commandTargets;
internal TResult Execute<TTarget, TResult>(Func<TTarget, TResult> func) {
var result = default(TResult);
if (commandTargets.TryGetValue(typeof(TTarget), out object target)) {
result = func((TTarget)target);
}
return result;
}
OK, i am not sure if this is what you are looking for. I am assuming that each plugin contains field of the following definition:
public Command<T> {Name} = (o) => { return (T)o; };
example from code provided by you:
public Command<IDispatchingRepository> Dispatching = (o) => { return (IDispatchingRepository)o; };
One way to dynamically create class in .NET Core is by using the Microsoft.CodeAnalysis.CSharp nuget - this is Roslyn.
The result is compiled assembly with class called DynamicRepositories having all command fields from all plugins from all loaded dlls into the current AppDomain represented as static public fields.
The code has 3 main components: DynamicRepositoriesBuildInfo class, GetDynamicRepositoriesBuildInfo method and LoadDynamicRepositortyIntoAppDomain method.
DynamicRepositoriesBuildInfo - information for the command fields from the plugins and all assemblies needed to be loaded during the dynamic complication. This will be the assemblies which defines the Command type and the generic arguments of the Command type (ex: IDispatchingRepository)
GetDynamicRepositoriesBuildInfo method - creates DynamicRepositoriesBuildInfo using reflection by scanning loaded assemblies for the PluginEntryAttribute and PluginImplAttribute.
LoadDynamicRepositortyIntoAppDomain method - DynamicRepositoriesBuildInfo it creates assembly called DynamicRepository.dll with single public class App.Dynamic.DynamicRepositories
Here is the code
public class DynamicRepositoriesBuildInfo
{
public IReadOnlyCollection<Assembly> ReferencesAssemblies { get; }
public IReadOnlyCollection<FieldInfo> PluginCommandFieldInfos { get; }
public DynamicRepositoriesBuildInfo(
IReadOnlyCollection<Assembly> referencesAssemblies,
IReadOnlyCollection<FieldInfo> pluginCommandFieldInfos)
{
this.ReferencesAssemblies = referencesAssemblies;
this.PluginCommandFieldInfos = pluginCommandFieldInfos;
}
}
private static DynamicRepositoriesBuildInfo GetDynamicRepositoriesBuildInfo()
{
var pluginCommandProperties = (from a in AppDomain.CurrentDomain.GetAssemblies()
let entryAttr = a.GetCustomAttribute<PluginEntryAttribute>()
where entryAttr != null
from t in a.DefinedTypes
where t == entryAttr.PluginType
from p in t.GetFields(BindingFlags.Public | BindingFlags.Instance)
where p.FieldType.GetGenericTypeDefinition() == typeof(Command<>)
select p).ToList();
var referenceAssemblies = pluginCommandProperties
.Select(x => x.DeclaringType.Assembly)
.ToList();
referenceAssemblies.AddRange(
pluginCommandProperties
.SelectMany(x => x.FieldType.GetGenericArguments())
.Select(x => x.Assembly)
);
var buildInfo = new DynamicRepositoriesBuildInfo(
pluginCommandFieldInfos: pluginCommandProperties,
referencesAssemblies: referenceAssemblies.Distinct().ToList()
);
return buildInfo;
}
private static Assembly LoadDynamicRepositortyIntoAppDomain()
{
var buildInfo = GetDynamicRepositoriesBuildInfo();
var csScriptBuilder = new StringBuilder();
csScriptBuilder.AppendLine("using System;");
csScriptBuilder.AppendLine("namespace App.Dynamic");
csScriptBuilder.AppendLine("{");
csScriptBuilder.AppendLine(" public class DynamicRepositories");
csScriptBuilder.AppendLine(" {");
foreach (var commandFieldInfo in buildInfo.PluginCommandFieldInfos)
{
var commandNamespaceStr = commandFieldInfo.FieldType.Namespace;
var commandTypeStr = commandFieldInfo.FieldType.Name.Split('`')[0];
var commandGenericArgStr = commandFieldInfo.FieldType.GetGenericArguments().Single().FullName;
var commandFieldNameStr = commandFieldInfo.Name;
csScriptBuilder.AppendLine($"public {commandNamespaceStr}.{commandTypeStr}<{commandGenericArgStr}> {commandFieldNameStr} => (o) => ({commandGenericArgStr})o;");
}
csScriptBuilder.AppendLine(" }");
csScriptBuilder.AppendLine("}");
var sourceText = SourceText.From(csScriptBuilder.ToString());
var parseOpt = CSharpParseOptions.Default.WithLanguageVersion(LanguageVersion.CSharp7_3);
var syntaxTree = SyntaxFactory.ParseSyntaxTree(sourceText, parseOpt);
var references = new List<MetadataReference>
{
MetadataReference.CreateFromFile(typeof(object).Assembly.Location),
MetadataReference.CreateFromFile(typeof(System.Runtime.AssemblyTargetedPatchBandAttribute).Assembly.Location),
};
references.AddRange(buildInfo.ReferencesAssemblies.Select(a => MetadataReference.CreateFromFile(a.Location)));
var compileOpt = new CSharpCompilationOptions(OutputKind.DynamicallyLinkedLibrary,
optimizationLevel: OptimizationLevel.Release,
assemblyIdentityComparer: DesktopAssemblyIdentityComparer.Default);
var compilation = CSharpCompilation.Create(
"DynamicRepository.dll",
new[] { syntaxTree },
references: references,
options: compileOpt);
using (var memStream = new MemoryStream())
{
var result = compilation.Emit(memStream);
if (result.Success)
{
var assembly = AppDomain.CurrentDomain.Load(memStream.ToArray());
return assembly;
}
else
{
throw new ArgumentException();
}
}
}
This is how to execute the code
var assembly = LoadDynamicRepositortyIntoAppDomain();
var type = assembly.GetType("App.Dynamic.DynamicRepositories");
The type variable represents the compiled class which has all the plugin commands as public static fields. You are loosing all type safety once you start using dynamic code compilation / building. If you need to execute some code from the type variable you will need reflection.
So if you have
PluginA
{
public Command<IDispatchingRepository> Dispatching= (o) => ....
}
PluginB
{
public Command<IDispatchingRepository> Scheduling = (o) => ....
}
the dynamically create type will look like this
public class DynamicRepositories
{
public static Command<IDispatchingRepository> Dispatching= (o) => ....
public static Command<IDispatchingRepository> Scheduling = (o) => ....
}
Here's another take, which does not require building code dynamically.
I'm assuming the following code for the plugin framework. Note that I did not make any assumptions regarding the abstract Plugin class, because I had no further information.
#region Plugin Framework
public delegate TTarget Command<out TTarget>(object obj);
/// <summary>
/// Abstract base class for plugins.
/// </summary>
public abstract class Plugin
{
}
#endregion
Next, here are two sample plugins. Note the DynamicTarget custom attributes, which I will describe in the next step.
#region Sample Plugin: ICustomerRepository
/// <summary>
/// Sample model class, representing a customer.
/// </summary>
public class Customer
{
public Customer(string name)
{
Name = name;
}
public string Name { get; }
}
/// <summary>
/// Sample target interface.
/// </summary>
public interface ICustomerRepository
{
Customer AddCustomer(string name);
Customer GetCustomer(string name);
}
/// <summary>
/// Sample plugin.
/// </summary>
[DynamicTarget(typeof(ICustomerRepository))]
public class CustomerRepositoryPlugin : Plugin, ICustomerRepository
{
private readonly Dictionary<string, Customer> _customers = new Dictionary<string, Customer>();
public Customer AddCustomer(string name)
{
var customer = new Customer(name);
_customers[name] = customer;
return customer;
}
public Customer GetCustomer(string name)
{
return _customers[name];
}
}
#endregion
#region Sample Plugin: IProductRepository
/// <summary>
/// Sample model class, representing a product.
/// </summary>
public class Product
{
public Product(string name)
{
Name = name;
}
public string Name { get; }
}
/// <summary>
/// Sample target interface.
/// </summary>
public interface IProductRepository
{
Product AddProduct(string name);
Product GetProduct(string name);
}
/// <summary>
/// Sample plugin.
/// </summary>
[DynamicTarget(typeof(IProductRepository))]
public class ProductRepositoryPlugin : Plugin, IProductRepository
{
private readonly Dictionary<string, Product> _products = new Dictionary<string, Product>();
public Product AddProduct(string name)
{
var product = new Product(name);
_products[name] = product;
return product;
}
public Product GetProduct(string name)
{
return _products[name];
}
}
#endregion
Here's what your static Repositories class would look like with the two sample plugins:
#region Static Repositories Example Class from Question
public static class Repositories
{
public static readonly Command<ICustomerRepository> CustomerRepositoryCommand = o => (ICustomerRepository) o;
public static readonly Command<IProductRepository> ProductRepositoryCommand = o => (IProductRepository) o;
}
#endregion
To begin the actual answer to your question here's the custom attribute used to mark the plugins. This custom attribute has been used on the two example plugins shown above.
/// <summary>
/// Marks a plugin as the target of a <see cref="Command{TTarget}" />, specifying
/// the type to be registered with the <see cref="DynamicCommands" />.
/// </summary>
[AttributeUsage(AttributeTargets.Class, Inherited = false, AllowMultiple = true)]
public class DynamicTargetAttribute : Attribute
{
public DynamicTargetAttribute(Type type)
{
Type = type;
}
public Type Type { get; }
}
The custom attribute is parsed in the RegisterDynamicTargets(Assembly) of the following DynamicRepository class to identify the plugins and the types (e.g., ICustomerRepository) to be registered. The targets are registered with the CommandRouter shown below.
/// <summary>
/// A dynamic command repository.
/// </summary>
public static class DynamicCommands
{
/// <summary>
/// For all assemblies in the current domain, registers all targets marked with the
/// <see cref="DynamicTargetAttribute" />.
/// </summary>
public static void RegisterDynamicTargets()
{
foreach (Assembly assembly in AppDomain.CurrentDomain.GetAssemblies())
{
RegisterDynamicTargets(assembly);
}
}
/// <summary>
/// For the given <see cref="Assembly" />, registers all targets marked with the
/// <see cref="DynamicTargetAttribute" />.
/// </summary>
/// <param name="assembly"></param>
public static void RegisterDynamicTargets(Assembly assembly)
{
IEnumerable<Type> types = assembly
.GetTypes()
.Where(type => type.CustomAttributes
.Any(ca => ca.AttributeType == typeof(DynamicTargetAttribute)));
foreach (Type type in types)
{
// Note: This assumes that we simply instantiate an instance upon registration.
// You might have a different convention with your plugins (e.g., they might be
// singletons accessed via an Instance or Default property). Therefore, you
// might have to change this.
object target = Activator.CreateInstance(type);
IEnumerable<CustomAttributeData> customAttributes = type.CustomAttributes
.Where(ca => ca.AttributeType == typeof(DynamicTargetAttribute));
foreach (CustomAttributeData customAttribute in customAttributes)
{
CustomAttributeTypedArgument argument = customAttribute.ConstructorArguments.First();
CommandRouter.Default.RegisterTarget((Type) argument.Value, target);
}
}
}
/// <summary>
/// Registers the given target.
/// </summary>
/// <typeparam name="TTarget">The type of the target.</typeparam>
/// <param name="target">The target.</param>
public static void RegisterTarget<TTarget>(TTarget target)
{
CommandRouter.Default.RegisterTarget(target);
}
/// <summary>
/// Gets the <see cref="Command{TTarget}" /> for the given <typeparamref name="TTarget" />
/// type.
/// </summary>
/// <typeparam name="TTarget">The target type.</typeparam>
/// <returns>The <see cref="Command{TTarget}" />.</returns>
public static Command<TTarget> Get<TTarget>()
{
return obj => (TTarget) obj;
}
/// <summary>
/// Extension method used to help dispatch the command.
/// </summary>
/// <typeparam name="TTarget">The type of the target.</typeparam>
/// <typeparam name="TResult">The type of the result of the function invoked on the target.</typeparam>
/// <param name="_">The <see cref="Command{TTarget}" />.</param>
/// <param name="func">The function invoked on the target.</param>
/// <returns>The result of the function invoked on the target.</returns>
public static TResult Execute<TTarget, TResult>(this Command<TTarget> _, Func<TTarget, TResult> func)
{
return CommandRouter.Default.Execute(func);
}
}
Instead of dynamically creating properties, the above utility class offers a simple Command<TTarget> Get<TTarget>() method, with which you can create the Command<TTarget> instance, which is then used in the Execute extension method. The latter method finally delegates to the CommandRouter shown next.
/// <summary>
/// Command router used to dispatch commands to targets.
/// </summary>
public class CommandRouter
{
public static readonly CommandRouter Default = new CommandRouter();
private readonly Dictionary<Type, object> _commandTargets = new Dictionary<Type, object>();
/// <summary>
/// Registers a target.
/// </summary>
/// <typeparam name="TTarget">The type of the target instance.</typeparam>
/// <param name="target">The target instance.</param>
public void RegisterTarget<TTarget>(TTarget target)
{
_commandTargets[typeof(TTarget)] = target;
}
/// <summary>
/// Registers a target instance by <see cref="Type" />.
/// </summary>
/// <param name="type">The <see cref="Type" /> of the target.</param>
/// <param name="target">The target instance.</param>
public void RegisterTarget(Type type, object target)
{
_commandTargets[type] = target;
}
internal TResult Execute<TTarget, TResult>(Func<TTarget, TResult> func)
{
var result = default(TResult);
if (_commandTargets.TryGetValue(typeof(TTarget), out object target))
{
result = func((TTarget)target);
}
return result;
}
}
#endregion
Finally, here are a few unit tests showing how the above classes work.
#region Unit Tests
public class DynamicCommandTests
{
[Fact]
public void TestUsingStaticRepository_StaticDeclaration_Success()
{
ICustomerRepository customerRepository = new CustomerRepositoryPlugin();
CommandRouter.Default.RegisterTarget(customerRepository);
Command<ICustomerRepository> command = Repositories.CustomerRepositoryCommand;
Customer expected = command.Execute(repo => repo.AddCustomer("Bob"));
Customer actual = command.Execute(repo => repo.GetCustomer("Bob"));
Assert.Equal(expected, actual);
Assert.Equal("Bob", actual.Name);
}
[Fact]
public void TestUsingDynamicRepository_ManualRegistration_Success()
{
ICustomerRepository customerRepository = new CustomerRepositoryPlugin();
DynamicCommands.RegisterTarget(customerRepository);
Command<ICustomerRepository> command = DynamicCommands.Get<ICustomerRepository>();
Customer expected = command.Execute(repo => repo.AddCustomer("Bob"));
Customer actual = command.Execute(repo => repo.GetCustomer("Bob"));
Assert.Equal(expected, actual);
Assert.Equal("Bob", actual.Name);
}
[Fact]
public void TestUsingDynamicRepository_DynamicRegistration_Success()
{
// Register all plugins, i.e., CustomerRepositoryPlugin and ProductRepositoryPlugin
// in this test case.
DynamicCommands.RegisterDynamicTargets();
// Invoke ICustomerRepository methods on CustomerRepositoryPlugin target.
Command<ICustomerRepository> customerCommand = DynamicCommands.Get<ICustomerRepository>();
Customer expectedBob = customerCommand.Execute(repo => repo.AddCustomer("Bob"));
Customer actualBob = customerCommand.Execute(repo => repo.GetCustomer("Bob"));
Assert.Equal(expectedBob, actualBob);
Assert.Equal("Bob", actualBob.Name);
// Invoke IProductRepository methods on ProductRepositoryPlugin target.
Command<IProductRepository> productCommand = DynamicCommands.Get<IProductRepository>();
Product expectedHammer = productCommand.Execute(repo => repo.AddProduct("Hammer"));
Product actualHammer = productCommand.Execute(repo => repo.GetProduct("Hammer"));
Assert.Equal(expectedHammer, actualHammer);
Assert.Equal("Hammer", actualHammer.Name);
}
}
#endregion
You can find the whole implementation here.

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);
}
}

Autofac and Background processes

I am using WebApi 2 and have the need to fire off a process in the background. I need it to do it's stuff and not affect the current request.
So after some digging around I found that I could just use
public void Save(Order model) => Task.Run(() => postmanService.Update(model));
I wanted to debug it to make sure it was working, so I change it to this:
public void Save(Order model) => await postmanService.Update(model).ConfigureAwait(false);
But when I run this I get this error:
"The ObjectContext instance has been disposed and can no longer be used for operations that require a connection."
I am not sure why this is happening because my DatabaseContext is registered like this:
builder.RegisterType<DatabaseContext>().As<DbContext>().InstancePerLifetimeScope();
builder.RegisterType<UnitOfWork>().As<IUnitOfWork>().InstancePerLifetimeScope();
builder.RegisterType<CollectionManagerProvider>().As<ICollectionManagerProvider>().InstancePerRequest();
builder.RegisterType<PostmanService>().As<IPostmanService>();
The PostmanService is what saves the data to the database and it looks like this:
public class PostmanService : Service<Postman>, IPostmanService
{
public PostmanService(IUnitOfWork unitOfWork) : base(unitOfWork)
{}
/// <summary>
/// Save or update a message
/// </summary>
/// <param name="model"></param>
public void Save(Postman model)
{
if (model.Id == 0)
Repository.Create(model);
else
Repository.Update(model);
}
/////////--------- Removed for brevity ---------/////////
}
The repository looks like this:
public class Repository<T> : IRepository<T> where T : class
{
// Create our private properties
private readonly DbContext _context;
private readonly DbSet<T> _dbEntitySet;
/// <summary>
/// Default constructor
/// </summary>
/// <param name="context">The database context</param>
public Repository(DbContext context)
{
// Assign our context and entity set
_context = context ?? throw new ArgumentNullException("context");
_dbEntitySet = context.Set<T>();
}
/// <summary>
/// Creates an entity
/// </summary>
/// <param name="model"></param>
public void Create(T model) => _dbEntitySet.Add(model);
/// <summary>
/// Updates an entity
/// </summary>
/// <param name="model"></param>
public void Update(T model) => _context.Entry<T>(model).State = EntityState.Modified;
/////////--------- Removed for brevity ---------/////////
}
And finally, the unit of work looks like this:
public class UnitOfWork : IUnitOfWork
{
private readonly Dictionary<Type, object> _repositories;
// Public properties
public DbContext Context { get; }
/// <summary>
/// Default constructor
/// </summary>
public UnitOfWork(DbContext context)
{
Context = context;
_repositories = new Dictionary<Type, object>();
}
/// <summary>
/// Gets the entity repository
/// </summary>
/// <typeparam name="TEntity">The entity model</typeparam>
/// <returns></returns>
public IRepository<TEntity> GetRepository<TEntity>() where TEntity : class
{
// If our repositories have a matching repository, return it
if (_repositories.Keys.Contains(typeof(TEntity)))
return _repositories[typeof(TEntity)] as IRepository<TEntity>;
// Create a new repository for our entity
var repository = new Repository<TEntity>(Context);
// Add to our list of repositories
_repositories.Add(typeof(TEntity), repository);
// Return our repository
return repository;
}
/// <summary>
/// Saves the database changes asynchronously
/// </summary>
/// <returns></returns>
public async Task SaveChangesAsync()
{
try
{
// Save the changes to the database
await Context.SaveChangesAsync();
}
catch (DbEntityValidationException ex) {
// Retrieve the error messages as a list of strings.
var errorMessages = ex.EntityValidationErrors.SelectMany(x => x.ValidationErrors).Select(x => x.ErrorMessage);
// Join the list to a single string.
var fullErrorMessage = string.Join("; ", errorMessages);
// Combine the original exception message with the new one.
var exceptionMessage = string.Concat(ex.Message, " The validation errors are: ", fullErrorMessage);
// Throw a new DbEntityValidationException with the improved exception message.
throw new DbEntityValidationException(exceptionMessage, ex.EntityValidationErrors);
}
}
}
I am not sure if the issue is with the CollectionManagerProvider and how that is registered (Because I have registered it as InstancePerRequest and techinically the method is being fired on a different thread.
Can someone help me with this?
One option would be to use Owned - http://docs.autofac.org/en/latest/advanced/owned-instances.html , and take the responsibility of disposal upon yourself.
public Repository(Owned<DbContext> context)
public PostmanService(Owned<IUnitOfWork> unitOfWork)
etc etc
or:
builder.RegisterType<DatabaseContext>().As<DbContext>().Inst‌​ancePerLifetimeScope‌​().ExternallyOwned()‌​;
builder.RegisterType<UnitOfWork>().As<IUnitOfWork>().InstancePerLifetimeScope().ExternallyOwned()‌​;
etc etc
See:
http://docs.autofac.org/en/latest/advanced/owned-instances.html
http://docs.autofac.org/en/latest/lifetime/disposal.html

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);

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