Как настроить привязки Ninject для этого сценария?

У меня есть вопрос о Ninject, но прежде чем перейти непосредственно к вопросу, я объясню общий сценарий.

У меня есть бизнес-интерфейс под названием ITest__Business и его реализация Test__Business. Этот класс зависит от 3 интерфейсов: ITest__Repository, ITest2__Repository и IConnectionUtil. Интерфейсы ITest__Repository, ITest2__Repository и IConnectionUtil имеют классы, реализующие их с именем по умолчанию (Test__Repository, Test2__Repository и ConnectionUtil соответственно).

Эти доменные классы представляют бизнес-сущности с зависимостями классов репозитория и утилитой подключения для обработки открытия и закрытия подключения к базе данных. Бизнес-класс зависит как от репозиториев, так и от утилиты подключения. Connectionutil, созданный в бизнесе, совместно используется двумя репозиториями (так что и бизнес, и репозитории обрабатывают одно соединение с БД).

Это код вышеупомянутого:

public interface ITest__Business {
    TablaUno ManageTablaUno(TablaUno tablaUno);
    IConnectionUtil ConnectionUtil {get; }
    IConnectionUtil ConnectionUtil2 {get; }
}
public class Test__Business : ITest__Business {
    private IConnectionUtil connUtil1;
    private ITest__Repository repo1;
    private ITest2__Repository repo2;
    public Test__Business(IConnectionUtil connUtil1, ITest__Repository repo1, ITest2__Repository repo2) {
        this.connUtil1 = connUtil1;
        this.repo1 = repo1;
        this.repo2 = repo2;
    }

    public TablaUno ManageTablaUno(TablaUno tablaUno){
       using (var scope = new TransactionScope()) {
           // Methods of repo1 and repo2 within transaction.
           //...
       }
    }

public interface ITest__Repository {
    IConnectionUtil ConnectionUtil { get; }
    TablaUno ManageTablaUno(TablaUno tablaUno);
}
public class Test__Repository : BaseRepository, ITest__Repository {
    public Test__Repository(IConnectionUtil connectionUtil) : base(connectionUtil) {
        // The connectionUtil is passed to the base class to retrieve the DbConnection
    }

    public TablaUno ManageTablaUno(TablaUno tablaUno) {
        // Invocation to stored procedure with the connection of the base class.
    }
}

public interface ITest2__Repository {/*Metodos propios*/}
public class Test2__Repository : BaseRepository, ITest2__Repository {
     // Logic similar to Test_Repository.
}

public interface IConnectionUtil {
    DbConnection Connection { get; }
    void Open();
    void Close();
}
public class ConnectionUtil : IConnectionUtil, IDisposable {
    private SqlConnection connection;

    public ConnectionUtil(string connStringKey) {
        var connString = WebConfigurationManager.ConnectionStrings[connStringKey].ConnectionString;
        connection = new SqlConnection(connString);
    }

    public DbConnection Connection => connection;

    public void Open() {
        try {
            connection.Open();
        } catch (Exception ex) {
            Debug.WriteLine($"Excepcion en ConnectionUtil.Open: {ex.Message}");
            throw;
        }
    }

    public void Close() {
        try {
            if (connection != null && connection.State == ConnectionState.Open) {
                connection.Close();
            }
        } catch (Exception ex) {
            Debug.WriteLine($"Excepcion en ConnectionUtil.Close: {ex.Message}");
            throw;
        }
    }

    /*Disposable Logic*/
}

И моя конфигурация модуля Ninject:

public class ConsoleModule : NinjectModule {
    public override void Load() {
        Kernel.Bind<IConnectionUtil>().To<ConnectionUtil>().InCallScope().WithConstructorArgument("connStringKey", "Test1Connection");
        Kernel.Bind<ITest__Repository>().To<Test__Repository>().InTransientScope();
        Kernel.Bind<ITest2__Repository>().To<Test2__Repository>().InTransientScope();
        Kernel.Bind<ITestDb2__Repository>().To<TestDb2__Repository>().InTransientScope();
        Kernel.Bind<ITest__Business>().To<Test__Business>().InTransientScope();
    }
}

Впоследствии был запрошен новый репозиторий (repo3) и добавлен новый connectionutil (connUtil2). ConnUtil2, созданный в бизнесе, отличается от connUtil1 (у него есть собственное подключение к другой БД), и он должен использоваться совместно с новым репозиторием (repo3). Это позволяет взаимодействовать с двумя разными базами данных.

Для этого я создал класс атрибутов ConnectionAttribute с конструктором, задающим строку подключения, которая будет считываться из файла .config. Этот атрибут необходимо добавить в connUtil2 и repo3 с новой строкой подключения «Test2Connection», указывающей, что они связаны. Repo1, repo2 и connUtil1 не добавляют этот атрибут, поэтому при решении зависимостей, если эти цели не имеют этого атрибута, будет использоваться исходная строка подключения «Test1Connection».

[AttributeUsage(AttributeTargets.Parameter, AllowMultiple = true)]
public sealed class ConnectionAttribute : Attribute {
    public string ConnectionString { get; private set; }

