Setting lifetime manager for registrations done using UnityConfiguration scanner - configuration

I have a ASP.NET MVC4 application and am using Unity for IOC. I am using Unity.MVC4 and UnityConfiguration Nuget packages to help with the registration.
I need to automatically register a load of interfaces and their related types to the Unity container. To do this I created a dummy interface; IDependencyInjectionScanner that all my real interfaces inherit from. Below is the code showing that.
public interface IDependencyInjectionScanner
{
}
public interface IChair : IDependencyInjectionScanner
{
NumberOfLegs { get; set; }
}
public class Chair : IChair
{
public NumberOfLegs { get; set; }
}
public interface ITable : IDependencyInjectionScanner
{
NumberOfChairs { get; set; }
}
public class Table : ITable
{
public NumberOfChairs { get; set; }
}
I then used UnityConfiguration to bind the registrations using the scanner. I have get the interfaces being correctly resolved in the controller. Below is the code that shows how I did the binding.
Scan(scan =>
{
scan.AssembliesInDirectory(Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "bin"));
scan.With<FirstInterfaceConvention>();
scan.Include(x => (x.GetInterface(typeof(IDependencyInjectionScanner).Name) != null));
scan.ForRegistries();
});
The problem is that I want to register all the types found by the scanner using the hierarchical lifetime manager but can figure out how to do this. The GitHub page for UnityConfiguration https://github.com/thedersen/UnityConfiguration states that this could be achieved by the code below:
Configure<IChair>().AsHierarchicalControlled();
However I if I have to do that for each of the interfaces bound by the scanner then the scanner is of no use as I may as well do:
Register<IChair, Chair>().AsHierarchicalControlled();
Can someone assist me with finding a solution to this please.

Here's an answer to your question using UnityConfiguration. You can create a custom convention to configure the lifetime. Just be careful because it looks like the calls within the Scan() method are order dependent.
public class HierarchicalLifetimeConvention : IAssemblyScannerConvention
{
public void Process(Type type, IUnityRegistry registry)
{
registry.Configure(type).AsHierarchicalControlled();
}
}
and then add that to your Scan() call...
Scan(scan =>
{
scan.AssembliesInDirectory(Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "bin"));
scan.With<FirstInterfaceConvention>();
scan.With<HierarchicalLifetimeConvention>(); //<-- New convention
scan.Include(x => (x.GetInterface(typeof(IDependencyInjectionScanner).Name) != null));
scan.ForRegistries();
});

As suggested by #TylerOhlsen I used the built-in Registration by Convention feature of Unity 3.0. I have got it to add the registration mappings and they are using the hierarchical lifetime manager. below is the code for that
container.RegisterTypes(
AllClasses.FromLoadedAssemblies().Where(
t => t.GetInterface(typeof(IDependencyInjectionScanner).Name) != null),
WithMappings.FromMatchingInterface,
WithName.Default,
WithLifetime.Hierarchical);
There is one thing that is disturbing me; when I look at the registrations I have 4 (based on the example code above). 2 type mappings for the Chair type and 2 type mappings for the Table type.
Can anyone shed any light on why this is, as I was only expecting two mappings.

Related

Entity Framework Code First Update Does Not Update Foreign Key

