Bonjour,
Dans le cadre d'API OData nous utilisons Unity pour injecter le bon contexte de connexion à une base de données dans le constructeur de certains de nos contrôleurs ODATA.
Tout fonctionne sur nos machines de développement mais pas sur le serveur sur lequel nous déployons les API où avons le message "<Error><Message>An error has occured</Message></Error>".
Je suis certain qu'il s'agit bien d'un problème lié à Unity et non d'accès à la source de données car nous avons certains contrôleur qui n'ont pas besoins d'accéder à la base.
Sur un tel contrôleur (sans constructeur) qui ne plante pas, j'en ajoute un de la manière suivante mais qui passent en paramètre la repository mais ne l'utilise pas, et sa plante :
Code C# : Sélectionner tout - Visualiser dans une fenêtre à part
1
2
3
4 public XXXController(IODataRepository<XXX> repository) { internalRepository = repository; }
Bref, je ne suis pas familier avec Unity et mon collègue est aux abonnés absents
De ce que j'ai pu voir la registration est faite de cette manière (la méthode Register est appelé depuis le Startup)
Code C# : Sélectionner tout - Visualiser dans une fenêtre à part
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49 public static class UnityConfig { // Configuration du résolveur de dépendance public static UnityContainer Container { get; set; } public static void Register(HttpConfiguration config) { Container = new UnityContainer(); Container.RegisterType<ODataContext>( new InjectionConstructor( new ResolvedParameter(typeof(SqlConnectionStringBuilder), "builder"))); /* CA1506 (Maintenance) * Avoid excessive coupling */ RegisterCommon(); RegisterAccounting(); RegisterInternalManagement(); Container.RegisterType<ConnectionStringDataPathHandler>(); // On initialise la valeur à vide et null Container.RegisterInstance("Folder", String.Empty); Container.RegisterInstance<SqlConnectionStringBuilder>("builder", new SqlConnectionStringBuilder()); if (config != null) { config.DependencyResolver = new UnityResolver(Container); } } private static void RegisterCommon() { /* CA2000 (Reliability) * Call Dispose on object before losing scope */ RegisterType<IODataRepository<XXX>, ODataRepository<XXX>>(); .... } /* CA2000 (Reliability) * Call Dispose on object before losing scope */ [SuppressMessage("Microsoft.Reliability", "CA2000:Dispose objects before losing scope", Justification = "UnityContainer becomes responsible for the release of the manager")] private static void RegisterType<TFrom, TTo>() where TTo : TFrom { Container.RegisterType<TFrom, TTo>(new HierarchicalLifetimeManager()); } }
Il y a également une classe UnityResolver :
Code C# : Sélectionner tout - Visualiser dans une fenêtre à part
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63 public class UnityResolver : IDependencyResolver { public UnityResolver(IUnityContainer container) { if (container == null) { throw new ArgumentNullException(nameof(container)); } internalContainer = container; } public object GetService(Type serviceType) { try { return internalContainer.Resolve(serviceType); } catch (ResolutionFailedException r) { return null; } } public IEnumerable<object> GetServices(Type serviceType) { try { return internalContainer.ResolveAll(serviceType); } catch (ResolutionFailedException) { return new List<object>(); } } public IDependencyScope BeginScope() { var child = internalContainer.CreateChildContainer(); return new UnityResolver(child); } public void Dispose() { Dispose(true); GC.SuppressFinalize(this); } protected virtual void Dispose(bool disposing) { internalContainer.Dispose(); } /* CA1051 (Design) * Do not declare visble instance fields */ protected IUnityContainer Container { get { return internalContainer; } set { internalContainer = value; } } private IUnityContainer internalContainer; }
La classe ODataRepository n'a rien d'exceptionnel :
Code C# : Sélectionner tout - Visualiser dans une fenêtre à part
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61 public class ODataRepository<T> : IODataRepository<T> where T : class { #region Private Fields private readonly ODataContext _currentContext; #endregion #region Constructor public ODataRepository(ODataContext context) { _currentContext = context; } #endregion #region IODataRepository public IQueryable<T> SelectAll() { return DataSet.AsQueryable(); } public SingleResult<T> SelectByKey(String key) { PropertyInfo primaryKeyProperty = null; var properties = typeof(T).GetProperties(); foreach(var property in properties) { var attribute = property.CustomAttributes.FirstOrDefault(it => it.AttributeType.Name.Equals("KeyAttribute")); if (attribute != null) { primaryKeyProperty = property; break; } } if (primaryKeyProperty != null) { var parameter = Expression.Parameter(typeof(T)); var body = Expression.Equal(Expression.Property(parameter, primaryKeyProperty), Expression.Constant(key)); var expression = Expression.Lambda<Func<T, Boolean>>(body, parameter); return SingleResult.Create(DataSet.Where(expression).AsQueryable()); } return null; } #endregion protected IDbSet<T> DataSet { get { return _currentContext.CreateSet<T>(); } } public ODataContext Context { get { return _currentContext; } set { } } }
Bref, ça fonctionne en local même après un clean et un rebuild mais ça ne fonctionne plus une fois sur le serveur.
J'ai vérifié le Web Deploy et il contient bien les DLL de Unity.
Encore une fois, ce code n'est pas le mien et je peine à comprendre comment ça fonctionne.
D'après les tutos que j'ai lu et ce que j'en ai compris, je ne vois pas où est le problème surtout que ça fonctionne en local, ça doit donc être une question d'environnement mais quoi ?
Merci pour votre aide.
Partager