    public ConnectionAttribute(string connectionString) {
        ConnectionString = connectionString;
    }
}

public class Test__Business : ITest__Business {
    // Properties and methods are ignored for brevity.
    public Test__Business(IConnectionUtil connUtil1, [Connection("Test2Connection")] IConnectionUtil connUtil2,
        ITest__Repository repo1, ITest2__Repository repo2, [Connection("Test2Connection")] ITestDb2__Repository repo3) {
        this.connUtil1 = connUtil1;
        this.connUtil2 = connUtil2;
        this.repo1 = repo1;
        this.repo2 = repo2;
        this.repo3 = repo3;
    }
}

Я также обновил модуль ninject:

public class ConsoleModule : NinjectModule {
    private IList<string> scopeList = new List<string>();

    public ConsoleModule() {
        foreach (ConnectionStringSettings connstr in WebConfigurationManager.ConnectionStrings) {
            scopeList.Add(connstr.Name);
        }
    }

    //public override void Load() {
    //    Kernel.Bind<IConnectionUtil>().To<ConnectionUtil>().InCallScope().WithConstructorArgument("connStringKey", "Test1");
    //    Kernel.Bind<ITest__Repository>().To<Test__Repository>().InTransientScope();
    //    Kernel.Bind<ITest2__Repository>().To<Test2__Repository>().InTransientScope();
    //    Kernel.Bind<ITestDb2__Repository>().To<TestDb2__Repository>().InTransientScope();
    //    Kernel.Bind<ITest__Business>().To<Test__Business>().InTransientScope();
    //}

