Viacheslav Eremin | 1. Тест для компанії Thehouse.bg (VB, MVC 4, Razor, Linq-to-SQL)
 
 
 
(MVC) MVC (2016 год)

Декілька моїх останніх тестових проєктів.

роботодавці дуже люблять різноманітні тести для своїх майбутніх робітників. В чому загальна помилка оцінки робітника по результатам тестів - подивимося на це детальніше.



Далі, друзі, я покажу три свої останні теста, який я зробив, намагаючись знайти нормальну (не фрілансерську) роботу.

1. Тест для компанії Thehouse.bg (VB, MVC 4, Razor, Linq-to-SQL)

Тест був мною виконаний повністю, але пропозицію про постійну роботу я так і не отримав від них. У інтернеті код теста я виклав ось тут http://thehouse.vb-net.com/. Як бачите, сайт чудово працює. Цей сайт трошки нагадує мій реальний проєкт shel-auto.ru, яким я займався декілька років, тільки у реальному проєкті більше 50-ти тисяч стрічок мого коду, а тут лише декілька стрічок. Реальний проєкт, подібний до цього тесту, як по тематиці, так і по технології - я описав ось тут - Unit-тести для ASP.NET MVC.



Моя зацікавленість у цієї фірмі полягала у тому, що ці фірма розташована у тому же невеличкому місти Бургасі, де я зараз живу. І працюють вони на .NET (що рідко для Бургаса). Тому я прочитав завдання і запропонував работодавцю виконати цей тестове завдання на місці, безпосередньо після співбесіди. Я сказав, що звичайно у якості ORM я використовую Linq-to-SQL, могу і на EntityFramework. Відповідь була - це не так важливо, можете написати цій тест хоч на ADO.NET - але на MVC, не на ASP.NET Classic! Контролів DevExpress у мене вдома немає, бо це платна прога. Колись я ії використовував у якихось проєктах, здалося це нецікавим. Добре, відповів я, тоді я зроблю вам на Linq-to-SQL, MVC і найшвидши для роботи і програмування засобом. І я зробив цей тест, вислав його работодавцю, рекрутер підтвердив, що фірма отримала результат тесту, але після цього фірма з зв'язку зникла. Зробили вигляд, нібито теста мені не доручали виконати, ніякого листа щодо роботи від них я не отримав. Це кидок у чистому вигляді. Заставили мене день попрацювати безкоштовно. Тому викладаю у інтернеті і цей опис і їх завдання і його рішення.

По-перше я зробив базу. Зрозуміло, що у реальному світі базу би можливо зробити ретельніше, ну наприклад додати дати реєстрації усіх об'єктів, бани юзеров тощо. Розвивати проєкт можливо скільки завгодно. Але для тестів я я зробив саме так.


   1:  USE [TheHouse]
   2:  GO
   3:   
   4:  SET ANSI_NULLS ON
   5:  GO
   6:  SET QUOTED_IDENTIFIER ON
   7:  GO
   8:  CREATE TABLE [dbo].[User](
   9:      [id] [uniqueidentifier] NOT NULL,
  10:      [Login] [nvarchar](50) NOT NULL,
  11:      [Pass] [nvarchar](50) NOT NULL,
  12:      [IsAdmin] [int] NOT NULL,
  13:   CONSTRAINT [PK_User] PRIMARY KEY CLUSTERED 
  14:  (
  15:      [id] ASC
  16:  )WITH (PAD_INDEX  = OFF, STATISTICS_NORECOMPUTE  = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS  = ON, ALLOW_PAGE_LOCKS  = ON) ON [PRIMARY]
  17:  ) ON [PRIMARY]
  18:  GO
  19:   
  20:  SET ANSI_NULLS ON
  21:  GO
  22:  SET QUOTED_IDENTIFIER ON
  23:  GO
  24:  CREATE TABLE [dbo].[AutoBrand](
  25:      [i] [int] IDENTITY(1,1) NOT NULL,
  26:      [AutoBrandName] [nvarchar](50) NOT NULL,
  27:   CONSTRAINT [PK_AutoBrand] PRIMARY KEY CLUSTERED 
  28:  (
  29:      [i] ASC
  30:  )WITH (PAD_INDEX  = OFF, STATISTICS_NORECOMPUTE  = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS  = ON, ALLOW_PAGE_LOCKS  = ON) ON [PRIMARY]
  31:  ) ON [PRIMARY]
  32:  GO
  33:   
  34:  SET ANSI_NULLS ON
  35:  GO
  36:  SET QUOTED_IDENTIFIER ON
  37:  GO
  38:  CREATE TABLE [dbo].[ProductCategory](
  39:      [i] [int] IDENTITY(1,1) NOT NULL,
  40:      [CategoryName] [nvarchar](50) NOT NULL,
  41:   CONSTRAINT [PK_ProductCategory] PRIMARY KEY CLUSTERED 
  42:  (
  43:      [i] ASC
  44:  )WITH (PAD_INDEX  = OFF, STATISTICS_NORECOMPUTE  = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS  = ON, ALLOW_PAGE_LOCKS  = ON) ON [PRIMARY]
  45:  ) ON [PRIMARY]
  46:  GO
  47:   
  48:  SET ANSI_NULLS ON
  49:  GO
  50:  SET QUOTED_IDENTIFIER ON
  51:  GO
  52:  CREATE TABLE [dbo].[Country](
  53:      [i] [int] IDENTITY(1,1) NOT NULL,
  54:      [CountryName] [nvarchar](50) NOT NULL,
  55:   CONSTRAINT [PK_Country] PRIMARY KEY CLUSTERED 
  56:  (
  57:      [i] ASC
  58:  )WITH (PAD_INDEX  = OFF, STATISTICS_NORECOMPUTE  = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS  = ON, ALLOW_PAGE_LOCKS  = ON) ON [PRIMARY]
  59:  ) ON [PRIMARY]
  60:  GO
  61:   
  62:  SET ANSI_NULLS ON
  63:  GO
  64:  SET QUOTED_IDENTIFIER ON
  65:  GO
  66:  CREATE TABLE [dbo].[AutoModel](
  67:      [id] [uniqueidentifier] NOT NULL,
  68:      [ModelName] [nvarchar](50) NOT NULL,
  69:      [FromYear] [int] NOT NULL,
  70:      [ToYear] [int] NOT NULL,
  71:      [ToAutoBrand] [int] NOT NULL,
  72:   CONSTRAINT [PK_AutoModel] PRIMARY KEY CLUSTERED 
  73:  (
  74:      [id] ASC
  75:  )WITH (PAD_INDEX  = OFF, STATISTICS_NORECOMPUTE  = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS  = ON, ALLOW_PAGE_LOCKS  = ON) ON [PRIMARY]
  76:  ) ON [PRIMARY]
  77:  GO
  78:   
  79:  SET ANSI_NULLS ON
  80:  GO
  81:  SET QUOTED_IDENTIFIER ON
  82:  GO
  83:  CREATE TABLE [dbo].[Supplier](
  84:      [id] [uniqueidentifier] NOT NULL,
  85:      [SupplierName] [nvarchar](250) NOT NULL,
  86:      [ToCountry] [int] NOT NULL,
  87:      [SupplierAddress] [nvarchar](250) NOT NULL,
  88:      [SupplierContact] [nvarchar](250) NOT NULL,
  89:   CONSTRAINT [PK_Supplier] PRIMARY KEY CLUSTERED 
  90:  (
  91:      [id] ASC
  92:  )WITH (PAD_INDEX  = OFF, STATISTICS_NORECOMPUTE  = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS  = ON, ALLOW_PAGE_LOCKS  = ON) ON [PRIMARY]
  93:  ) ON [PRIMARY]
  94:  GO
  95:   
  96:  SET ANSI_NULLS ON
  97:  GO
  98:  SET QUOTED_IDENTIFIER ON
  99:  GO
 100:  create View [dbo].[AllAutoModel] as
 101:  select * from dbo.AutoModel join dbo.AutoBrand
 102:  on dbo.AutoModel.ToAutoBrand=dbo.AutoBrand.i
 103:  GO
 104:   
 105:  SET ANSI_NULLS ON
 106:  GO
 107:  SET QUOTED_IDENTIFIER ON
 108:  GO
 109:  CREATE TABLE [dbo].[Product](
 110:      [id] [uniqueidentifier] NOT NULL,
 111:      [Code] [nvarchar](50) NOT NULL,
 112:      [Name] [nvarchar](250) NOT NULL,
 113:      [ToCategory] [int] NOT NULL,
 114:      [PriceIN] [money] NOT NULL,
 115:      [PriceOUT] [money] NOT NULL,
 116:      [ToAutoModel] [uniqueidentifier] NOT NULL,
 117:      [ToSupplier] [uniqueidentifier] NOT NULL,
 118:   CONSTRAINT [PK_Product] PRIMARY KEY CLUSTERED 
 119:  (
 120:      [id] ASC
 121:  )WITH (PAD_INDEX  = OFF, STATISTICS_NORECOMPUTE  = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS  = ON, ALLOW_PAGE_LOCKS  = ON) ON [PRIMARY]
 122:  ) ON [PRIMARY]
 123:  GO
 124:   
 125:  SET ANSI_NULLS ON
 126:  GO
 127:  SET QUOTED_IDENTIFIER ON
 128:  GO
 129:  create View [dbo].[AllSupplier] as
 130:  select * from dbo.Supplier join dbo.Country
 131:  on dbo.Supplier.ToCountry=dbo.Country.i
 132:  GO
 133:   
 134:  SET ANSI_NULLS ON
 135:  GO
 136:  SET QUOTED_IDENTIFIER ON
 137:  GO
 138:  create View [dbo].[AllProduct] as
 139:  select dbo.Product.*,
 140:  dbo.ProductCategory.CategoryName,  
 141:  dbo.AutoModel.id as AutoModel_id,
 142:  dbo.AutoModel.ModelName,
 143:  dbo.AutoModel.FromYear,
 144:  dbo.AutoModel.ToYear,
 145:  dbo.AutoModel.ToAutoBrand,
 146:  dbo.AutoBrand.AutoBrandName,
 147:  dbo.Supplier.id as Supplier_id,
 148:  dbo.Supplier.SupplierAddress,
 149:  dbo.Supplier.SupplierContact,
 150:  dbo.Supplier.SupplierName,
 151:  dbo.Supplier.ToCountry,
 152:  dbo.Country.CountryName
 153:  from dbo.Product 
 154:  join dbo.ProductCategory on dbo.Product.ToCategory=dbo.ProductCategory.i
 155:  join dbo.AutoModel on dbo.AutoModel.id=dbo.Product.ToAutoModel
 156:  join dbo.AutoBrand on dbo.AutoModel.ToAutoBrand=dbo.AutoBrand.i
 157:  join dbo.Supplier on dbo.Supplier.id=dbo.Product.ToSupplier
 158:  join dbo.Country on dbo.Country.i=dbo.Supplier.ToCountry
 159:  GO
 160:   
 161:  ALTER TABLE [dbo].[AutoModel]  WITH CHECK ADD  CONSTRAINT [FK_AutoModel_AutoBrand] FOREIGN KEY([ToAutoBrand])
 162:  REFERENCES [dbo].[AutoBrand] ([i])
 163:  GO
 164:  ALTER TABLE [dbo].[AutoModel] CHECK CONSTRAINT [FK_AutoModel_AutoBrand]
 165:  GO
 166:   
 167:  ALTER TABLE [dbo].[Product]  WITH CHECK ADD  CONSTRAINT [FK_Product_AutoModel] FOREIGN KEY([ToAutoModel])
 168:  REFERENCES [dbo].[AutoModel] ([id])
 169:  GO
 170:  ALTER TABLE [dbo].[Product] CHECK CONSTRAINT [FK_Product_AutoModel]
 171:  GO
 172:   
 173:  ALTER TABLE [dbo].[Product]  WITH CHECK ADD  CONSTRAINT [FK_Product_ProductCategory] FOREIGN KEY([ToCategory])
 174:  REFERENCES [dbo].[ProductCategory] ([i])
 175:  GO
 176:  ALTER TABLE [dbo].[Product] CHECK CONSTRAINT [FK_Product_ProductCategory]
 177:  GO
 178:   
 179:  ALTER TABLE [dbo].[Product]  WITH CHECK ADD  CONSTRAINT [FK_Product_Supplier] FOREIGN KEY([ToSupplier])
 180:  REFERENCES [dbo].[Supplier] ([id])
 181:  GO
 182:  ALTER TABLE [dbo].[Product] CHECK CONSTRAINT [FK_Product_Supplier]
 183:  GO
 184:   
 185:  ALTER TABLE [dbo].[Supplier]  WITH CHECK ADD  CONSTRAINT [FK_Supplier_Country] FOREIGN KEY([ToCountry])
 186:  REFERENCES [dbo].[Country] ([i])
 187:  GO
 188:  ALTER TABLE [dbo].[Supplier] CHECK CONSTRAINT [FK_Supplier_Country]
 189:  GO

