7

Piszę aplikację bazy danych przy użyciu Visual Studio 2012 z Entity Framework 5 i SQL Server 2008. Chciałbym Entity Framework podszywać się pod użytkownika SQL Server (tj. Zaloguj Się). Stworzyłem nowy konstruktor dla kontekstu DB MyDatabaseEntities, który zawiera argument nazwy użytkownika do podszywania się. Oto kod, który pisałem:Entity Framework 5 - Implementacja SQL Server "Execute As User"

public partial class MyDatabaseEntities 
{ 
    private String _impersonateUser = null; 

    public MyDatabaseEntities(String impersonateUser) 
     : base("MyConnectionString") 
    { 
     _impersonateUser = impersonateUser; 

     this.Database.Connection.StateChange += Connection_StateChange; 

    } 

    void Connection_StateChange(object sender, StateChangeEventArgs e) 
    { 
     if (e.CurrentState == ConnectionState.Open && e.OriginalState != ConnectionState.Open) 
     { 
      using (var cmd = this.Database.Connection.CreateCommand()) 
      { 
       cmd.CommandType = CommandType.Text; 

       cmd.Parameters.Add(new SqlParameter("user", _impersonateUser)); 

       cmd.CommandText = "EXECUTE AS USER = @user"; 

       cmd.ExecuteNonQuery(); 

      } 

     } 

    } 

musiałem dodać czek ...

if (e.CurrentState == ConnectionState.Open && e.OriginalState != ConnectionState.Open) 

... ponieważ metoda metoda Connection_StateChange wydaje się wykonać nawet gdy stan hasn” t zmienił się. Wtedy problemem jest to, że gdy uruchamiam kod dwukrotnie,

public void RunSimpleQuery() 
{ 
    using (MyDatabaseEntities context = new MyDatabaseEntities("UserName")) 
    { 
     var result = context.TableName.ToList(); 

    } 

} 

... Entity Framework rzuca SqlException:

poważny błąd wystąpił na bieżącej komendy. Wyniki, jeśli jakiekolwiek, powinny zostać odrzucone. \ R \ nW przypadku bieżącego polecenia wystąpił poważny błąd. Wyniki, jeśli występują, należy odrzucić.

Wszelkie pomysły?

Aktualizacja 1

ja w moim kodu powyżej, zmieniłem ...

cmd.CommandText = "EXECUTE AS USER = @user;"; 

... do ...

cmd.CommandText = "REVERT; EXECUTE AS USER = @user;"; 

... a ja wciąż uzyskać ten sam błąd SqlException.

+0

Problem polega na tym, że EF zamyka połączenie, gdy nie jest potrzebne, i zwraca je z powrotem do puli. Więc kiedy ponownie wykona jakiś SQL, żąda nowego połączenia z puli, gdzie twoje zdarzenie może nie zostać zainicjalizowane. –

+0

@LadislavMrnka Tak, zidentyfikowałeś problem. Sądzę, że będę musiał określić w łańcuchu połączenia, aby nie używać łączenia połączeń ... to wstyd. Utwórz odpowiedź na to (powiedz to, co powiedziałeś w komentarzu), aby dać ci kredyt. Dzięki! – HydroPowerDeveloper

+0

Powinieneś raczej spróbować przejąć kontrolę nad samym połączeniem (przekazując go do DbContext), ale wystąpił problem z tym: http://blogs.msdn.com/b/diego/archive/2012/01/26/exception -z-dbcontext-api-uprawnienia-połączenie-może-być-skonstruowane-z-zamkniętym-dbconnection.aspx. –

Odpowiedz

6

Problem polega na tym, że EF zamyka połączenie, gdy go nie potrzebuje i zwraca je z powrotem do puli. Więc kiedy ponownie wykona jakiś SQL, żąda nowego połączenia z puli, gdzie twoje zdarzenie może nie zostać zainicjalizowane. Ale znowu uważam, że powinieneś spróbować rozwiązać ten problem ręcznie kontrolując czas życia połączenia, aby mieć zarówno korzyści z łączenia połączeń, jak i być w stanie spełnić twoje wymagania.

+0

Jeszcze raz dziękuję za pomoc! – HydroPowerDeveloper

+0

BTW: Przyjmuję waszą radę w odniesieniu do ręcznego kontrolowania czasu życia połączenia, aby korzystać z łączenia połączeń z podszywaniem się (podany link jest bardzo pomocny). Myślę, że pójdę swoją drogą. Zaktualizuję kod i opublikuję go jako kolejną aktualizację. – HydroPowerDeveloper

1

Wiem, że to stare pytanie, ale może będzie przydatne dla kogoś.

zrobiłem w inny sposób, przy użyciu kodu ...

Zamiast

wydarzenie Connection_StateChanged

tworzę dwie metody w tej samej klasie:

public void ChangeUser(String sUser) 
    { 
     if(Database.Connection.State != ConnectionState.Open) 
      Database.Connection.Open(); 

     using (var cmd = Database.Connection.CreateCommand()) 
     { 
      cmd.CommandType = CommandType.Text; 
      cmd.Parameters.Add(new SqlParameter("user", sUser)); 
      cmd.CommandText = "EXECUTE AS USER = @user;"; 
      cmd.ExecuteNonQuery(); 
     } 
    } 

    public void Revert() 
    { 
     if (Database.Connection.State != ConnectionState.Open) 
      Database.Connection.Open(); 

     using (var cmd = Database.Connection.CreateCommand()) 
     { 
      cmd.CommandType = CommandType.Text; 
      cmd.CommandText = "REVERT;"; 
      cmd.ExecuteNonQuery(); 
     } 
    } 

Używam go przed i po wykonaniu procedury przechowywanej,

using (var db = new MyDatabaseEntities()) 
     { 
      db.ChangeUser(model.Username); 
      var result = db.Something(); 
      db.Revert(); 
      return result; 
     } 

Działa dobrze z SP i nie rzuca wyjątku nawet po wielu egzekucjach.Jeśli uda mi się złapać zdarzenie po wykonaniu polecenia, być może wszystkie zostaną uwięzione w MyDatabaseEntities.