    public override void Load() {
        Kernel.Bind<IConnectionUtil>().To<ConnectionUtil>().InScope(context => {
            var scopeCadena = string.Empty;
            if (context.Request.Target.Member.DeclaringType.Name.Contains("Repository")) {
                var pr = context.Request.ParentRequest.Target.GetCustomAttributes(typeof(ConnectionAttribute), false);
                var cadena = pr.Length == 0 ? "Test1" : ((ConnectionAttribute)pr[0]).ConnectionString;
                scopeCadena = scopeList.Single(x => x == cadena);
            }

            if (context.Request.Target.Member.DeclaringType.Name.Contains("Business")) {
                var attrs = context.Request.Target.GetCustomAttributes(typeof(ConnectionAttribute), false);
                var cadena = attrs.Length == 0 ? "Test1" : ((ConnectionAttribute)attrs[0]).ConnectionString;
                scopeCadena = scopeList.Single(x => x == cadena);
            }

            return scopeCadena;
        }).WithConstructorArgument("connStringKey", context => {
            var cadena = "Test1";
            if (context.Request.Target.Member.DeclaringType.Name.Contains("Repository")) {
                var pr = context.Request.ParentRequest.Target.GetCustomAttributes(typeof(ConnectionAttribute), false);
                cadena = pr.Length == 0 ? "Test1" : ((ConnectionAttribute)pr[0]).ConnectionString;
            }

            if (context.Request.Target.Member.DeclaringType.Name.Contains("Business")) {
                var attrs = context.Request.Target.GetCustomAttributes(typeof(ConnectionAttribute), false);
                cadena = attrs.Length == 0 ? "Test1" : ((ConnectionAttribute)attrs[0]).ConnectionString;
            }

            return cadena;
        });

        Kernel.Bind<ITest__Repository>().To<Test__Repository>().InTransientScope();
        Kernel.Bind<ITestDb2__Repository>().To<TestDb2__Repository>();
        Kernel.Bind<ITest__Business>().To<Test__Business>();
    }

Кажется, это работает нормально, но проблема в том, что каждый раз, когда я вызываю Kernel.Get ‹ITest__Business›, connUtil1 и connUtil2 распределяются между бизнесом, и мне нужно, чтобы они были связаны с областью бизнеса, создавая новые connUtil1 и connUtil2 с каждым бизнес-экземпляром.

Как мне настроить ninject для этого нового случая? Пожалуйста помогите.

============================================== ====================

ОБНОВЛЕНИЕ

С другой стороны, хотя ответ на мой вопрос дан, мне было любопытно проверить, работает ли он, если добавлен новый интерфейс ITestDb3__Repository для подключения к третьей базе данных. Обновление моего конструктора бизнес-класса выглядит так:

public Test__Business(IConnectionUtil connUtil1,
    ITest__Repository repo1, ITest2__Repository repo2, 
    [Connection("Test2Connection")] IConnectionUtil connUtil2, [Connection("Test2Connection")] ITestDb2__Repository repo3, 
    [Connection("Test3Connection")] IConnectionUtil connUtil3, [Connection("Test3Connection")] ITestDb2__Repository repo4) {
    this.connUtil1 = connUtil1;
    this.connUtil2 = connUtil2;
    this.connUtil3 = connUtil3;
    this.repo1 = repo1;
    this.repo2 = repo2;
    this.repo3 = repo3;
    this.repo3 = repo4;
}

Что должно произойти, так это то, что должно быть 3 разных connectionUtils, а repo4 использует один и тот же connUtil3. Но для этого нового сценария connUtil3 равен connUtil2, так как это область действия только в том случае, если он имеет атрибут, но не его значение. Какой будет конфигурация нинжекта для этого нового сценария?

============================================== ====================

ОБНОВЛЕНИЕ №2

Что мне нужно, так это иметь способ связать репозитории с connectionutils в бизнес-классе.

Первый случай — это когда бизнесу необходимо подключиться к 2 базам данных и зависеть от 3 репозиториев, чтобы они внутренне выполняли вызов хранимых процедур. Репозитории не обрабатывают подключение, но это делается через интерфейс IConnectionUtil, который получает строку подключения. В этом случае код будет следующим:

/**** Test__Business constructor signature ****/
public Test__Business(IConnectionUtil connUtil1, ITest__Repository repo1_1, ITest2__Repository repo1_2, 
    IConnectionUtil connUtil2, ITestDb2__Repository repo2_1) { /* ... */ }

/**** Test__Business creation statements (equivalent to Kernel.Get<ITest__Business>) ****/
// repository1_1 and repository1_2 share the same connUtil1.
IConnectionUtil connUtil1 = new ConnectionUtil("TestConnection1"); // Connection string of the 1st database.
ITest__Repository repository1_1 = new Test__Repository(connUtil1);
ITest2__Repository repository1_2 = new Test2__Repository(connUtil1);

// repository2_1 shares the same connUtil2.
IConnectionUtil connUtil2 = new ConnectionUtil("TestConnection2"); // Connection string of the 2nd database.
ITestDb2__Repository repository2_1 = new TestDb2__Repository(connUtil2);

ITest__Business business = new Test__Business(connUtil1, repository1_1, repository1_2, connUtil2, repository2_1);

Второй случай: если попросить изменить бизнес так, чтобы он подключался к третьей базе данных, теперь в зависимости от нового IConnectionUtil и новых репозиториев, которые используют этот connectionUtil (предположим, что были разработаны 2 новых интерфейса репозитория, которые вызывали хранимые процедуры этого третьего). новая база данных), при этом бизнес-конструктор будет выглядеть так:

/**** Test__Business constructor signature ****/
public Test__Business(IConnectionUtil connUtil1, ITest__Repository repo1_1, ITest2__Repository repo1_2,
    IConnectionUtil connUtil2, ITestDb2__Repository repo2_1,
    IConnectionUtil connUtil3, ITestDb3_1__Repository repository3_1, ITestDb3_2__Repository repository3_2) { /* ... */}

/**** Test__Business creation statements (equivalent to Kernel.Get<ITest__Business>) ****/
// repository1_1 and repository1_2 share the same connUtil1.
IConnectionUtil connUtil1 = new ConnectionUtil("TestConnection1"); // Connection string of the 1st database.
ITest__Repository repository1_1 = new Test__Repository(connUtil1);
ITest2__Repository repository1_2 = new Test2__Repository(connUtil1);

// repository2_1 shares the same connUtil2.
IConnectionUtil connUtil2 = new ConnectionUtil("TestConnection2"); // Connection string of the 2nd database.
ITestDb2__Repository repository2_1 = new TestDb2__Repository(connUtil2);

// repository3_1 and repository3_2 share the same connUtil3.
IConnectionUtil connUtil3 = new ConnectionUtil("TestConnection3"); // Connection string of the 3rd database.
ITestDb3_1__Repository repository3_1 = new TestDb3_1__Repository(connUtil3);
ITestDb3_2__Repository repository3_2 = new TestDb3_1__Repository(connUtil3);

ITest__Business business = new Test__Business(connUtil1, repository1_1, repository1_2, connUtil2, repository2_1, connUtil3, repository3_1, repository3_2);

И так далее, если мне нужно добавить новые подключения к другим базам данных. Мне нужен способ связать в бизнес-конструкторе, какие репозитории используют один и тот же connectionutil.

ПРИМЕЧАНИЕ. Каждый репозиторий и ConnectionUtil уникальны для каждого бизнес-объекта, поэтому 2 вызова Kernel.Get должны создавать разные бизнес-объекты, репозитории и ConnectionUtils.

Пожалуйста помогите.


person Marlonchosky    schedule 10.01.2018    source источник


Ответы (2)


Проблема ясна: вы используете области, но они существуют только один раз для каждой строки подключения. Однако вам потребуется область для каждого бизнес-экземпляра для каждой строки подключения.

Итак, как насчет того, чтобы расширить вашу предыдущую привязку InScallScope(), которая, казалось, работала нормально?

Сохранив свой собственный атрибут для указания подключения, вы можете создать несколько привязок для IConnectionUtil:

Kernel.Bind<IConnectionUtil>().To<ConnectionUtil>()
    .InCallScope()
    .WithConstructorArgument("connStringKey", "Test1Connection");

Kernel.Bind<IConnectionUtil>().To<ConnectionUtil>()
    .WhenAnyAncestorMatches(HasConnectionStringAttribute)
    .InCallScope()
    .WithConstructorArgument("connStringKey", RetrieveConnectinStringFromAttribute);

Пока я оставляю вам реализацию bool HasConnectionStringAttribute(IContext context) и string RetrieveConnectinStringFromAttribute(IContext context) в качестве упражнения ;-) (не стесняйтесь обновлять ответ, чтобы сделать его полным!)