Цей тест я зробив на своїх улюблених технологіях MVC 4 у VS2010 у сінтаксісі RAZOR, тому я використував Linq-To-SQL, бо він і працює швидше, ніж EntityFramework і вже має в собі код репозіторі-патерна для роботи з даними.



   1:  <?xml version="1.0" encoding="utf-8"?>
   2:  <!--
   3:    For more information on how to configure your ASP.NET application, please visit
   4:    http://go.microsoft.com/fwlink/?LinkId=169433
   5:    -->
   6:   
   7:  <configuration>
   8:    <appSettings>
   9:      <!-- Этот адрес нужен для работы на разных машинах -->
  10:      <add key="LoginDomain" value="localhost"/>
  11:      <!-- Этот адрес нужен для активации логина и сброса пароля -->
  12:      <add key="HostingURL" value="localhost:59748"/>
  13:      <!--  -->
  14:      <add key="webpages:Version" value="2.0.0.0" />
  15:      <add key="webpages:Enabled" value="false" />
  16:      <add key="PreserveLoginUrl" value="true" />
  17:      <add key="ClientValidationEnabled" value="true" />
  18:      <add key="UnobtrusiveJavaScriptEnabled" value="true" />
  19:    </appSettings>
  20:    
  21:    <connectionStrings>
  22:      <add name="TheHouseConnectionString" connectionString="Data Source=XXXXXXXXX;Initial Catalog=TheHouse;User ID=YYYYYYYYYYYY;Password=ZZZZZZZZZZZZZZZ" providerName="System.Data.SqlClient" />
  23:    </connectionStrings>
  24:    
  25:    <system.web>
  26:      <customErrors mode="Off" />
  27:      <compilation debug="true" targetFramework="4.0" />
  28:      <authentication mode="Forms">
  29:        <forms loginUrl="~/Login/Index" timeout="2880" />
  30:      </authentication>
  31:      <pages>
  32:        <namespaces>
  33:          <add namespace="System.Web.Helpers" />
  34:          <add namespace="System.Web.Mvc" />
  35:          <add namespace="System.Web.Mvc.Ajax" />
  36:          <add namespace="System.Web.Mvc.Html" />
  37:          <add namespace="System.Web.Routing" />
  38:          <add namespace="System.Web.WebPages" />
  39:          <add namespace="System.Web.Optimization"/>
  40:        </namespaces>
  41:      </pages>
  42:    </system.web>

Далі я додав це два класа, бо було зрозуміло, що потрібно буде відображати дані у комбобоксах та для админкі буде потрібна аутентифікація. Зрозуміло, що в реальному світі все робиться інакше, тобто справочники вичитуються з бази один раз за весь період рециклінгу сайту, тобто раз на добу, а не при кожному реквесті. Але ж це тест, тобто просто бесплатна робота, мастер-класс для ідіотів, безкоштовне марнотратство часу. Тому тільки так. Краще тільки за гроші.