I'm using EF 4.1 Code First. I have an entity defined with a property like this:
public class Publication
{
// other stuff
public virtual MailoutTemplate Template { get; set; }
}
I've configured this foreign key using fluent style like so:
modelBuilder.Entity<Publication>()
.HasOptional(p => p.Template)
.WithMany()
.Map(p => p.MapKey("MailoutTemplateID"));
I have an MVC form handler with some code in it that looks like this:
public void Handle(PublicationEditViewModel publicationEditViewModel)
{
Publication publication = Mapper.Map<PublicationEditViewModel, Publication>(publicationEditViewModel);
publication.Template = _mailoutTemplateRepository.Get(publicationEditViewModel.Template.Id);
if (publication.Id == 0)
{
_publicationRepository.Add(publication);
}
else
{
_publicationRepository.Update(publication);
}
_unitOfWork.Commit();
}
In this case, we're updating an existing Publication entity, so we're going through the else path. When the _unitOfWork.Commit() fires, an UPDATE is sent to the database that I can see in SQL Profiler and Intellitrace, but it does NOT include the MailoutTemplateID in the update.
What's the trick to get it to actually update the Template?
Repository Code:
public virtual void Update(TEntity entity)
{
_dataContext.Entry(entity).State = EntityState.Modified;
}
public virtual TEntity Get(int id)
{
return _dbSet.Find(id);
}
UnitOfWork Code:
public void Commit()
{
_dbContext.SaveChanges();
}
depends on your repository code. :) If you were setting publication.Template while Publication was being tracked by the context, I would expect it to work. When you are disconnected and then attach (with the scenario that you have a navigation property but no explicit FK property) I'm guessing the context just doesn't have enough info to work out the details when SaveChanges is called. I'd do some experiments. 1) do an integration test where you query the pub and keep it attached to the context, then add the template, then save. 2) stick a MailOutTemplateId property on the Publicaction class and see if it works. Not suggesting #2 as a solution, just as a way of groking the behavior. I"m tempted to do this experiment, but got some other work I need to do. ;)
I found a way to make it work. The reason why I didn't initially want to have to do a Get() (aside from the extra DB hit) was that then I couldn't do this bit of AutoMapper magic to get the values:
Publication publication = Mapper.Map<PublicationEditViewModel, Publication>(publicationEditViewModel);
However, I found another way to do the same thing that doesn't use a return value, so I updated my method like so and this works:
public void Handle(PublicationEditViewModel publicationEditViewModel)
{
Publication publication = _publicationRepository.Get(publicationEditViewModel.Id);
_mappingEngine.Map(publicationEditViewModel, publication);
// publication = Mapper.Map<PublicationEditViewModel, Publication>(publicationEditViewModel);
publication.Template = _mailoutTemplateRepository.Get(publicationEditViewModel.Template.Id);
if (publication.Id == 0)
{
_publicationRepository.Add(publication);
}
else
{
_publicationRepository.Update(publication);
}
_unitOfWork.Commit();
}
I'm injecting an IMappingEngine now into the class, and have wired it up via StructureMap like so:
For<IMappingEngine>().Use(() => Mapper.Engine);
For more on this, check out Jimmy's AutoMapper and IOC post.

Castle: using an existing (not single) instance for a lower-level dependency