person BatteryBackupUnit    schedule 10.01.2018
comment
Большое спасибо за помощь, очень элегантный ответ. Я реализовал оба метода, и это работает для моего случая. С другой стороны, хотя ответ на мой вопрос вы уже дали мне, мне было любопытно проверить, работает ли он, если я добавлю новый интерфейс ITestDb3__Repository для подключения к третьей базе данных. Я обновил свой вопрос для этого нового сценария, не могли бы вы мне помочь? - person Marlonchosky; 10.01.2018
comment
Я понимаю, что для этого нового сценария я должен создать пользовательскую область на основе области вызова, а также для имени строки подключения. Это правильно? Но я не уверен, как это сделать - person Marlonchosky; 10.01.2018
comment
@Marlonchosky Это возможно, да. Глядя на вики, можно найти соответствующую часть, где ваше решение должно отличаться: Contrasting it to InNamedScope, one would say its a limited version of InNamedScope() that automatically adds an anonymous Named Scope to the Root object generated by the Get<T>(). т.е. вам нужно будет добавить именованную область для стандартной строки подключения и по одной для каждого настроенного диспетчера подключений. Это также потребует привязки IConnectionUtil InNamedScope(..) для каждой настроенной строки подключения. - person BatteryBackupUnit; 11.01.2018
comment
@Marlonchosky Однако, вероятно, есть лучшие альтернативные решения. Трудно рассуждать об этом, если вы не предоставите конкретный пример того, что должен делать контейнер DI, в основном вручную написав демонстрацию того, что должен делать DI (все необходимые операторы new). Это значительно облегчило бы поиск оптимального решения. - person BatteryBackupUnit; 11.01.2018
comment
Спасибо большое. Я обновил свой вопрос в конце, ниже ОБНОВЛЕНИЕ № 2 - person Marlonchosky; 12.01.2018
comment
Пожалуйста помогите. - person Marlonchosky; 13.01.2018

