Question:
How can I pass a string representing a table name to an NHibernate ClassMap?
Details:
I have several databases which are almost the same, with some minor variations in table and view names. I would like to be able to handle these variations via configuration parameters in my app.config file. For example, I could specify the table names for a particular configuration using the following custom section:
<tableNames>
<add key="logicalTable1" value="ACTUAL_TABLE_1"/>
<add key="logicalTable2" value="ACTUAL_TABLE_2"/>
</tablenames>
Now, if I load these config parameters at runtime, how do I get the table name into the ClassMap (i.e.,
public class MyClassMap : ClassMap<MyClass>
{
public class MyClassMap()
{
Table("ACTUAL_TABLE_1"); // <--- HERE I WANT Table(logicaTable1)
...
}
Note: I am using Ninject to inject ISessionFactory -- not sure if this matters.
Unless I am missing something, you could just use the ConfigurationManager class:
public class MyClassMap()
{
var table = ConfigurationManager.AppSettings["logicalTable2"];
Table(table);
...
}
Your project would need to reference the System.Configuration assembly.
Update:
Or use a "TableNameProvider" class:
public interface ITableNameProvider
{
string LogicalTable1 { get; }
}
public class TableNameProvider : ITableNameProvider
{
public string LogicalTable1 { get { return ConfigurationManager.AppSettings["logicalTable1"]; } }
}
I'm not sure about exactly how to do this with Ninject, but assuming its like other containers:
public class MyClassMap()
{
var provider = GetMyContainer().GetInstance<ITableProvider>();
var table = provider.LogicalTable1;
Table(table);
...
}
This way you would just need to change your TableNameProvider class.
I'm not sure if you could inject a MappingFactory or something into Fluent-NHibernate to handle mapping file dependencies and true injection. Something to look into.
Related
I created a generic user repository base class that provides reusable user management functionality.
public class UserRepository<TUser> where TUser : new, IUser
{
}
I have a concrete implementation of IUser called UserImpl, and corresponding mapping class UserImplMap : ClassMap<UserImpl> (they all are in the same namespace and assembly). I add the mapping using AddFromAssemblyOf . I also use this to create / generate the schema.
So far so good and things work as expected.
Now, in a different project, I needed a few additional properties in my IUser implementation class, so I implemented a new class UserImplEx : UserImpl. This class has the additional properties that I needed. Also, I created a new mapping class UserImplExMap : SubclassMap<UserImplEx>
Now when I create schema using this approach, I get two tables one for UserImpl and one for UserImplEx.
Is is possible to configure / code Fluent mapping in some way so that all the properties (self, plus inherited) of UserImplEx get mapped in a single table UserImplEx instead of getting split into two tables?
Alternatively, if I provide full mapping in UserImplExMap : ClassMap<UserImplEx>, then I do get the schema as desired, but I also get an additional table for UserImpl (because corresponding mapping is present in the UserRepository assembly). If I follow this approach, is there a way to tell AddFromAssemblyOf to exclude specific mapping classes?
Option 1
since you have inhertance here and want the correct type back NH has to store the type somewhere, either through the table the data is in or a discriminator.
If a discriminator column in the table does not matter then add DiscriminatorColumn("userType", "user"); in UserImplMap and DiscriminatorValue("userEx") in UserImplExMap
Option 2
class MyTypeSource : ITypeSource
{
private ITypeSource _inner = new AssemblyTypeSource(typeof(UserImplMap).Assembly);
public IEnumerable<Type> GetTypes()
{
return _inner.Where(t => t != typeof(UserImplMap)).Concat(new [] { typeof(UserImplExMap) });
}
public void LogSource(IDiagnosticLogger logger)
{
_inner.LogSource(logger);
}
public string GetIdentifier()
{
return _inner.GetIdentifier();
}
}
and when configuring
.Mappings(m =>
{
var model = new PersistenceModel();
PersistenceModel.AddMappingsFromSource(new MyTypeSource());
m.UsePersistenceModel(model);
})
I am getting this error when I try to use code first migrations.
My context has a constructor with the connection name.
public class VeraContext : DbContext, IDbContext
{
public VeraContext(string NameOrConnectionStringName = "VeraDB")
: base(NameOrConnectionStringName)
{
}
public IDbSet<User> Users { get; set; }
public IDbSet<Product> Products { get; set; }
public IDbSet<IntCat> IntCats { get; set; }
}
This connection name is injected with ninject when the project runs, I have also specified it as a default as in the above code but this did not help.
kernel.Bind<IDbContext>()
.To<VeraContext>()
.WithConstructorArgument("NameOrConnectionStringName", "VeraDB");
When I try to add migrations with "Enable-Migrations" is throws up the error:
The target context 'VeraData.EF.Infrastructure.VeraContext' is not
constructible. Add a default constructor or provide an implementation
of IDbContextFactory.
If I remove the constructor from VeraContext it will work but creates another database with VeraData.EF.Infrastructure.VeraContext as its name.
I presume that ninject only passes the connection string when the project runs and not when I use code first migrations. Anyway I can inject/provide a default for the connection name when using code first migrations ?
Essentially you need a default ctor (that's the error) - but just implementing it would lead to problems.
You'd have to implement the IDbContextFactory for the results to be consistent (or your migration from code won't work etc.).
Migrations actually call your default constructor to make a
connection. So you're other ctor won't matter much.
Here is the basic factory...
public class MyContextFactory : IDbContextFactory<MyContext>
{
public MyContext Create()
{
return new MyDBContext("YourConnectionName");
}
}
You should combine that with injection, to inject and construct your DbContext as you wish.
If you don't want to spend time looking into the IDbContextFactory option, and to get things working create a default constructor and hard-code the name of the connection string when calling the base DbContext:
public class CustomContext : DbContext
{
public CustomContext() :base("name=Entities") {}
}
SRC: http://www.appetere.com/Blogs/SteveM/April-2012/Entity-Framework-Code-First-Migrations
To complement #nccsbim071 answer, I have to add one more thing... this option doesn't like constructor with default parameters... for instance:
public MyContext(bool paramABC = false) : base("name=Entities") {...}
instead you have to create a non-parameter (default) constructor and the parameter-constructor like old fashion way.
public MyContext() :base("name=Entities") {...}
public MyContext(bool paramABC) : this() {...}
NOTE:
Entities in this case means the connection string name... By convention, the name of the context is the same as the connection string name and since MyContext is not the same as Entities, it's necessary specify it manually.
In my situation I wanted to use the default connection factory, instead of explicitly providing one. Somewhere inside EF6 it'll try to lookup the factory, but it fails with this exception message. Stepping through the EF6 code, I found that Glimpse.Ado was wrapping the connection factory, which made the lookup fail to find a match.
I am getting an error : i am using entity framework, wcf.
Error:cannot implicitly convert type System.linq.iorderedQueryable<xDataModel.Info> to System.Collection.Generic.List<xServiceLibrary.Info>
Below are my code:
WCF Service:
namespace xServiceLibrary
{
public List<Info> GetScenario()
{
xEntities db = new xEntities();
var query = from qinfo in db.Infoes
select qinfo;
//return query.Cast<Info>().ToList(); (not working)
//return query.toList(); (not working)
return query;
}
}
Interface:
namespace xServiceLibrary
{
[OperationContract]
List<Info> GetScenario();
}
Class:
namespace xServiceLibrary
{
[DataContract]
public class Info
{
[DataMember]
public int Scenario_Id;
[DataMember]
public string Scenario_Name { get; set; }
[DataMember]
public string Company_Name { get; set; }
}
}
update:(2)
I have two class library files.
One is xDataModel namespace in which i have created xmodel.edmx file.
second is xServiceLibrary namespace where i am implementing Wcf Service.
i have attached the xDataModel.dll file in my xServiceLibrary so that i could query my EF Model.
i am not able to understand the concept. any help would be appreciated.
The problem is that you have two different types named Info: DataModel.Info and ServiceLibrary.Info - because these are different types you cannot cast one into the other.
If there is no strong reason for both being there I would eliminate one of them. Otherwise as a workaround you could project DataModel.Info to ServiceLibrary.Info by copying the relevant properties one by one:
var results = (from qinfo in db.Infoes
select new ServiceLibrary.Info()
{
Scenario_Id = qinfo.Scenario_Id,
//and so on
}).ToList();
The problem is that you have two different classes, both called Info, both in scope at the time you run your query. This is a very very bad thing, especially if you thought they were the same class.
If DataModel.Info and ServiceLibrary.Info are the same class, you need to figure out why they are both in scope at the same time and fix that.
If they are different classes, you need to be explicit about which one you are trying to return. Assuming that your EF model includes a set of DataModel.Info objects, your options there are:
Return a List<DataModel.Info> which you can get by calling query.ToList()
Return a List<ServiceLibrary.Info> which you can get by copying the fields from your DataModel.Info objects:
var query = from qinfo in db.Info
select new ServiceLibrary.Info
{
Scenario_Id = q.Scenario_Id,
Scenario_Name = q.Scenario_Name
Company_Name = q.Company_Name
};
Return something else, such as your custom DTO object, similar to #2 but with only the specific fields you need (e.g. if ServiceLibrary.Info is a heavy object you don't want to pass around.
In general, though, your problem is centered around the fact that the compiler is interpreting List<Info> as List<ServiceLibrary.Info> and you probably don't want it to.
I have the following scenario in my Silverlight 4 application:
public class TheViewModel
{
[Import()]
public TheChild Child { get; set; }
}
[Export()]
public class TheChild
{
[ImportingConstructor()]
public TheChild(String myName, IAmTheService service) { ... }
}
[Export(typeof(IAmTheService))]
public class TheService : IAmTheService
{
public void DoSomething(String theName);
}
As you can see, TheChild's constructor requires one imported parameter and one static value that is context-sensitive (has to be provided by the parent). The string value cannot come from AppSettings, configuration, etc. and can only be provided by the current instance of the parent class (TheViewModel in this case).
As a rule-of-thumb, I've always approached dependency-injection as follows:
Required dependencies are satisfied through constructor injection
Optional dependencies are satisfied through property injection
The "myName" parameter is required so I would prefer to set it through the constructor but given the way MEF works, I realize this may have to change.
Can you tell me how you've handled this scenario and your thoughts behind the solution?
You can specify a specific import contract in conjunction with [ImportingConstructor]. For example:
[Export()]
public class TheChild
{
[ImportingConstructor()]
public TheChild([Import("MyName")] String myName, IAmTheService service) { ... }
Given that, an export of a string decorated with [Export("MyName")] will be required and used to fulfill the dependency. Any of the [Import] specifications should work in this case (ie: importing a subclass by type, importing by name, etc).
Does anybody know if it is possible to control the names of the types generated through Castle DynamicProxy? I was hoping to take advantage of the ability to persist the assembly generated by Castle to add some additional classes with some specific functionality to my project, but I would like to be able to control the names of these generated proxy types. Any help would be greatly appreciated.
I actually plan to persist instances of these classes as well as instances of the original classes that are the sources of the proxies with NHibernate. So, I need these names to be consistent across multiple generations of the assembly.
I did some interesting digging. Specifying proxy names appears to be possible using an INamingScope, but it is far from straightforward to get the INamingScope wedged in. You would need to create your own ProxyFactoryFactory, which would create a ProxyFactory identical to NHibernate.ByteCode.Castle.ProxyFactory, except it would initilize ProxyGenerator:
public class CustomProxyFactory : AbstractProxyFactory {
private static readonly ProxyGenerator ProxyGenerator = new ProxyGenerator(new CustomProxyBuilder());
// remainder of code is identical
}
public class CustomProxyBuilder : DefaultProxyBuilder {
public CustomProxyBuilder() : base(new CustomModuleScope()) {}
}
public class CustomModuleScope : ModuleScope {
public CustomModuleScope() : base(false, false, new CustomNamingScope(), DEFAULT_ASSEMBLY_NAME, DEFAULT_FILE_NAME, DEFAULT_ASSEMBLY_NAME, DEFAULT_FILE_NAME) {}
}
public class CustomNamingScope : INamingScope {
public CustomNamingScope() {}
private CustomNamingScope(INamingScope parent) {
ParentScope = parent;
}
public string GetUniqueName(string suggestedName) {
// your naming logic goes here
}
public INamingScope SafeSubScope() {
return new CustomModuleScope(this);
}
public INamingScope ParentScope { get; private set; }
}
I honestly haven't tried running or compiling any of this. Just digging through the NHibernate and Castle.Core source code. Hopefully it gives you some ideas...
Take a look at the ProxyGenerators project in NHContrib. It allows you to pre-generate NHibernate's lazy loading proxies.
http://nhforge.org/wikis/proxygenerators10/default.aspx
Whether you use the ProxyGenerators or not, you integrate your custom proxies into NHibernate via the Proxy Factory Factory. In hibernate.cfg.xml:
<hibernate-configuration xmlns="urn:nhibernate-configuration-2.2">
<session-factory>
<property name="proxyfactory.factory_class">YOUR_PROXY_FACTORY_FACTORY</property>
</session-factory>
</hibernate-configuration>