I have a model roughly like this:
public interface IUnitOfWork { }
public class UnitOfWork : IUnitOfWork { }
public interface IService { }
public class Service : IService
{
public IUnitOfWork UnitOfWork { get; set; }
}
public class ViewModel
{
public IService Service { get; set; }
}
And a configuration that could be like this:
container.Register(Component.For<IService>().ImplementedBy<Service>()
.LifeStyle.Transient
Component.For<IUnitOfWork>().ImplementedBy<UnitOfWork>()
.LifeStyle.Transient,
Component.For<ViewModel>().LifeStyle.Transient);
I need to resolve, at different points, two instances of ViewModel (I'm using a typed factory for this, but let's leave that aside for simplicity and assume I'm using the raw container)
The catch is that I need to resolve two instances of ViewModel at different points (from another ViewModel that knows about both), and they need to share the same IUnitOfWork.
So, something like this:
var vm1 = container.Resolve<ViewModel>();
//...later
var vm2 = container.Resolve<ViewModel>();
Now, it's very easy to share the Service. I'd just have to do something like:
var vm2 = container.Resolve<ViewModel>(new { vm1.Service });
But of course the actual model is more complicated (different ViewModels, with more Services each), so that's not an option.
I can pass the UnitOfWork to Resolve, but it doesn't get used by default (which makes sense). Is there any way to use that parameter (probably by registering a delegate somewhere) when resolving the second ViewModel?
I'd like to be able to do the following:
var vm2 = container.Resolve<ViewModel>(new { UnitOfWork });
And get a ViewModel whose Service has that specific UnitOfWork.
If you need to share a component and you cannot set as singleton(rich client) or perwebrequest, you need to use Contextual lifestyle.
check this thread see my last comment to downoload contrib w/ Contextual Lifestyle
For you case I assume those 2 ViewModel will be used by 1 View... so View + UoW require Contextual Lifestyle
check also this one too see comments at the end
The solution was to use ContextualLifestyle coupled with a custom factory that kept a reference to the ContainerContext, in order to use the same one when resolving another ViewModel.

Entity Framework/Linq to sql model to business model

I'm coming from a stored procedure and creating the data access layer manually approach. I am trying to understand where I should fit Linq To SQL or entity frameworks into my normal planning. I normally seperate out the business layer from the DAL layer and use a repository inbetween.
It seems that people will either use the generated classes from linq to sql, extend them by using the partial class or do a full seperation and map the generated linq classes to seperate business entities. I am partial to the seperate Business entities. However, this seems to be counterintuitive.
One of my last projects used DDD and the entity framework. When needing to udpate an object it moved the business entity to the repistory layer which when going to the DAL layer would create a context and than requery the object. It would than update the values and resbumit.
I didn't see the large point as the data context wasn't saved and required an extra query to grab the object before updating. Normally I would just do the update(If concurrency wasn't an issue)
So my questions come down to:
Does it make sense to seperate linq to sql generated classes into Business entities?
Should the data context be saved or is that impractical?
Thanks for your time, trying to make sure I understand. I normally like to seperate out as it makes it cleaner to understand even in some smaller porjects.
I currently hand roll my own Dto classes and Datacontext instead of using auto-generated code files from Linq to Sql. To give some background of my solution architecture/modeling, I have a "Contract" project, and a "Dal" project. (Also a "Model" project, but I'll try to stay focused here on Dal only). Hand-rolling my own Dtos and Datacontext, makes everything a lot smaller and simpler, I'll give a few examples of how I do that here.
I never return out a Dto object outside of the Dal, in fact I make sure to declare them as internal. The way I return them out is I cast them as an interface (interfaces are located in my "Contract" layer). We'll make a simple "PersonRepository" that implements an "IPersonRetriever and IPersonSaver" interfaces.
Contracts:
public interface IPersonRetriever
{
IPerson GetPersonById(Guid personId);
}
public interface IPersonSaver
{
void SavePerson(IPerson person);
}
Dal:
public class PersonRepository : IPersonSaver, IPersonRetriever
{
private string _connectionString;
public PersonRepository(string connectionString)
{
_connectionString = connectionString;
}
IPerson IPersonRetriever.GetPersonById(Guid id)
{
using (var dc = new PersonDataContext(_connectionString))
{
return dc.PersonDtos.FirstOrDefault(p => p.PersonId == id);
}
}
void IPersonSaver.SavePerson(IPerson person)
{
using (var dc = new PersonDataContext(_connectionString))
{
var personDto = new PersonDto
{
Id = person.Id,
FirstName = person.FirstName,
Age = person.Age
};
dc.PersonDtos.InsertOnSubmit(personDto);
dc.SubmitChanges();
}
}
}
PersonDataContext:
internal class PersonDataContext : System.Data.Linq.DataContext
{
static MappingSource _mappingSource = new AttributeMappingSource(); // necessary for pre-compiled linq queries in .Net 4.0+
internal PersonDataContext(string connectionString) : base(connectionString, _mappingSource) { }
internal Table<PersonDto> PersonDtos { get { return GetTable<PersonDto>(); } }
}
[Table(Name = "dbo.Persons")]
internal class PersonDto : IPerson
{
[Column(Name = "PersonIdentityId", IsPrimaryKey = true, IsDbGenerated = false)]
internal Guid Id { get; set; }
[Column]
internal string FirstName { get; set; }
[Column]
internal int Age { get; set; }
#region IPerson implementation
Guid IPerson.Id { get { return this.Id; } }
string IPerson.FirstName { get { return this.FirstName; } }
int IPerson.Age { get { return this.Age; } }
#endregion
}
You will need to add the "Column" attribute to all of your Dto properties, but if you notice, if there is a one-to-one correlation between what you want the field to be exposed as on the interface, and the name of the actual table column, you won't need to add any of the Named Parameters. In this example my PersonId in the database is stored as "PersonIdentityId", yet I only want my interface to make the field say "Id".
That's how I do my Dal layer, I believe this layer should be dumb, real dumb. Dumb in the sense that it is only there for CRUD (Create, Retrieve, Update and Delete) operations. All of the business logic would go into my "Model" project, which would consume and utilize the IPersonSaver and IPersonRetriever interfaces.
Hope this helps!

PLINQO / LINQ-To-SQL - Generated Entity Self Save Method?

Hi I'm trying to create a basic data model / layer
The idea is to have:
Task task = TaskRepository.GetTask(2);
task.Description = "The task has changed";
task.Save();
Is this possible? I've tried the code below
Note: The TaskRepository.GetTask() methods detaches the Task entity.
I'd expect this to work, any ideas why it doesnt?
Thanks
public partial class Task
{
// Place custom code here.
public void Save()
{
using (TinyTaskDataContext db = new TinyTaskDataContext { Log = Console.Out })
{
db.Task.Attach(this);
db.SubmitChanges();
}
}
#region Metadata
// For more information about how to use the metadata class visit:
// http://www.plinqo.com/metadata.ashx
[CodeSmith.Data.Audit.Audit]
internal class Metadata
{
// WARNING: Only attributes inside of this class will be preserved.
public int TaskId { get; set; }
[Required]
public string Name { get; set; }
[Now(EntityState.New)]
[CodeSmith.Data.Audit.NotAudited]
public System.DateTime DateCreated { get; set; }
}
#endregion
}
Having done some reading I've realised I was implmenting the Repository pattern incorrectly. I should have been adding the Save method to the repository for conventions sake.
However, the actually problem I was having with regard to commiting the disconnected dataset was due to optimistic concurrency. The datacontext's job is to keep track of the state of it's entities. When entities become disconnected you loose that state.
I've found you need to add a timestamp field to the database table or I can set the UpdateCheck field on each column in my dbml file.
Here is some info about the UpdateCheck
Some useful links about disconnected Linq and plinqo
Great info on implementing the Repository pattern with LINQ
Short tutorial for implementing for updating and reattaching entities
Previously answer question
Rick Strahl on LINQ to SQL and attaching Entities
There is no need for this line (Task task = new Task();). The above should work although I've never seen it implemented in this manner. Have you thought about using the managers? Are you running into any runtime errors?
Thanks
-Blake Niemyjski

Fluent NHibernate DuplicateMappingException with AutoMapping

Summary:
I want to save two classes of the same name and different namespaces with the Fluent NHibernate Automapper
Context
I'm writing having to import a lot of different objects to database for testing. I'll eventually write mappers to a proper model.
I've been using code gen and Fluent NHibernate to take these DTOs and dump them straight to db.
the exception does say to (try using auto-import="false")
Code
public class ClassConvention : IClassConvention
{
public void Apply(IClassInstance instance)
{
instance.Table(instance.EntityType.Namespace.Replace(".", "_"));
}
}
namespace Sample.Models.Test1
{
public class Test
{
public virtual int Id { get; set; }
public virtual string Something { get; set; }
}
}
namespace Sample.Models.Test2
{
public class Test
{
public virtual int Id { get; set; }
public virtual string SomethingElse { get; set; }
}
}
And here's the actual app code
var model = AutoMap.AssemblyOf<Service1>()
.Where(t => t.Namespace.StartsWith("Sample.Models"))
.Conventions.AddFromAssemblyOf<Service1>();
var cfg = Fluently.Configure()
.Database(
MySQLConfiguration.Standard.ConnectionString(
c => c.Is("database=test;server=localhost;user id=root;Password=;")))
.Mappings(m => m.AutoMappings.Add(model))
.BuildConfiguration();
new SchemaExport(cfg).Execute(false, true, false);
Thanks I really appreciate any help
Update using Fluent Nhibernate RC1
solution from fluent-nhibernate forums by James Gregory
Got around to having a proper look at
this tonight. Basically, it is down to
the AutoImport stuff the exception
mentioned; when NHibernate is given
the first mapping it sees that the
entity is named with the full assembly
qualified name and creates an import
for the short name (being helpful!),
and then when you add the second one
it then complains that this import is
now going to conflict. So the solution
is to turn off the auto importing;
unfortunately, we don't have a way to
do that in the RC... I've just
commited a fix that adds in the
ability to change this in a
convention. So if you get the latest
binaries or source, you should be able
to change your Conventions line in
your attached project to do this:
.Conventions.Setup(x => {
x.AddFromAssemblyOf<Program>();
x.Add(AutoImport.Never()); });
Which adds all the conventions you've
defined in your assembly, then uses
one of the helper conventions to turn
off auto importing.
I was not able to get this to work using Conventions for FluentMappings (in contrast to AutoMappings). However, the following works for me, though it must be added to each ClassMap where needed.
public class AMap : ClassMap<A>
{
public AMap()
{
HibernateMapping.Not.AutoImport();
Map(x => x.Item, "item");
...
}
}
I am having real problem with this, and the example above or any of its variants do not help.
var cfg = new NotifyFluentNhibernateConfiguration();
return Fluently.Configure()
.Database(
FluentNHibernate.Cfg.Db.MsSqlConfiguration.MsSql2005
.ConnectionString("Server=10.2.65.227\\SOSDBSERVER;Database=NotifyTest;User ID=NHibernateTester;Password=test;Trusted_Connection=False;")
)
.Mappings(m => {
m.AutoMappings
.Add(AutoMap.AssemblyOf<SubscriptionManagerRP>(cfg));
m.FluentMappings.Conventions.Setup(x =>
{
x.AddFromAssemblyOf<Program>();
x.Add(AutoImport.Never());
});
} )
.BuildSessionFactory();
I can't find Program's reference..
I've also tried to put down a seperate xml file to in desperation config fluent nhibernate's mapping to auto-import = false with no success.
Can I please have some more extensive example on how to do this?
Edit, I got the latest trunk just weeks ago.
Edit, Solved this by removing all duplicates.
I have had the same problem. I solved it like this:
Fluently.Configure()
.Database(MsSqlConfiguration.MsSql2008
.ConnectionString(...)
.AdoNetBatchSize(500))
.Mappings(m => m.FluentMappings
.Conventions.Setup(x => x.Add(AutoImport.Never()))
.AddFromAssembly(...)
.AddFromAssembly(...)
.AddFromAssembly(...)
.AddFromAssembly(...))
;
The imported part is: .Conventions.Setup(x => x.Add(AutoImport.Never())). Everything seems to be working fine with this configuration.
Use the BeforeBindMapping event to gain access to the object representation of the .HBM XML files.
This event allows you to modify any properties at runtime before the NHibernate Session Factory is created. This also makes the FluentNHibernate-equivalent convention unnecessary. Unfortunately there is currently no official documentation around this really great feature.
Here's a global solution to duplicate mapping problems ( Just remember that all HQL queries will now need to use Fully Qualified Type names instead of just the class names ).
var configuration = new NHibernate.Cfg.Configuration();
configuration.BeforeBindMapping += (sender, args) => args.Mapping.autoimport = false;
I had to play around with where to add the convention AutoImport.Never() to. I have my persistence mapping separated into different projects - models for each application can also be found in different projects. Using it with Fluent NHibernate and auto mapping.
There are occasions when domains, well mappings really have to be combined. This would be when I need access to all domains. POCO classes used will sometimes have the same name and different namespaces, just as examples above.
Here is how my combine all mapping looks like:
internal static class NHIbernateUtility
{
public static ISessionFactory CreateSessionFactory(string connectionString)
{
return Fluently.Configure()
.Database(
MsSqlConfiguration
.MsSql2008
.ConnectionString(connectionString))
.Mappings(m => m.AutoMappings
.Add(ProjectA.NHibernate.PersistenceMapper.CreatePersistenceModel()))
.Mappings(m => m.AutoMappings
.Add(ProjectB.NHibernate.PersistenceMapper.CreatePersistenceModel()))
.Mappings(m => m.AutoMappings
.Add(ProjectC.NHibernate.PersistenceMapper.CreatePersistenceModel())).BuildSessionFactory();
}
}
And one of the persistence mappers:
public static class PersistenceMapper
{
public static AutoPersistenceModel CreatePersistenceModel()
{
return
AutoMap.AssemblyOf<Credential>(new AutoMapConfiguration())
.IgnoreBase<BaseEntity>()
.Conventions.Add(AutoImport.Never())
.Conventions.Add<TableNameConvention>()
.Conventions.Add<StandardForeignKeyConvention>()
.Conventions.Add<CascadeAllConvention>()
.Conventions.Add<StandardManyToManyTableNameConvention>()
.Conventions.Add<PropertyConvention>();
}
}
Persistence mappers are very similar for each POCO namespace - some have overrides. I had to add .Conventions.Add(AutoImport.Never()) to each persistence mapper and it works like a charm.
Just wanted to share this if anyone else is doing it this way.