После долгих размышлений и изучения ответа и благодаря поддержке @BatteryBackupUnit я нашел способ создать настраиваемая область на основе InCallScope.

Проверка исходного кода InCallScope /a> я создал новый метод расширения "InCallAndConnectionStringScope".

public static class NinjectExtensions {
    public static IBindingNamedWithOrOnSyntax<T> InCallAndConnectionStringScope<T>(this IBindingInSyntax<T> syntax, Func<IContext, string> getConnString) {
        return syntax.InScope(context => {
            var connString = getConnString(context);
            var ScopeParameterName = $"NamedScopeInCallScope_{connString}";
            var rootContext = context;
            while (!IsCurrentResolveRoot(rootContext) && rootContext.Request.ParentContext != null) {
                rootContext = rootContext.Request.ParentContext;
            }

            return GetOrAddScope(rootContext, ScopeParameterName);
        });
    }

    private static bool IsCurrentResolveRoot(IContext context) {
        return context.Request.GetType().FullName == "Ninject.Extensions.ContextPreservation.ContextPreservingResolutionRoot+ContextPreservingRequest";
    }

    private static object GetOrAddScope(IContext parentContext, string scopeParameterName) {
        var namedScopeParameter = GetNamedScopeParameter(parentContext, scopeParameterName);
        if (namedScopeParameter == null) {
            namedScopeParameter = new NamedScopeParameter(scopeParameterName);
            parentContext.Parameters.Add(namedScopeParameter);
        }

        return namedScopeParameter.Scope;
    }

    private static NamedScopeParameter GetNamedScopeParameter(IContext context, string scopeParameterName) {
        return context.Parameters.OfType<NamedScopeParameter>().SingleOrDefault(parameter => parameter.Name == scopeParameterName);
    } 
}

И конфигурация привязки:

public class ConsoleCustomModule : NinjectModule {
    public override void Load() {
        Bind<IConnectionUtil>().To<ConnectionUtil>().InCallAndConnectionStringScope(GetConnectionString)
            .WithConstructorArgument(GetConnectionString);

        Bind<ITestDb2__Repository>().To<TestDb2__Repository>();
        Bind<ITest__Repository>().To<Test__Repository>();
        Bind<ITest__Business>().To<Test__Business>();
    }

    private string GetConnectionString(IContext context) {
        var cadena = "Test1Connection";
        if (context.Request.Target.Member.DeclaringType.Name.Contains("Repository")) {
            var pr = context.Request.ParentRequest.Target.GetCustomAttributes(typeof(ConnectionAttribute), false);
            cadena = pr.Length == 0 ? "Test1Connection" : ((ConnectionAttribute)pr[0]).ConnectionString;
        }
        if (context.Request.Target.Member.DeclaringType.Name.Contains("Business")) {
            var attrs = context.Request.Target.GetCustomAttributes(typeof(ConnectionAttribute), false);
            cadena = attrs.Length == 0 ? "Test1Connection" : ((ConnectionAttribute)attrs[0]).ConnectionString;
        }

        return cadena;
    }
}

Имея следующую подпись конструктора:

public Test__Business(IConnectionUtil connUtil1, ITest__Repository repo1, ITest__Repository repo1_2,
    [Connection("Test2Connection")]IConnectionUtil connUtil2, [Connection("Test2Connection")] ITest__Repository repo2, [Connection("Test2Connection")] ITestDb2__Repository repo2_2, 
    [Connection("Test3Connection")]IConnectionUtil connUtil3, [Connection("Test3Connection")]ITestDb2__Repository repo3) {
    this.connUtil1 = connUtil1;
    this.connUtil2 = connUtil2;
    this.connUtil3 = connUtil3;
    this.repo1 = repo1;
    this.repo1_2 = repo1_2;
    this.repo2 = repo2;
    this.repo2_2 = repo2_2;
    this.repo3 = repo3;
}

Вы можете проверять объекты в соответствии с необходимой областью действия с помощью следующих тестов:

[TestMethod]
public void Varias_Pruebas() {
    NinjectKernel.Init();
    var business1 = NinjectKernel.Kernel.Get<ITest__Business>();
    var business2 = NinjectKernel.Kernel.Get<ITest__Business>();

    // Different business objects.
    Assert.AreNotEqual(business1, business2);
    Assert.AreNotSame(business1, business2);

    // Different ConnectionUtil objects.
    Assert.AreNotEqual(business1.ConnectionUtil, business1.ConnectionUtil2);
    Assert.AreNotSame(business1.ConnectionUtil, business1.ConnectionUtil2);
    Assert.AreNotEqual(business1.ConnectionUtil, business1.ConnectionUtil2);
    Assert.AreNotSame(business1.ConnectionUtil, business1.ConnectionUtil2);
    Assert.AreNotEqual(business1.ConnectionUtil2, business1.ConnectionUtil3);
    Assert.AreNotSame(business1.ConnectionUtil2, business1.ConnectionUtil3);

    // Different repositories objects.
    Assert.AreNotEqual(business1.repo1, business1.repo1_2);
    Assert.AreNotSame(business1.repo1, business1.repo1_2);
    Assert.AreNotEqual(business1.repo1, business2.repo1);
    Assert.AreNotSame(business1.repo1, business2.repo1);
    Assert.AreNotEqual(business1.repo2, business1.repo2_2);
    Assert.AreNotSame(business1.repo2, business1.repo2_2);
    Assert.AreNotEqual(business1.repo2, business2.repo2);
    Assert.AreNotSame(business1.repo2, business2.repo2);

    // ConnectionUtils are shared between parameters with the same connString value of the connection attribute.
    Assert.AreEqual(business1.ConnectionUtil, business1.repo1.ConnectionUtil);
    Assert.AreSame(business1.ConnectionUtil, business1.repo1.ConnectionUtil);
    Assert.AreEqual(business1.ConnectionUtil, business1.repo1_2.ConnectionUtil);
    Assert.AreSame(business1.ConnectionUtil, business1.repo1_2.ConnectionUtil);
    Assert.AreEqual(business1.ConnectionUtil2, business1.repo2.ConnectionUtil);
    Assert.AreSame(business1.ConnectionUtil2, business1.repo2.ConnectionUtil);
    Assert.AreEqual(business1.ConnectionUtil2, business1.repo2_2.ConnectionUtil);
    Assert.AreSame(business1.ConnectionUtil2, business1.repo2_2.ConnectionUtil);
     Assert.AreEqual(business1.ConnectionUtil3, business1.repo3.ConnectionUtil);
    Assert.AreSame(business1.ConnectionUtil3, business1.repo3.ConnectionUtil);

    // No ConnectionUtils are shared between parameters with different connString value from the connection attribute.
    Assert.AreNotEqual(business1.ConnectionUtil, business1.repo2.ConnectionUtil);
    Assert.AreNotSame(business1.ConnectionUtil, business1.repo2.ConnectionUtil);
    Assert.AreNotEqual(business1.ConnectionUtil, business1.repo2_2.ConnectionUtil);
    Assert.AreNotSame(business1.ConnectionUtil, business1.repo2_2.ConnectionUtil);
    Assert.AreNotEqual(business1.ConnectionUtil2, business1.repo1.ConnectionUtil);
    Assert.AreNotSame(business1.ConnectionUtil2, business1.repo1.ConnectionUtil);
    Assert.AreNotEqual(business1.ConnectionUtil2, business1.repo1_2.ConnectionUtil);
    Assert.AreNotSame(business1.ConnectionUtil2, business1.repo1_2.ConnectionUtil);
    Assert.AreNotEqual(business1.ConnectionUtil2, business1.repo3.ConnectionUtil);
    Assert.AreNotSame(business1.ConnectionUtil2, business1.repo3.ConnectionUtil);
    Assert.AreNotEqual(business1.ConnectionUtil3, business1.repo1.ConnectionUtil);
    Assert.AreNotSame(business1.ConnectionUtil3, business1.repo1.ConnectionUtil);
    Assert.AreNotEqual(business1.ConnectionUtil3, business1.repo1_2.ConnectionUtil);
    Assert.AreNotSame(business1.ConnectionUtil3, business1.repo1_2.ConnectionUtil);
    Assert.AreNotEqual(business1.ConnectionUtil3, business1.repo2.ConnectionUtil);
    Assert.AreNotSame(business1.ConnectionUtil3, business1.repo2.ConnectionUtil);
    Assert.AreNotEqual(business1.ConnectionUtil3, business1.repo2_2.ConnectionUtil);
    Assert.AreNotSame(business1.ConnectionUtil3, business1.repo2_2.ConnectionUtil);
}
person Marlonchosky    schedule 22.01.2018