Ось код ціх класів.


   1:  Public Class PartRef
   2:   
   3:      Public Property CatList As System.Collections.Generic.List(Of Mvc.SelectListItem)
   4:      Public Property ModelList As System.Collections.Generic.List(Of Mvc.SelectListItem)
   5:      Public Property SupList As System.Collections.Generic.List(Of Mvc.SelectListItem)
   6:   
   7:      Public Sub New()
   8:          Dim db1 As New TheHouseDBDataContext
   9:          SupList = New System.Collections.Generic.List(Of Mvc.SelectListItem)
  10:          CatList = New System.Collections.Generic.List(Of Mvc.SelectListItem)
  11:          ModelList = New System.Collections.Generic.List(Of Mvc.SelectListItem)
  12:          Dim Mod1 = (From X In db1.AllAutoModels Select X).ToList
  13:          Dim Sup1 = (From X In db1.AllSuppliers Select X).ToList
  14:          Dim Cat1 = (From X In db1.ProductCategories).ToList
  15:          For Each One In Mod1
  16:              ModelList.Add(New SelectListItem With {.Value = One.id.ToString, .Text = One.ModelName, .Selected = False})
  17:          Next
  18:          For Each One In Sup1
  19:              SupList.Add(New SelectListItem With {.Value = One.id.ToString, .Text = One.SupplierName, .Selected = False})
  20:          Next
  21:          For Each One In Cat1
  22:              CatList.Add(New SelectListItem With {.Value = One.i, .Text = One.CategoryName, .Selected = False})
  23:          Next
  24:      End Sub
  25:   
  26:  End Class

   1:  Public Class AU
   2:      Shared Sub SetAU(Login As String, Remember As String)
   3:          If Remember.Contains("true") Then
   4:              Dim ticket As New FormsAuthenticationTicket(1, Login, Now, Now.AddYears(1), True, ", FormsAuthentication.FormsCookiePath)
   5:              Dim EncryptTicket As String = FormsAuthentication.Encrypt(ticket)
   6:              Dim AUCook As HttpCookie = New HttpCookie(FormsAuthentication.FormsCookieName, EncryptTicket)
   7:              AUCook.Domain = System.Configuration.ConfigurationManager.AppSettings("LoginDomain")
   8:              AUCook.Expires = Now.AddYears(1)
   9:              HttpContext.Current.Response.Cookies.Add(AUCook)
  10:          Else
  11:              FormsAuthentication.SetAuthCookie(Login, True)
  12:          End If
  13:      End Sub
  14:   
  15:      Shared Function GetCurrentUser() As Global.TheHouseVB.User
  16:          Dim db1 As New TheHouseDBDataContext
  17:          Dim User1 = (From X In db1.Users Select X Where X.Login = HttpContext.Current.User.Identity.Name).ToList
  18:          If User1.Count > 0 Then
  19:              Return User1(0)
  20:          Else
  21:              Return Nothing
  22:          End If
  23:      End Function
  24:   
  25:      Shared Sub DelAU()
  26:          FormsAuthentication.SignOut()
  27:      End Sub
  28:   
  29:  End Class

Далі я послідовно зробив усі чотири контролера, код їх занудний, коментувати тут нічого. Якщо б функції цих контролерів були реальні, зрозуміло що код потрібно було в виносити в окремий рівень, робити класи для моделей і так далі - але тут по-перше весь код по три стрічки, подруге і це забагато для безкоштовної роботи.

Мабуть едина особливість цього коду - третья стрічка наступного коду, тобто я унаслідовав Admin-контролер від User-контролеру.

   1:  Namespace TheHouseVB
   2:      Public Class AdminController
   3:          Inherits UserController
   4:   
   5:          Function Users() As ActionResult
   6:              Return View((From X In db1.Users Select X).ToList)
   7:          End Function
   8:   
   9:          Function Parts() As ActionResult
  10:              ViewData("IsAdmin") = CurrentUser.IsAdmin
  11:              Return View((From X In db1.AllProducts Select X).ToList)
  12:          End Function
  13:   
  14:          Function Suppliers() As ActionResult
  15:              Return View((From X In db1.AllSuppliers Select X).ToList)
  16:          End Function
  17:   
  18:          Function AutoModels() As ActionResult
  19:              Return View((From X In db1.AllAutoModels Select X).ToList)
  20:          End Function
  21:   
  22:          Function Categories() As ActionResult
  23:              Return View((From X In db1.ProductCategories Select X).ToList)
  24:          End Function
  25:   
  26:          Function AddNewPart() As ActionResult
  27:              Return View(New Global.TheHouseVB.PartRef)
  28:          End Function
  29:   
  30:          <HttpPost()>
  31:          Function AddNewPart(Prm As FormCollection) As ActionResult
  32:              Try
  33:                  db1.Products.InsertOnSubmit(New Product With {
  34:                  .id = Guid.NewGuid,
  35:                  .Code = Prm("code"),
  36:                  .Name = Prm("name"),
  37:                  .PriceIN = Prm("in"),
  38:                  .PriceOUT = Prm("out"),
  39:                  .ToCategory = Prm("cat"),
  40:                  .ToSupplier = Guid.Parse(Prm("sup")),
  41:                  .ToAutoModel = Guid.Parse(Prm("mod"))
  42:      })
  43:                  db1.SubmitChanges()
  44:                  Return RedirectToAction("Parts")
  45:              Catch ex As Exception
  46:                  ViewData("Err1") = ex.Message
  47:                  Return View()
  48:              End Try
  49:          End Function
  50:   
  51:          Function AddNewUser() As ActionResult
  52:              Return View()
  53:          End Function
  54:   
  55:          <HttpPost()>
  56:          Function AddNewUser(Prm As FormCollection) As ActionResult
  57:              Try
  58:                  db1.Users.InsertOnSubmit(New User With {.id = Guid.NewGuid, .IsAdmin = 0, .Login = Prm("email"), .Pass = ("pass")})
  59:                  db1.SubmitChanges()
  60:                  Return RedirectToAction("Users")
  61:              Catch ex As Exception
  62:                  ViewData("Err1") = ex.Message
  63:                  Return View()
  64:              End Try
  65:          End Function
  66:   
  67:          Function AddNewCategory() As ActionResult
  68:              Return View()
  69:          End Function
  70:   
  71:          <HttpPost()>
  72:          Function AddNewCategory(Prm As FormCollection) As ActionResult
  73:              Try
  74:                  db1.ProductCategories.InsertOnSubmit(New ProductCategory With {.CategoryName = Prm("cat")})
  75:                  db1.SubmitChanges()
  76:                  Return RedirectToAction("Categories")
  77:              Catch ex As Exception
  78:                  ViewData("Err1") = ex.Message
  79:                  Return View()
  80:              End Try
  81:          End Function
  82:   
  83:          Function AddNewModel() As ActionResult
  84:              Dim BrandsList As New System.Collections.Generic.List(Of Mvc.SelectListItem)
  85:              Dim Brands = (From X In db1.AutoBrands Select X).ToList
  86:              For Each One In Brands
  87:                  BrandsList.Add(New SelectListItem With {.Value = One.i, .Text = One.AutoBrandName, .Selected = False})
  88:              Next
  89:              Return View(BrandsList)
  90:          End Function
  91:   
  92:          <HttpPost()>
  93:          Function AddNewModel(Prm As FormCollection) As ActionResult
  94:              Try
  95:                  db1.AutoModels.InsertOnSubmit(New AutoModel With {.id = Guid.NewGuid, .FromYear = Prm("from"), .ToYear = Prm("to"), .ModelName = Prm("name"), .ToAutoBrand = Prm("brand")})
  96:                  db1.SubmitChanges()
  97:                  Return RedirectToAction("AutoModels")
  98:              Catch ex As Exception
  99:                  ViewData("Err1") = ex.Message
 100:                  Return View()
 101:              End Try
 102:          End Function
 103:   
 104:          Function AddNewSupplier() As ActionResult
 105:              Dim CountryList As New System.Collections.Generic.List(Of Mvc.SelectListItem)
 106:              Dim Country = (From X In db1.Countries Select X).ToList
 107:              For Each One In Country
 108:                  CountryList.Add(New SelectListItem With {.Value = One.i, .Text = One.CountryName, .Selected = False})
 109:              Next
 110:              Return View(CountryList)
 111:          End Function
 112:   
 113:          <HttpPost()>
 114:          Function AddNewSupplier(Prm As FormCollection) As ActionResult
 115:              Try
 116:                  db1.Suppliers.InsertOnSubmit(New Supplier With {.ToCountry = Prm("country"), .SupplierAddress = Prm("addr"), .SupplierContact = Prm("cont"), .SupplierName = Prm("name")})
 117:                  db1.SubmitChanges()
 118:                  Return RedirectToAction("Suppliers")
 119:              Catch ex As Exception
 120:                  ViewData("Err1") = ex.Message
 121:                  Return View()
 122:              End Try
 123:          End Function
 124:   
 125:          Function DelUser(ID As String) As ActionResult
 126:              Try
 127:                  Dim User1 = (From X In db1.Users Select X Where X.id.ToString = ID).ToList
 128:                  If User1.Count > 0 Then
 129:                      db1.Users.DeleteOnSubmit(User1(0))
 130:                      db1.SubmitChanges()
 131:                  End If
 132:                  Return RedirectToAction("Users")
 133:              Catch ex As Exception
 134:                  ViewData("Err1") = ex.Message
 135:                  Return View("Users", (From X In db1.Users Select X).ToList)
 136:              End Try
 137:   
 138:          End Function
 139:   
 140:          Function DelPart(ID As String) As ActionResult
 141:              Try
 142:                  Dim Prod1 = (From X In db1.Products Select X Where X.id.ToString = ID).ToList
 143:                  If Prod1.Count > 0 Then
 144:                      db1.Products.DeleteOnSubmit(Prod1(0))
 145:                      db1.SubmitChanges()
 146:                  End If
 147:                  Return RedirectToAction("Parts")
 148:              Catch ex As Exception
 149:                  ViewData("Err1") = ex.Message
 150:                  ViewData("IsAdmin") = CurrentUser.IsAdmin
 151:                  Return View("Parts", (From X In db1.AllProducts Select X).ToList)
 152:              End Try
 153:          End Function
 154:   
 155:          Function DelSuppl(ID As String) As ActionResult
 156:              Try
 157:                  Dim Sup1 = (From X In db1.Suppliers Select X Where X.id.ToString = ID).ToList
 158:                  If Sup1.Count > 0 Then
 159:                      db1.Suppliers.DeleteOnSubmit(Sup1(0))
 160:                      db1.SubmitChanges()
 161:                  End If
 162:                  Return RedirectToAction("Suppliers")
 163:              Catch ex As Exception
 164:                  ViewData("Err1") = ex.Message
 165:                  Return View("Suppliers", (From X In db1.AllSuppliers Select X).ToList)
 166:              End Try
 167:          End Function
 168:   
 169:          Function DelModel(ID As String) As ActionResult
 170:              Try
 171:                  Dim Mod1 = (From X In db1.AutoModels Select X Where X.id.ToString = ID).ToList
 172:                  If Mod1.Count > 0 Then
 173:                      db1.AutoModels.DeleteOnSubmit(Mod1(0))
 174:                      db1.SubmitChanges()
 175:                  End If
 176:                  Return RedirectToAction("AutoModels")
 177:              Catch ex As Exception
 178:                  ViewData("Err1") = ex.Message
 179:                  Return View("AutoModels", (From X In db1.AllAutoModels Select X).ToList)
 180:              End Try
 181:          End Function
 182:   
 183:          Function DelCat(ID As String) As ActionResult
 184:              Try
 185:                  Dim Cat1 = (From X In db1.ProductCategories Select X Where X.i.ToString = ID).ToList
 186:                  If Cat1.Count > 0 Then
 187:                      db1.ProductCategories.DeleteOnSubmit(Cat1(0))
 188:                      db1.SubmitChanges()
 189:                  End If
 190:                  Return RedirectToAction("Categories")
 191:              Catch ex As Exception
 192:                  ViewData("Err1") = ex.Message
 193:                  Return View("Categories", (From X In db1.ProductCategories Select X).ToList)
 194:              End Try
 195:          End Function
 196:   
 197:   
 198:      End Class
 199:  End Namespace

   1:  Namespace TheHouseVB
   2:      Public Class HomeController
   3:          Inherits System.Web.Mvc.Controller
   4:   
   5:          Public CurrentUser As Global.TheHouseVB.User
   6:   
   7:          Protected Overrides Sub OnActionExecuting(ctx As System.Web.Mvc.ActionExecutingContext)
   8:              MyBase.OnActionExecuting(ctx)
   9:              CurrentUser = AU.GetCurrentUser
  10:              If CurrentUser IsNot Nothing Then
  11:                  If CurrentUser.IsAdmin = 1 Then
  12:                      ctx.HttpContext.Response.Redirect("/Admin/Index")
  13:                  Else
  14:                      ctx.HttpContext.Response.Redirect("/Cab/Index")
  15:                  End If
  16:              End If
  17:          End Sub
  18:   
  19:          Function Index() As ActionResult
  20:              Dim db1 As New TheHouseDBDataContext
  21:              Dim Data = (From X In db1.AllProducts Select X).ToList
  22:              ViewData("IsAdmin") = 0
  23:              Return View(Data)
  24:          End Function
  25:   
  26:          Function OnePart(ID As String) As ActionResult
  27:              Dim db1 As New TheHouseDBDataContext
  28:              Dim Data = (From X In db1.AllProducts Select X Where X.id.ToString = ID).ToList
  29:              ViewData("IsAdmin") = 0
  30:              Return View(Data(0))
  31:          End Function
  32:   
  33:          Function Target() As ActionResult
  34:              Return View()
  35:          End Function
  36:   
  37:          <HttpPost()> _
  38:          Function Search(Prm As FormCollection)
  39:              ViewData("IsAdmin") = 0
  40:              Dim db1 As New TheHouseDBDataContext
  41:              Dim Data = (From X In db1.AllProducts Select X Where X.Code Like Prm("search")).ToList
  42:              Return View("Index", Data)
  43:          End Function
  44:   
  45:          Function GetProjectCode(id As String) As ActionResult
  46:              If id = "VB" Then
  47:                  Return File("~/TheHouseVB1.zip", "application/octet-stream", "TheHouseVB1.zip")
  48:              ElseIf id = "CS" Then
  49:                  Return File("~/TheHouseCS1.zip", "application/octet-stream", "TheHouseCS1.zip")
  50:              ElseIf id = "SQL" Then
  51:                  Return File("~/TheHouseSQL.zip", "application/octet-stream", "TheHouseSQL.zip")
  52:              End If
  53:          End Function
  54:   
  55:   
  56:      End Class
  57:  End Namespace

   1:  Namespace TheHouseVB
   2:      Public Class LoginController
   3:          Inherits System.Web.Mvc.Controller
   4:   
   5:          Function Index() As ActionResult
   6:              Return View()
   7:          End Function
   8:   
   9:          <HttpPost()>
  10:          Function Index(Prm As FormCollection) As ActionResult
  11:              Dim db1 As New TheHouseDBDataContext
  12:              Dim AdmUser = (From X In db1.Users Select X Where X.Login = Prm("name") And X.Pass = Prm("pass") And X.IsAdmin = 1).ToList
  13:              If AdmUser.Count > 0 Then
  14:                  AU.SetAU(Prm("name"), Prm("remember"))
  15:                  Return RedirectToAction("Index", "Admin")
  16:              End If
  17:              Dim NoAdmUser = (From X In db1.Users Select X Where X.Login = Prm("name") And X.Pass = Prm("pass") And X.IsAdmin = 0).ToList
  18:              If NoAdmUser.Count > 0 Then
  19:                  AU.SetAU(Prm("name"), Prm("remember"))
  20:                  Return RedirectToAction("Index", "User")
  21:              End If
  22:              Return View()
  23:          End Function
  24:   
  25:          Function Register() As ActionResult
  26:              Return View()
  27:          End Function
  28:   
  29:          <HttpPost()>
  30:          Function Register(Prm As FormCollection) As ActionResult
  31:              Dim db1 As New TheHouseDBDataContext
  32:              db1.Users.InsertOnSubmit(New Global.TheHouseVB.User With {.id = Guid.NewGuid, .Login = Prm("email"), .Pass = Prm("pass")})
  33:              db1.SubmitChanges()
  34:              AU.SetAU(Prm("email"), Prm("pass"))
  35:              Return RedirectToAction("Index", "User")
  36:          End Function
  37:      End Class
  38:  End Namespace

   1:  Namespace TheHouseVB
   2:      Public Class UserController
   3:          Inherits System.Web.Mvc.Controller
   4:   
   5:          Public CurrentUser As Global.TheHouseVB.User
   6:          Public db1 As TheHouseDBDataContext
   7:   
   8:          Protected Overrides Sub OnActionExecuting(ctx As System.Web.Mvc.ActionExecutingContext)
   9:              MyBase.OnActionExecuting(ctx)
  10:              db1 = New TheHouseDBDataContext
  11:              CurrentUser = AU.GetCurrentUser
  12:              If CurrentUser Is Nothing Then ctx.HttpContext.Response.Redirect("/Home/Index")
  13:          End Sub
  14:   
  15:          Function Index() As ActionResult
  16:              ViewData("UserName") = CurrentUser.Login
  17:              ViewData("IsAdmin") = CurrentUser.IsAdmin
  18:              Return View((From X In db1.AllProducts Select X).ToList)
  19:          End Function
  20:   
  21:          Function OnePart(ID As String) As ActionResult
  22:              Dim db1 As New TheHouseDBDataContext
  23:              Dim Data = (From X In db1.AllProducts Select X Where X.id.ToString = ID).ToList
  24:              ViewData("IsAdmin") = CurrentUser.IsAdmin
  25:              Return View(Data(0))
  26:          End Function
  27:   
  28:          Function LogOff() As ActionResult
  29:              AU.DelAU()
  30:              Return RedirectToAction("Index", "Home")
  31:          End Function
  32:   
  33:          <HttpPost()> _
  34:          Function Search(Prm As FormCollection)
  35:              ViewData("IsAdmin") = 0
  36:              Dim db1 As New TheHouseDBDataContext
  37:              Dim Data = (From X In db1.AllProducts Select X Where X.Code Like Prm("search")).ToList
  38:              Return View("Index", Data)
  39:          End Function
  40:   
  41:   
  42:          Function AddPart(ID As String) As ActionResult
  43:              'no code to processing user basket - it's only a demo site
  44:              Return RedirectToAction("Index")
  45:          End Function
  46:   
  47:   
  48:      End Class
  49:  End Namespace

Зрозуміло, що у реальних проєктах таких простих OnActionExecuting теж не може буте, це перший кандидат на тисячі та десятки тисяч стрічок коду.

Робота сайта починається з ініціалізації, чомусь у мене у MVC 4 на працює оптимізація JavaScript, але це не важливо, у реальних проєктах таких простих конфігів все одно не буває. Це друге місце будь-якого проєкту, яке починає розбухати до тисячів і десятків тисяч стрічок коду.



Взагалі тут і пакувати нічого, тільки CSS, я навіть jQuery додав про запас, я ним не користувався у цьому проєкті.



Ну і код усіх форм публікувати немає бажання, повністю завантажити проєкт ви можете з самого тестового сайту вони зроблені однаково. Я покажу їх на скринах.



Зрозуміло, що в реальних проєктах так не робиться. Щонайменше використовується яка-небудь CMS. Я маю декілька своїх власних CMS для подібних MVC-проєктів. Ось тут описана моя улюблена - Моя CMS для ASP.NET MVC..


2. Тест для компанії bobs.bg (C#, MVC 5, AJAX, EntityFramework)


Цей тест був значно важкий для мене, бо по-перше його потрібно будо виконати на шарпі, а по-друге у дуже обмежений час. І сама технологія булу значно важкої, порівняно з попереднім тестом - C# + AJAX + EntityFramework. До того го ж, моя студія не прочитала базу, яку вони мені дали. Із-за цієї бази я встиг зробити цей тест, але з невеличкою затримкою, десь на годину. Я пояснив їм все це з базою, вони зрозуміли, і відповіли, что тест зарахований як пройдений. Але після відповіді зникли так же точно, як і попередня фірма. Ось це завдання.

Тобто до мене у цієї тестової задачці хтось колупався, щось почав, я нібито продовжую з тієї точки, на який зупинився попередник, який намагався отримати роботу у цієї фірмі. І наступний програміст, як я розумію, почне з того місця, з якого я закінчив. Ну щось, це розумно - так можно величезний проєкт довести до кінця, при чому з великою якістю та у короткий строк!

Але справа в тому, що всі програмисти працюють трошки у різному середовищу, мені значно простіше було б почати взагалі проєкт спочатку, але у базі були дані, і тому я витратив важливу годину, поки зумів прочитати базу, який наковиряв попередній програміст.



Взагалі нова студія працює з базами якось незрозуміло, наприклад щоб відкрити базу у Server Explorer спочатку потрібно видалити LDF-файл. Але врешті-решт мені вдалося прочитати базу. На скринах нище вже моя база, знімати скрини під час тесту у мене не було часу, я був злий, бо розумів що витрачаю час марно.



Як бачите, глобально це WEB-проект на NET 4.5



   1:  <?xml version="1.0" encoding="utf-8"?>
   2:  <!--
   3:    For more information on how to configure your ASP.NET application, please visit
   4:    http://go.microsoft.com/fwlink/?LinkId=152368
   5:    -->
   6:  <configuration>
   7:    <configSections>
   8:      <!-- For more information on Entity Framework configuration, visit http://go.microsoft.com/fwlink/?LinkID=237468 -->
   9:      <section name="entityFramework" type="System.Data.Entity.Internal.ConfigFile.EntityFrameworkSection, EntityFramework, Version=5.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" requirePermission="false" />
  10:    </configSections>
  11:    <connectionStrings>
  12:      <add name="DevTestDBEntities" connectionString="metadata=res://*/Models.DevTest.csdl|res://*/Models.DevTest.ssdl|res://*/Models.DevTest.msl;provider=System.Data.SqlClient;provider connection string=&quot;data source=(LocalDB)\v11.0;attachdbfilename=|DataDirectory|\DevTestDB.mdf;integrated security=True;MultipleActiveResultSets=True;App=EntityFramework&quot;" providerName="System.Data.EntityClient" />
  13:    </connectionStrings>
  14:    <appSettings>
  15:      <add key="webpages:Version" value="2.0.0.0" />
  16:      <add key="webpages:Enabled" value="false" />
  17:      <add key="PreserveLoginUrl" value="true" />
  18:      <add key="ClientValidationEnabled" value="true" />
  19:      <add key="UnobtrusiveJavaScriptEnabled" value="true" />
  20:    </appSettings>
  21:    <system.web>
  22:      <httpRuntime targetFramework="4.5" />
  23:      <compilation debug="true" targetFramework="4.5">
  24:        <assemblies>
  25:          <add assembly="System.Data.Entity, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" />
  26:        </assemblies>
  27:      </compilation>
  28:      <authentication mode="Forms">
  29:        <forms loginUrl="~/Account/Login" timeout="2880" />
  30:      </authentication>
  31:      <pages>
  32:        <namespaces>
  33:          <add namespace="System.Web.Helpers" />
  34:          <add namespace="System.Web.Mvc" />
  35:          <add namespace="System.Web.Mvc.Ajax" />
  36:          <add namespace="System.Web.Mvc.Html" />
  37:          <add namespace="System.Web.Optimization" />
  38:          <add namespace="System.Web.Routing" />
  39:          <add namespace="System.Web.WebPages" />
  40:        </namespaces>
  41:      </pages>
  42:      <profile defaultProvider="DefaultProfileProvider">
  43:        <providers>
  44:          <add name="DefaultProfileProvider" type="System.Web.Providers.DefaultProfileProvider, System.Web.Providers, Version=1.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35" connectionStringName="DefaultConnection" applicationName="/" />
  45:        </providers>
  46:      </profile>
  47:      <membership defaultProvider="DefaultMembershipProvider">
  48:        <providers>
  49:          <add name="DefaultMembershipProvider" type="System.Web.Providers.DefaultMembershipProvider, System.Web.Providers, Version=1.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35" connectionStringName="DefaultConnection" enablePasswordRetrieval="false" enablePasswordReset="true" requiresQuestionAndAnswer="false" requiresUniqueEmail="false" maxInvalidPasswordAttempts="5" minRequiredPasswordLength="6" minRequiredNonalphanumericCharacters="0" passwordAttemptWindow="10" applicationName="/" />
  50:        </providers>
  51:      </membership>
  52:      <roleManager defaultProvider="DefaultRoleProvider">
  53:        <providers>
  54:          <add name="DefaultRoleProvider" type="System.Web.Providers.DefaultRoleProvider, System.Web.Providers, Version=1.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35" connectionStringName="DefaultConnection" applicationName="/" />
  55:        </providers>
  56:      </roleManager>
  57:      <!--
  58:              If you are deploying to a cloud environment that has multiple web server instances,
  59:              you should change session state mode from "InProc" to "Custom". In addition,
  60:              change the connection string named "DefaultConnection" to connect to an instance
  61:              of SQL Server (including SQL Azure and SQL  Compact) instead of to SQL Server Express.
  62:        -->
  63:      <sessionState mode="InProc" customProvider="DefaultSessionProvider">
  64:        <providers>
  65:          <add name="DefaultSessionProvider" type="System.Web.Providers.DefaultSessionStateProvider, System.Web.Providers, Version=1.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35" connectionStringName="DefaultConnection" />
  66:        </providers>
  67:      </sessionState>
  68:    </system.web>
  69:    <system.webServer>
  70:      <validation validateIntegratedModeConfiguration="false" />
  71:      <handlers>
  72:        <remove name="ExtensionlessUrlHandler-ISAPI-4.0_32bit" />
  73:        <remove name="ExtensionlessUrlHandler-ISAPI-4.0_64bit" />
  74:        <remove name="ExtensionlessUrlHandler-Integrated-4.0" />
  75:        <add name="ExtensionlessUrlHandler-ISAPI-4.0_32bit" path="*." verb="GET,HEAD,POST,DEBUG,PUT,DELETE,PATCH,OPTIONS" modules="IsapiModule" scriptProcessor="%windir%\Microsoft.NET\Framework\v4.0.30319\aspnet_isapi.dll" preCondition="classicMode,runtimeVersionv4.0,bitness32" responseBufferLimit="0" />
  76:        <add name="ExtensionlessUrlHandler-ISAPI-4.0_64bit" path="*." verb="GET,HEAD,POST,DEBUG,PUT,DELETE,PATCH,OPTIONS" modules="IsapiModule" scriptProcessor="%windir%\Microsoft.NET\Framework64\v4.0.30319\aspnet_isapi.dll" preCondition="classicMode,runtimeVersionv4.0,bitness64" responseBufferLimit="0" />
  77:        <add name="ExtensionlessUrlHandler-Integrated-4.0" path="*." verb="GET,HEAD,POST,DEBUG,PUT,DELETE,PATCH,OPTIONS" type="System.Web.Handlers.TransferRequestHandler" preCondition="integratedMode,runtimeVersionv4.0" />
  78:      </handlers>
  79:    </system.webServer>
  80:    <runtime>
  81:      <assemblyBinding xmlns="urn:schemas-microsoft-com:asm.v1">
  82:        <dependentAssembly>
  83:          <assemblyIdentity name="System.Web.Helpers" publicKeyToken="31bf3856ad364e35" />
  84:          <bindingRedirect oldVersion="1.0.0.0-2.0.0.0" newVersion="2.0.0.0" />
  85:        </dependentAssembly>
  86:        <dependentAssembly>
  87:          <assemblyIdentity name="System.Web.Mvc" publicKeyToken="31bf3856ad364e35" />
  88:          <bindingRedirect oldVersion="1.0.0.0-4.0.0.0" newVersion="4.0.0.0" />
  89:        </dependentAssembly>
  90:        <dependentAssembly>
  91:          <assemblyIdentity name="System.Web.WebPages" publicKeyToken="31bf3856ad364e35" />
  92:          <bindingRedirect oldVersion="1.0.0.0-2.0.0.0" newVersion="2.0.0.0" />
  93:        </dependentAssembly>
  94:        <dependentAssembly>
  95:          <assemblyIdentity name="EntityFramework" publicKeyToken="b77a5c561934e089" />
  96:          <bindingRedirect oldVersion="0.0.0.0-5.0.0.0" newVersion="5.0.0.0" />
  97:        </dependentAssembly>
  98:        <dependentAssembly>
  99:          <assemblyIdentity name="WebGrease" publicKeyToken="31bf3856ad364e35" />
 100:          <bindingRedirect oldVersion="0.0.0.0-1.3.0.0" newVersion="1.3.0.0" />
 101:        </dependentAssembly>
 102:      </assemblyBinding>
 103:    </runtime>
 104:    <entityFramework>
 105:      <defaultConnectionFactory type="System.Data.Entity.Infrastructure.LocalDbConnectionFactory, EntityFramework">
 106:        <parameters>
 107:          <parameter value="v12.0" />
 108:        </parameters>
 109:      </defaultConnectionFactory>
 110:    </entityFramework>
 111:  </configuration>

У цьому середовище BundleConfig у мене працює нормально, тому старт проекту звичайний.



Зрозуміло, що в першу чергу у цьому проєкті потрібні моделі, бо саме їх потрібно буде вводити з форм, обробляти та додавати у базу.



Я зробив модель по-модному - з атрибутами, якими вміє користуватися jQuery.Validate.

   1:  using System;
   2:  using System.Collections.Generic;
   3:  using System.Linq;
   4:  using System.Web;
   5:  using System.Web.Mvc;
   6:  using System.ComponentModel;
   7:  using System.ComponentModel.DataAnnotations;
   8:   
   9:  namespace UATP.DevTest.ViewModel
  10:  {
  11:      public class PersonAddressViewModel
  12:      {
  13:          public string Id { get; set; }
  14:   
  15:   
  16:          [Required]
  17:          [StringLength(20)]
  18:          [RegularExpression(@"^[a-zA-Z]+$", ErrorMessage = "Use letters only please")]
  19:          public string FirstName { get; set; }
  20:          [Required]
  21:          [StringLength(20)]
  22:          [RegularExpression(@"^[a-zA-Z]+$", ErrorMessage = "Use letters only please")]
  23:          public string LastName { get; set; }
  24:          [Required]
  25:          [StringLength(20)]
  26:          [RegularExpression(@"^[a-zA-Z]+$", ErrorMessage = "Use letters only please")]
  27:          public string AddressLine { get; set; }
  28:          [Required]
  29:          [StringLength(10)]
  30:          public string City { get; set; }
  31:          [Required]
  32:          [StringLength(5)]
  33:          [RegularExpression(@"^\d+", ErrorMessage = "Use number only please")]
  34:          public string PostalCode { get; set; }
  35:          public DateTime ModifiedDate { get; set; }
  36:      }
  37:  }

Далі, форма за формою я зробив всі форми, ну перші чотири форми тут взагалі нецікаві.




   1:  @model UATP.DevTest.ViewModel.PersonAddressViewModel
   2:   
   3:  <h2>Create</h2>
   4:   
   5:  @using (Html.BeginForm()) {
   6:      @Html.AntiForgeryToken()
   7:      @Html.ValidationSummary(true)
   8:   
   9:      <fieldset>
  10:          <div class="editor-label">
  11:              @Html.LabelFor(model => model.FirstName)
  12:          </div>
  13:          <div class="editor-field">
  14:              @Html.EditorFor(model => model.FirstName)
  15:              @Html.ValidationMessageFor(model => model.FirstName)
  16:          </div>
  17:   
  18:          <div class="editor-label">
  19:              @Html.LabelFor(model => model.LastName)
  20:          </div>
  21:          <div class="editor-field">
  22:              @Html.EditorFor(model => model.LastName)
  23:              @Html.ValidationMessageFor(model => model.LastName)
  24:          </div>
  25:   
  26:          <div class="editor-label">
  27:              @Html.LabelFor(model => model.AddressLine)
  28:          </div>
  29:          <div class="editor-field">
  30:              @Html.EditorFor(model => model.AddressLine)
  31:              @Html.ValidationMessageFor(model => model.AddressLine)
  32:          </div>
  33:   
  34:          <div class="editor-label">
  35:              @Html.LabelFor(model => model.City)
  36:          </div>
  37:          <div class="editor-field">
  38:              @Html.EditorFor(model => model.City)
  39:              @Html.ValidationMessageFor(model => model.City)
  40:          </div>
  41:   
  42:          <div class="editor-label">
  43:              @Html.LabelFor(model => model.PostalCode)
  44:          </div>
  45:          <div class="editor-field">
  46:              @Html.EditorFor(model => model.PostalCode)
  47:              @Html.ValidationMessageFor(model => model.PostalCode)
  48:          </div>
  49:   
  50:          <p>
  51:              <input type="submit" value="Create" />
  52:          </p>
  53:      </fieldset>
  54:  }
  55:   
  56:  <div>
  57:      @Html.ActionLink("Back to List", "Index")
  58:  </div>
  59:   
  60:  @section Scripts {
  61:      @Scripts.Render("~/bundles/jqueryval")
  62:  }

Більш-менш цікаві тут форми роботи з AJAX і скрипти. Я міг би об'єднати ці дві форми в одну, як у тести вище, но цього разу зробив дві окремі форми.



А ось і сама перлинка цього завдання - три невелички AJAX-запроса.



Зверніть увагу, що всі три запроса зроблені за допомогою jquery.unobtrusive, але перші два зроблені за допомогою існуючого хелпера, а останній - ручками. Мабуть у цьому полягала хитрість тестерів? Незрозуміло. Чи може було потрібно зробити модний хелпер з третього AJAX? Незрозуміло. Якщо я мав би трошки більше часу, я міг би і свій хелпер додати до проекту. Але так якісно - тільки за гроші.


   1:  @model IEnumerable<UATP.DevTest.Models.Person>
   2:   
   3:  Search box<br />
   4:   
   5:      <fieldset title="Search" style="text-align:right;width:160px;">
   6:          <div class="editor-field">
   7:              FirstName<br />
   8:              @Html.TextBox("FirstName")
   9:          </div>
  10:          <div class="editor-label">
  11:              LastName<br>
  12:              @Html.TextBox("LastName")
  13:          </div>
  14:          <div class="editor-field">
  15:              AddressLine<br />
  16:              @Html.TextBox("AddressLine")
  17:          </div>
  18:          <div class="editor-field">
  19:              City<br />
  20:              @Html.TextBox("City")
  21:          </div>
  22:          <div class="editor-field">
  23:              PostalCode<br />
  24:              @Html.TextBox("PostalCode")
  25:          </div>
  26:          <div class="editor-field">
  27:   
  28:              <a href="#" id="data-search" name="data-search">Search</a>
  29:              
  30:          </div>
  31:   
  32:  </fieldset>
  33:   
  34:   
  35:  <p>
  36:      @Html.ActionLink("Create New Record", "Create")
  37:   
  38:  </p>
  39:   
  40:      First 3 records
  41:      <table>
  42:          <tr>
  43:              <th>
  44:                  @Html.DisplayNameFor(model => model.FirstName)
  45:              </th>
  46:              <th>
  47:                  @Html.DisplayNameFor(model => model.LastName)
  48:              </th>
  49:              <th>
  50:                  @Html.DisplayNameFor(model => model.Address.AddressLine)
  51:              </th>
  52:              <th>
  53:                  @Html.DisplayNameFor(model => model.Address.City)
  54:              </th>
  55:              <th>
  56:                  @Html.DisplayNameFor(model => model.Address.PostalCode)
  57:              </th>
  58:              <th></th>
  59:          </tr>
  60:   
  61:          @foreach (var item in Model)
  62:          {
  63:              <tr>
  64:                  <td>
  65:                      @Html.DisplayFor(modelItem => item.FirstName)
  66:                  </td>
  67:                  <td>
  68:                      @Html.DisplayFor(modelItem => item.LastName)
  69:                  </td>
  70:                  <td>
  71:                      @Html.DisplayFor(modelItem => item.Address.AddressLine)
  72:                  </td>
  73:                  <td>
  74:                      @Html.DisplayFor(modelItem => item.Address.City)
  75:                  </td>
  76:                  <td>
  77:                      @Html.DisplayFor(modelItem => item.Address.PostalCode)
  78:                  </td>
  79:                  <td>
  80:                      @Html.ActionLink("Edit", "Edit", new { id = item.id }) |
  81:                      @Html.ActionLink("Details", "Detail", new { id = item.id }) |
  82:                      @Html.ActionLink("Delete", "Delete", new { id = item.id })
  83:                  </td>
  84:              </tr>
  85:          }
  86:   
  87:      </table>
  88:   
  89:  <br />
  90:  @Ajax.ActionLink("Show all records with name starts on A-L",
  91:                   "ShowAL",
  92:                   null,
  93:                   new AjaxOptions
  94:                   {
  95:                       HttpMethod = "GET",
  96:                       InsertionMode = InsertionMode.Replace,
  97:                       UpdateTargetId = "content",
  98:                       OnComplete = "refresh1();"
  99:                   }, new { id = "data-al" })
 100:  <br />
 101:  @Ajax.ActionLink("Show all records with name starts on M-Z",
 102:                   "ShowMZ",
 103:                   null,
 104:                   new AjaxOptions
 105:                   {
 106:                       HttpMethod = "GET",
 107:                       InsertionMode = InsertionMode.Replace,
 108:                       UpdateTargetId = "content",
 109:                       OnComplete = "refresh1();"
 110:                   }, new { id = "data-mz" })
 111:   
 112:   
 113:  <div id="content"></div>
 114:   
 115:  <script type="text/javascript">
 116:      $(document).ready(function () {
 117:          $("#data-al").click(function (e) {
 118:              e.preventDefault();
 119:          });
 120:          $("#data-mz").click(function (e) {
 121:              e.preventDefault();
 122:          });
 123:          $("#data-search").click(function (e) {
 124:              e.preventDefault();
 125:          });
 126:      });
 127:  </script>
 128:   
 129:   
 130:  <script type="text/javascript">
 131:      $(document).ready(function () {
 132:   
 133:          function refresh1() {
 134:          };
 135:   
 136:         $("#data-search").bind("click", function (e) {
 137:              e.preventDefault();
 138:              var FirstName = $("#FirstName").val();
 139:              var LastName = $("#LastName").val();
 140:              var AddressLine = $("#AddressLine").val();
 141:              var City = $("#City").val();
 142:              var PostalCode = $("#PostalCode").val();
 143:              $.ajax({
 144:                  url: '/Person/Search',
 145:                  type: 'GET',
 146:                  data: {
 147:                      "FirstName": FirstName,
 148:                      "LastName": LastName,
 149:                      "AddressLine": AddressLine,
 150:                      "City": City,
 151:                      "PostalCode": PostalCode
 152:                  },
 153:                  success: function (data) {
 154:                      $("#content").replaceWith("<div id='content'>"+data+"</div");
 155:                  }
 156:              });
 157:          });
 158:   
 159:      });
 160:  </script>
 161:   
 162:  <br /><br />

Ну і далі залишився тільки контролер, який можна було б зробити тисячами засобів. Наприклад зробити повноцінну BLL, як це робиться у 3-tier application. Но я зробив як мені було легше - тобто коду тут так мало, що його немає сенсу навіть виносити в окрему BLL.



   1:  using System;
   2:  using System.Collections.Generic;
   3:  using System.Data;
   4:  using System.Linq;
   5:  using System.Text.RegularExpressions;
   6:  using System.Web;
   7:  using System.Web.Mvc;
   8:  using UATP.DevTest.Models;
   9:  using UATP.DevTest.ViewModel;
  10:   
  11:  namespace UATP.DevTest.Controllers
  12:  {
  13:      public class PersonController : Controller
  14:      {
  15:          private DevTestDBEntities _db = new DevTestDBEntities();
  16:   
  17:          public ActionResult Index()
  18:          {
  19:              //UPDATE TO RETURN TOP 3 PEOPLE ONLY
  20:              var P = (from Y in _db.People select Y).ToList();
  21:              return View(P.Take(3));
  22:              //return View(P);
  23:          }
  24:   
  25:          public ActionResult ShowAL()
  26:          {
  27:              if (Request.IsAjaxRequest())
  28:              {
  29:                  return PartialView("AjaxView", GetPart(@"^[a-lA-L]"));
  30:              }
  31:              else return RedirectToAction("Index");
  32:          }
  33:   
  34:          public ActionResult ShowMZ()
  35:          {
  36:              if (Request.IsAjaxRequest())
  37:              {
  38:                  return PartialView("AjaxView", GetPart(@"^[m-zM-Z]"));
  39:              }
  40:              else return RedirectToAction("Index");
  41:          }
  42:   
  43:          private List<Person> GetPart(string str1)
  44:          {
  45:              var ALregex = new Regex((str1));
  46:              //var P = (from Y in _db.People where ALregex.IsMatch(Y.FirstName) select Y).ToList();
  47:              var P = (from Y in _db.People select Y).ToList();
  48:              var AL_P = new List<Person>();
  49:              foreach (var one in P)
  50:              {
  51:                  if (ALregex.IsMatch(one.FirstName))
  52:                  {
  53:                      AL_P.Add(one);
  54:                  }
  55:              }
  56:              return AL_P;
  57:          }
  58:   
  59:   
  60:          public ActionResult Search(FormCollection Prm)
  61:          {
  62:              if (true)
  63:              {
  64:   
  65:                  string FirstName = Request.QueryString["FirstName"];
  66:                  string LastName = Request.QueryString["LastName"];
  67:                  string AddressLine = Request.QueryString["AddressLine"];
  68:                  string City = Request.QueryString["City"];
  69:                  string PostalCode = Request.QueryString["PostalCode"];
  70:   
  71:                  var X = (from P in _db.People
  72:                           join A in _db.Addresses on P.AddressID equals A.id
  73:                           where (
  74:                           ((FirstName == ") ? false : P.FirstName.Contains(FirstName)) ||
  75:                           ((LastName == ") ? false : P.LastName.Contains(FirstName)) ||
  76:                           ((AddressLine == ") ? false : A.AddressLine.Contains(AddressLine)) ||
  77:                           ((City == ") ? false : A.City.Contains(City)) ||
  78:                           ((PostalCode == ") ? false : A.PostalCode.Contains(PostalCode)) 
  79:                           )
  80:                           select P
  81:                           ).ToList();
  82:   
  83:                  return PartialView("AjaxView2", X);
  84:              }
  85:              else return RedirectToAction("Index");
  86:          }
  87:   
  88:   
  89:   
  90:          public ActionResult Details(int id)
  91:          {
  92:              return View();
  93:          }
  94:   
  95:          public ActionResult Create()
  96:          {
  97:              return View();
  98:          }
  99:   
 100:          [HttpPost]
 101:          public ActionResult Create(PersonAddressViewModel newObj)
 102:          {
 103:              try
 104:              {
 105:                  return RedirectToAction("Index");
 106:              }
 107:              catch
 108:              {
 109:                  return View();
 110:              }
 111:          }
 112:   
 113:   
 114:          public ActionResult Edit(int id)
 115:          {
 116:              var X = (from P in _db.People
 117:                       join A in _db.Addresses on P.AddressID equals A.id
 118:                       where P.id == id
 119:                       select new PersonAddressViewModel
 120:                       {
 121:                           FirstName = P.FirstName,
 122:                           LastName = P.LastName,
 123:                           AddressLine = A.AddressLine,
 124:                           City = A.City,
 125:                           PostalCode = A.PostalCode,
 126:                           ModifiedDate = A.ModifiedDate.Value,
 127:                       }).ToList();
 128:              return View(X[0]);
 129:          }
 130:   
 131:          [HttpPost]
 132:          public ActionResult Edit(int id, PersonAddressViewModel newObj)
 133:          {
 134:              try
 135:              {
 136:                  var P = (from Y in _db.People
 137:                           where Y.id == id
 138:                           select Y).ToList();
 139:                  int A_id = P[0].AddressID.Value;
 140:                  var A = (from Y in _db.Addresses
 141:                           where Y.id == A_id
 142:                           select Y).ToList();
 143:   
 144:                  if (newObj.AddressLine == A[0].AddressLine &&
 145:                     newObj.City == A[0].City &&
 146:                     newObj.PostalCode == A[0].City &&
 147:                     newObj.ModifiedDate == A[0].ModifiedDate)
 148:                  {
 149:                      _db.People.Remove(P[0]);
 150:   
 151:                      _db.People.Add(new Person
 152:                      {
 153:                          FirstName = newObj.FirstName,
 154:                          LastName = newObj.LastName,
 155:                          AddressID = A[0].id
 156:                      });
 157:   
 158:                  }
 159:                  else
 160:                  {
 161:                      var A1 = new Address
 162:                      {
 163:                          AddressLine = newObj.AddressLine,
 164:                          City = newObj.City,
 165:                          ModifiedDate = DateTime.Now,
 166:                          PostalCode = newObj.PostalCode
 167:                      };
 168:                      _db.Addresses.Add(A1);
 169:   
 170:                      _db.SaveChanges();
 171:                      int lastid = A1.id;
 172:   
 173:                      _db.People.Remove(P[0]);
 174:   
 175:                      _db.People.Add(new Person
 176:                      {
 177:                          FirstName = newObj.FirstName,
 178:                          LastName = newObj.LastName,
 179:                          AddressID = lastid
 180:                      });
 181:                  }
 182:                  _db.SaveChanges();
 183:                  return RedirectToAction("Index");
 184:              }
 185:              catch (Exception ex)
 186:              {
 187:                  return View();
 188:              }
 189:          }
 190:   
 191:          public ActionResult Delete(int id, bool dummy = false)
 192:          {
 193:              var P = (from Y in _db.People
 194:                       where Y.id == id
 195:                       select Y).ToList();
 196:              _db.People.Remove(P[0]);
 197:              _db.SaveChanges();
 198:              return RedirectToAction("Index");
 199:          }
 200:   
 201:          [HttpPost]
 202:          public ActionResult Delete(int id)
 203:          {
 204:              try
 205:              {
 206:                  return RedirectToAction("Index");
 207:              }
 208:              catch
 209:              {
 210:                  return View();
 211:              }
 212:          }
 213:   
 214:          public ActionResult Detail(int id)
 215:          {
 216:              var X = (from P in _db.People
 217:                       where P.id == id
 218:                       select P).ToList();
 219:              return View(X[0]);
 220:          }
 221:   
 222:      }
 223:  }

Тестовий сайт чудово працює, вживую він викладений тут - http://spa2.vb-net.com/. Але, як і у всіх попередніх випадках, після виконання цього теста пропозиції щодо постійної роботи у цієї компанії я не отримав.




3. Тест для компанії Sigma (C#, MVC 5, AJAX, Ninject, EntityFramework, WebAPI2, DTO)

Це найскладний для мене тест, можна сказати, що я його не виконав з повним використанням усіх технологій та бібліотек, які забажав замовник. Тобто тест працює, я його виконав у декількох варіантах, він викладений у інтернеті ось тут - http://spa1.vb-net.com/ - але роботодавцю жодне з моїх рішень не сподобалось. Хм...



Глобально цей тест потребує комбінації патерну, який я описав ось тут - Застосування патерну Dependency Injection за допомогою IoC-контейнера Ninject та SPA-framework. І ще декілька фішок. Але головну частину (якщо це потрібно зробити швидко) я роблю інакше. На тих технологіях, які мені подобаються. Тому далі, я опишу лише самий перший варіант цього тесту, який я зробив швидко, за день, але далі, коли я зрозумів, що все одно я не змогу використати всі бібліотеки, і зробити це якісно та швидко - я облишив цей тест.

Найпростішим засобом цей тест робиться взагалі без WebApi та взагалі без React. Ну і тем більше, без EntityFramework та тестів. Але так (на результат!) працюють у реальному світі. А тестові завдання зроблені саме так, щоб задрочить програміста якимись технологіями, які нікому не потрібні. Ну, наприклад, вище я писав про те, що React у всьому світі використаний на 500 сайтах, це лише трохи більше сайтів, ніж рекламних агенцій, які займаються промоутінгом цєї бібліотеці. Мабуть, навіть, книжок про React випущено більше 500 (кожна величезним тиражом). Але все одно це не привело к використанню цієї бібліотеці массово.

Але, давайте подивимось спочатку, як це можна взагалі зробити найпростішим засобом на jquery.unobtrusive. Я планував спочатку зробити цей сайт якомога простішим засобом, а потім редуціювати його до потрібних у тесті технологій, але по ходу зрозумів, що все одно не встигаю це зробити швидко ... і залишив цю задачку.

Перш за все - ніяке WebApi для вирішення цієї задачки не потрібно. Все можна зробити у звичайному контролері, який взагалі, як ви можете побачити, має дві стрічки смислового коду - 46 та 24. Тому, скільки би не умовляли програмістів використувати React - зрозуміло, що кода буде ЩОНАЙМЕНШЕ У СТО РАЗІВ БІЛЬШЕ. Якщо хтось хоче заперечити цьому - викладіть будь ласка своє рішення у каментах. У мої контролерах ДВІ стрічки смислового коду (WebApi контролер взагалі повністю пустий)- а скільки кода буде у ваших контролерах?



   1:  Public Class HomeController
   2:      Inherits System.Web.Mvc.Controller
   3:   
   4:      Function Index() As ActionResult
   5:          Return View()
   6:      End Function
   7:   
   8:      Function Log() As ActionResult
   9:          Return PartialView("Log")
  10:      End Function
  11:   
  12:      Function Data() As ActionResult
  13:          Return PartialView("Data")
  14:      End Function
  15:   
  16:      Function AddData() As ActionResult
  17:          Return PartialView("AddData")
  18:      End Function
  19:   
  20:      Function DelData(id As String) As ActionResult
  21:          Try
  22:              Dim i As String = id.Replace("del", ")
  23:              Dim db1 As New GruveoDBDataContext
  24:              db1.DelProduct(i)
  25:          Catch ex As Exception
  26:              Return PartialView("Err1", ex.Message)
  27:          End Try
  28:          Return Nothing
  29:      End Function
  30:   
  31:      Function UpdData(id As String) As ActionResult
  32:          Try
  33:              '    Dim i As String = id.Replace("upd", ")
  34:              '    Dim db1 As New GruveoDBDataContext
  35:              '    db1.DelProduct(i)
  36:          Catch ex As Exception
  37:              '    Return PartialView("Err1", ex.Message)
  38:          End Try
  39:          Return Nothing
  40:      End Function
  41:   
  42:      <HttpPost()>
  43:      Function AddData(Prm As FormCollection) As ActionResult
  44:          Try
  45:              Dim db1 As New GruveoDBDataContext
  46:              db1.AddProduct(Prm("txt"), Prm("price"), Prm("descript"))
  47:          Catch ex As Exception
  48:              Return PartialView("Err1", ex.Message)
  49:          End Try
  50:          Return Nothing
  51:      End Function
  52:   
  53:  End Class

Тепер подивимося, де взагалі прихований код проєкту. Сторінка Index теж пуста, там тільки плашка, яка підгружає смислову частку сайта.



А ось меню вже має дещо! Зверніть увагу, що я описую тут свій реальній процесс роботи, зрозуміло що після цих чорновіків потрібно зробити рефакторінг, ну хоча би стилі винести у окремі файли.

Як бачите, і тут ніякого Reacta нам не потрібно, все AJAX-меню зроблено всього двома AJAX-викликами та хандлером Refresh в одну стрічку. Хто небудь може зробити це ще коротше? Покажіть мені будь ласка у каментах.



   1:  @Code
   2:      ViewData("Title") = "Start page"
   3:      Layout = Nothing
   4:  End Code
   5:  <div id="menu">
   6:   
   7:      @Ajax.ActionLink("Data", "Data", Nothing, New AjaxOptions() With {
   8:      .HttpMethod = "GET",
   9:      .InsertionMode = InsertionMode.Replace,
  10:      .UpdateTargetId = "content"
  11:      }, New With {
  12:      .id = "menudata"})
  13:   
  14:      @Ajax.ActionLink("Log", "Log", Nothing, New AjaxOptions() With {
  15:      .HttpMethod = "GET",
  16:      .InsertionMode = InsertionMode.Replace,
  17:      .UpdateTargetId = "content"
  18:      }, New With {
  19:      .id = "menulog"})
  20:   
  21:  </div>
  22:   
  23:  <style>
  24:      #menudata
  25:      {
  26:          color:Black;
  27:          width: 100px;
  28:          background-color: #d9ecf3;
  29:          margin: 5px;
  30:          font-size:large;
  31:      }
  32:      #menulog
  33:      {
  34:          color:Black;
  35:          width: 100px;
  36:          background-color: #d9ecf3;
  37:          margin: 5px;
  38:          font-size:large;
  39:      }
  40:      #adddata
  41:      {
  42:         color:Black;
  43:         font-size:large; 
  44:      }
  45:      #sendpostdata
  46:      {
  47:         color:Black;
  48:         font-size:large;
  49:      }
  50:      .deldata
  51:      {
  52:         color:Black;
  53:      }
  54:      .upddata
  55:      {
  56:         color:Black;
  57:      }
  58:      
  59:  </style>
  60:   
  61:   
  62:   
  63:  <div id="content"></div>
  64:  <div id="errpanel"></div>
  65:   
  66:  <script type="text/javascript">
  67:      $(document).ready(function () {
  68:          $("#menudata").bind("click", function () {
  69:              $("#menulog").css("background-color", "#d9ecf3");
  70:              $("#menudata").css("background-color", "#7ac0da");
  71:          });
  72:          $("#menulog").bind("click", function () {
  73:              $("#menudata").css("background-color", "#d9ecf3");
  74:              $("#menulog").css("background-color", "#7ac0da");
  75:          });
  76:          refresh1 = function () {
  77:              $.ajax({
  78:                  url: '/Home/Data',
  79:                  type: 'GET',
  80:                  success: function (e) {
  81:                      $("#content").replaceWith(e);
  82:                  }
  83:              })
  84:          };
  85:      });
  86:  </script>
  87:   
  88:   
  89:  <script type="text/javascript">
  90:      $(document).ready(function () {
  91:          $("#adddata").click(function (e) {
  92:              e.preventDefault();
  93:              $("#adddata").hide();
  94:          });
  95:      });
  96:  </script>

Далі є вьюха Log, яка крутить інклуд з одним фрагментом даних Log.



   1:  @Code
   2:          ViewData("Title") = "Log"
   3:          Layout = Nothing
   4:  End Code
   5:   
   6:  <div id="content2" style="background-color:#7ac0da;width:100%;padding: 10px;" >
   7:   
   8:  @Code
   9:      Try
  10:      @: <table border="1">
  11:      Dim db1 As New SigmaTest.GruveoDBDataContext
  12:      Dim AllLog = (From X In db1.SpaTestLogs Select X Order By X.i Descending).ToList
  13:      For i As Integer = 0 To AllLog.Count - 1
  14:          If i Mod 2 = 0 Then
  15:          @: <tr>    
  16:          Else
  17:          @: <tr style="background-color:#d9ecf3">
  18:          End If
  19:      Html.RenderPartial("OneLog", AllLog(i))
  20:          @: </tr>
  21:      Next
  22:      @: </table>     
  23:      Catch ex As Exception
  24:      @ex.Message
  25:  End Try
  26:  End Code
  27:   
  28:  </div>

   1:  <td>
   2:  @Model.OperType
   3:  </td>
   4:  <td>
   5:  @Model.date
   6:  </td>
   7:  <td>
   8:  @Model.txt
   9:  </td>
  10:  <td>
  11:  @String.Format("{0:N2}",Model.Price)
  12:  </td>
  13:  <td>
  14:  @Model.Descript
  15:  </td>

З датою трошки складніше, по-перше є вьюха, яка додає дані до бази:




   1:  <div id="datapanel">
   2:      <form id="addform" action="#" method="post">
   3:      <input type="text" name="txt" id="txt" /><span style="color: Red">*</span><br />
   4:      <input type="text" name="price" id="price" /><span style="color: Red">*</span><br />
   5:      <input type="text" name="descript" id="descript" />
   6:      </form>
   7:      <a href="#" id="sendpostdata">(send)</a><br />
   8:  </div>
   9:   
  10:  <script type="text/javascript" >
  11:   
  12:      $(document).ready(function () {
  13:          $("#sendpostdata").click(function (e) {
  14:              e.preventDefault();
  15:              var poststring = $("#addform").serialize();
  16:              var ret = '';
  17:              $.ajax({
  18:                  url: '/Home/Adddata',
  19:                  data: poststring,
  20:                  type: 'POST',
  21:                  success: function (data) {
  22:                      ret = data;
  23:                  }
  24:              });
  25:          
  26:              refresh1();
  27:              $("#datapanel").hide();
  28:              $("#adddata").show();
  29:              $("#errpanel").replaceWith(ret);
  30:          });
  31:      });
  32:  </script>
  33:   
  34:   

А по-друге, дані потрібно оновлювати, показувати, та видаляти. Це робиться вьюхой Data.



   1:  @Code
   2:      ViewData("Title") = "Data"
   3:      Layout = Nothing
   4:  End Code
   5:  <div id="content" style="background-color: #7ac0da; width: 100%; padding: 10px;">
   6:  @Code
   7:      Try
   8:      @: <table border="1">
   9:      Dim db1 As New SigmaTest.GruveoDBDataContext
  10:      Dim AllProd = (From X In db1.SpaTestProducts Select X Order By X.i Descending).ToList
  11:      For i As Integer = 0 To AllProd.Count - 1
  12:          If i Mod 2 = 0 Then
  13:          @: <tr>    
  14:          Else
  15:          @: <tr style="background-color:#d9ecf3">
  16:          End If
  17:      Html.RenderPartial("OneData", AllProd(i))
  18:          @: </tr>
  19:      Next
  20:      @: </table>     
  21:      Catch ex As Exception
  22:      @ex.Message
  23:  End Try
  24:  End Code
  25:      <br />
  26:   
  27:   
  28:      @Ajax.ActionLink("(new)", "AddData", Nothing, New AjaxOptions() With {
  29:      .HttpMethod = "GET",
  30:      .InsertionMode = InsertionMode.Replace,
  31:      .UpdateTargetId = "addplace"
  32:      }, New With {
  33:      .id = "adddata"})
  34:   
  35:      <div id="addplace"></div>
  36:   
  37:  <script type="text/javascript" >
  38:      $(document).ready(function () {
  39:          $(".deldata").bind("click", function (e) {
  40:              e.preventDefault();
  41:              var id = this.id;
  42:              $.ajax({
  43:                  url: '/Home/DelData',
  44:                  type: 'GET',
  45:                  data: { "id": id },
  46:                  success: function (data) {
  47:                      refresh1();
  48:                  }
  49:              });
  50:          });
  51:          $(".upddata").bind("click", function (e) {
  52:              e.preventDefault();
  53:              var id = this.id;
  54:              $.ajax({
  55:                  url: '/Home/UpdData',
  56:                  type: 'GET',
  57:                  data: { "id": id },
  58:                  success: function (data) {
  59:                      refresh1();
  60:                  }
  61:              });
  62:          });
  63:      });
  64:  </script>
  65:   
  66:   
  67:  </div>

   1:  <td>
   2:  <a class="deldata" id="del@(Model.i)" href="#">(del)</a>
   3:  </td>
   4:  @*<td>
   5:  <a class="upddata" id="upd@(Model.i)" href="#">(upd)</a>
   6:  </td>*@
   7:  <td>
   8:  @Model.Txt
   9:  </td>
  10:  <td>
  11:  @String.Format("{0:N2}",Model.Price)
  12:  </td>
  13:  <td>
  14:  @Model.Descript
  15:  </td>

Як бачите, все це не просто, а надзвичайно просто! Ні в яке порівняння з новими замороченими технологіями, якими нібито навмисно намагаються задрочити програмістів різноманітні рекламні агентства, поширюючи маячню (нібито це дуже модернові та сучасні технології) - ніякого порівняння зі старими, перевіреними технологіями немає.



Можливо, я ще знайду вільний час і додам на цю сторінку ще декілька тестів, які я виконав в останні часи.





Comments ( )
<00>  <01>  <02>  <03>  <04>  <05>  <06>  <07>  <08>  <09>  <10>  <11>  <12>  <13>  <14>  <15>  <16>  <17>  <18>  <19>  <20>  <21>  <22>  <23
Link to this page: //www.vb-net.com/EmployerTests/index.htm
<SITEMAP>  <MVC>  <ASP>  <NET>  <DATA>  <KIOSK>  <FLEX>  <SQL>  <NOTES>  <LINUX>  <MONO>  <FREEWARE>  <DOCS>  <ENG>  <CHAT ME>  <ABOUT ME>  < THANKS ME>