ASP.NET?Core項目使用xUnit進(jìn)行單元測試
一、前言
在以前的.NET Framework項目中,我們也寫過一些單元測試的項目,而在ASP.NET Core 這種Web或者API應(yīng)用程序中要做單元測試是很方便的。
這篇文章主要講解如何使用xUnit對ASP.NET Core應(yīng)用程序做單元測試。.NET Core中常用的測試工具還有NUnit和MSTest。
xUnit是一個測試框架,可以針對.net/.net core項目進(jìn)行測試。測試項目需要引用被測試的項目,從而對其進(jìn)行測試。測試項目同時需要引用xUnit庫。測試編寫好后,用Test Runner來運行測試。Test Runner可以讀取測試代碼,并且會知道我們所使用的測試框架,然后執(zhí)行,并顯示結(jié)果。目前可用的Test Runner包括vs自帶的Test Explorer,或者dotnet core命令行,以及第三方工具,例如resharper等。
xUnit可以支持多種平臺的測試:
- .NET Framework
- .NET Core
- .NET Standard
- UWP
- Xamarin
二、創(chuàng)建示例項目
為了使示例項目更加的貼近真實的項目開發(fā),這里采用分層的方式創(chuàng)建一個示例項目,創(chuàng)建完成后的項目結(jié)構(gòu)如下圖所示:

下面講解一下每層的作用,按照從上往下的順序:
- TestDemo:從名字就可以看出來,這是一個單元測試的項目,針對控制器進(jìn)行測試。
- UnitTest.Data:數(shù)據(jù)訪問,封裝與EntityFrameworkCore相關(guān)的操作。
- UnitTest.IRepository:泛型倉儲接口,封裝基礎(chǔ)的增刪改查。
- UnitTest.Model:實體層,定義項目中使用到的所有實體。
- UnitTest.Repository:泛型倉儲接口實現(xiàn)層,實現(xiàn)接口里面定義的方法。
- UnitTestDemo:ASP.NET Core WebApi,提供API接口。
1、UnitTest.Model
實體層里面只有一個Student類:
using System;
using System.Collections.Generic;
using System.Text;
namespace UnitTest.Model
{
public class Student
{
public int ID { get; set; }
public string Name { get; set; }
public int Age { get; set; }
public string Gender { get; set; }
}
}2、UnitTest.Data
里面封裝與EF Core有關(guān)的操作,首先需要引入Microsoft.EntityFrameworkCore、Microsoft.EntityFrameworkCore.SqlServer、Microsoft.EntityFrameworkCore.Tools三個NuGet包,直接在管理NuGet程序包里面引入,這里不在講述。
引入相關(guān)NuGet包以后,我們創(chuàng)建數(shù)據(jù)上下文類,該類繼承自EF Core的DbContext,里面設(shè)置表名和一些屬性:
using Microsoft.EntityFrameworkCore;
using UnitTest.Model;
namespace UnitTest.Data
{
/// <summary>
/// 數(shù)據(jù)上下文類
/// </summary>
public class AppDbContext : DbContext
{
/// <summary>
/// 通過構(gòu)造函數(shù)給父類構(gòu)造傳參
/// </summary>
/// <param name="options"></param>
public AppDbContext(DbContextOptions<AppDbContext> options) : base(options)
{
}
public DbSet<Student> Students { get; set; }
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.Entity<Student>().ToTable("T_Student");
modelBuilder.Entity<Student>().HasKey(p => p.ID);
modelBuilder.Entity<Student>().Property(p => p.Name).HasMaxLength(32);
// 添加種子數(shù)據(jù)
modelBuilder.Entity<Student>().HasData(
new Student()
{
ID = 1,
Name = "測試1",
Age = 20,
Gender = "男"
},
new Student()
{
ID = 2,
Name = "測試2",
Age = 22,
Gender = "女"
},
new Student()
{
ID = 3,
Name = "測試3",
Age = 23,
Gender = "男"
});
base.OnModelCreating(modelBuilder);
}
}
}這里采用數(shù)據(jù)遷移的方式生成數(shù)據(jù)庫,需要在API項目中引入Microsoft.EntityFrameworkCore、Microsoft.EntityFrameworkCore.SqlServer、Microsoft.EntityFrameworkCore.Tools三個NuGet包。引入方式同上。
然后在API項目的appsettings.json文件里面添加數(shù)據(jù)庫鏈接字符串:
{
"Logging": {
"LogLevel": {
"Default": "Information",
"Microsoft": "Warning",
"Microsoft.Hosting.Lifetime": "Information"
}
},
"AllowedHosts": "*",
// 數(shù)據(jù)庫連接字符串
"ConnectionString": {
"DbConnection": "Initial Catalog=TestDb;User Id=sa;Password=1234;Data Source=.;Connection Timeout=10;"
}
}在JSON文件中添加完連接字符串以后,修改Startup類的ConfigureServices方法,在里面配置使用在json文件中添加的連接字符串:
// 添加數(shù)據(jù)庫連接字符串
services.AddDbContext<AppDbContext>(options =>
{
options.UseSqlServer(Configuration.GetSection("ConnectionString").GetSection("DbConnection").Value);
});這樣就可以使用數(shù)據(jù)遷移的方式生成數(shù)據(jù)庫了。
3、UnitTest.IRepository
該項目中使用泛型倉儲,定義一個泛型倉儲接口:
using System.Collections.Generic;
using System.Threading.Tasks;
namespace UnitTest.IRepository
{
public interface IRepository<T> where T:class,new()
{
Task<List<T>> GetList();
Task<int?> Add(T entity);
Task<int?> Update(T entity);
Task<int?> Delete(T entity);
}
}然后在定義IStudentRepository接口繼承自IRepository泛型接口:
using UnitTest.Model;
namespace UnitTest.IRepository
{
public interface IStudentRepository: IRepository<Student>
{
}
}4、UnitTest.Repository
這里是實現(xiàn)上面定義的倉儲接口:
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using UnitTest.Data;
using UnitTest.IRepository;
using UnitTest.Model;
namespace UnitTest.Repository
{
public class StudentRepository : IStudentRepository
{
private readonly AppDbContext _dbContext;
/// <summary>
/// 通過構(gòu)造函數(shù)實現(xiàn)依賴注入
/// </summary>
/// <param name="dbContext"></param>
public StudentRepository(AppDbContext dbContext)
{
_dbContext = dbContext;
}
public async Task<int?> Add(Student entity)
{
_dbContext.Students.Add(entity);
return await _dbContext.SaveChangesAsync();
}
public async Task<int?> Delete(Student entity)
{
_dbContext.Students.Remove(entity);
return await _dbContext.SaveChangesAsync();
}
public async Task<List<Student>> GetList()
{
List<Student> list = new List<Student>();
list = await Task.Run<List<Student>>(() =>
{
return _dbContext.Students.ToList();
});
return list;
}
public async Task<int?> Update(Student entity)
{
Student student = _dbContext.Students.Find(entity.ID);
if (student != null)
{
student.Name = entity.Name;
student.Age = entity.Age;
student.Gender = entity.Gender;
_dbContext.Entry<Student>(student).State = Microsoft.EntityFrameworkCore.EntityState.Modified;
return await _dbContext.SaveChangesAsync();
}
return 0;
}
}
}5、UnitTestDemo
先添加一個Value控制器,里面只有一個Get方法,而且沒有任何的依賴關(guān)系,先進(jìn)行最簡單的測試:
using Microsoft.AspNetCore.Mvc;
namespace UnitTestDemo.Controllers
{
[Route("api/[controller]")]
[ApiController]
public class ValueController : ControllerBase
{
[HttpGet("{id}")]
public ActionResult<string> Get(int id)
{
return $"Para is {id}";
}
}
}6、TestDemo
我們在添加測試項目的時候,直接選擇使用xUnit測試項目,如下圖所示:

這樣項目創(chuàng)建完成以后,就會自動添加xUnit的引用:
<ItemGroup>
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="16.2.0" />
<PackageReference Include="xunit" Version="2.4.1" />
<PackageReference Include="xunit.runner.visualstudio" Version="2.4.0" />
</ItemGroup>但要測試 ASP.NET Core 應(yīng)用還需要添加兩個 NuGet 包:
Install-Package Microsoft.AspNetCore.App Install-Package Microsoft.AspNetCore.TestHost
上面是使用命令的方式進(jìn)行安裝,也可以在管理NuGet程序包里面進(jìn)行搜索,然后安裝。
千萬不要忘記還要引入要測試的項目。最后的項目引入是這樣的:
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>netcoreapp3.1</TargetFramework>
<IsPackable>false</IsPackable>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Microsoft.AspNetCore.App" Version="2.2.8" />
<PackageReference Include="Microsoft.AspNetCore.TestHost" Version="3.1.2" />
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="16.2.0" />
<PackageReference Include="Newtonsoft.Json" Version="12.0.3" />
<PackageReference Include="xunit" Version="2.4.1" />
<PackageReference Include="xunit.runner.visualstudio" Version="2.4.0" />
<PackageReference Include="coverlet.collector" Version="1.0.1" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\UnitTest.Model\UnitTest.Model.csproj" />
<ProjectReference Include="..\UnitTestDemo\UnitTestDemo.csproj" />
</ItemGroup>
</Project>都添加完以后,重新編譯項目,保證生成沒有錯誤。
三、編寫單元測試
單元測試按照從上往下的順序,一般分為三個階段:
- Arrange:準(zhǔn)備階段。這個階段做一些準(zhǔn)備工作,例如創(chuàng)建對象實例,初始化數(shù)據(jù)等。
- Act:行為階段。這個階段是用準(zhǔn)備好的數(shù)據(jù)去調(diào)用要測試的方法。
- Assert:斷定階段。這個階段就是把調(diào)用目標(biāo)方法的返回值和預(yù)期的值進(jìn)行比較,如果和預(yù)期值一致則測試通過,否則測試失敗。
我們在API項目中添加了一個Value控制器,我們以Get方法作為測試目標(biāo)。一般一個單元測試方法就是一個測試用例。
我們在測試項目中添加一個ValueTest測試類,然后編寫一個單元測試方法,這里是采用模擬HTTPClient發(fā)送Http請求的方式進(jìn)行測試:
using Microsoft.AspNetCore;
using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.TestHost;
using System.Net;
using System.Net.Http;
using System.Threading.Tasks;
using UnitTestDemo;
using Xunit;
namespace TestDemo
{
public class ValueTests
{
public HttpClient _client { get; }
/// <summary>
/// 構(gòu)造方法
/// </summary>
public ValueTests()
{
var server = new TestServer(WebHost.CreateDefaultBuilder()
.UseStartup<Startup>());
_client = server.CreateClient();
}
[Fact]
public async Task GetById_ShouldBe_Ok()
{
// 1、Arrange
var id = 1;
// 2、Act
// 調(diào)用異步的Get方法
var response = await _client.GetAsync($"/api/value/{id}");
// 3、Assert
Assert.Equal(HttpStatusCode.OK, response.StatusCode);
}
}
}我們在構(gòu)造函數(shù)中,通過TestServer拿到一個HttpClient對象,用它來模擬Http請求。我們寫了一個測試用例,完整演示了單元測試的Arrange、Act和Assert三個步驟。
1、運行單元測試
單元測試用例寫好以后,打開“測試資源管理器”:

在底部就可以看到測試資源管理器了:

在要測試的方法上面右鍵,選擇“運行測試”就可以進(jìn)行測試了:

注意觀察測試方法前面圖標(biāo)的顏色,目前是藍(lán)色的,表示測試用例還沒有運行過:

測試用例結(jié)束以后,我們在測試資源管理器里面可以看到結(jié)果:

綠色表示測試通過。我們還可以看到執(zhí)行測試用例消耗的時間。
如果測試結(jié)果和預(yù)期結(jié)果一致,那么測試用例前面圖標(biāo)的顏色也會變成綠色:

如果測試結(jié)果和預(yù)期結(jié)果不一致就會顯示紅色,然后需要修改代碼直到出現(xiàn)綠色圖標(biāo)。我們修改測試用例,模擬測試失敗的情況:
using Microsoft.AspNetCore;
using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.TestHost;
using System.Net;
using System.Net.Http;
using System.Threading.Tasks;
using UnitTestDemo;
using Xunit;
namespace TestDemo
{
public class ValueTests
{
public HttpClient _client { get; }
/// <summary>
/// 構(gòu)造方法
/// </summary>
public ValueTests()
{
var server = new TestServer(WebHost.CreateDefaultBuilder()
.UseStartup<Startup>());
_client = server.CreateClient();
}
[Fact]
public async Task GetById_ShouldBe_Ok()
{
// 1、Arrange
var id = 1;
// 2、Act
// 調(diào)用異步的Get方法
var response = await _client.GetAsync($"/api/value/{id}");
//// 3、Assert
//Assert.Equal(HttpStatusCode.OK, response.StatusCode);
// 3、Assert
// 模擬測試失敗
Assert.Equal(HttpStatusCode.BadRequest, response.StatusCode);
}
}
}然后運行測試用例:

2、調(diào)試單元測試
我們也可以通過添加斷點的方式在測試用例中進(jìn)行調(diào)試。調(diào)試單元測試很簡單,只需要在要調(diào)試的方法上面右鍵選擇“調(diào)試測試”,如下圖所示:

其它操作就跟調(diào)試普通方法一樣。
除了添加斷點調(diào)試,我們還可以采用打印日志的方法來快速調(diào)試,xUnit可以很方便地做到這一點。我們修改ValueTest類:
using Microsoft.AspNetCore;
using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.TestHost;
using System.Net;
using System.Net.Http;
using System.Threading.Tasks;
using UnitTestDemo;
using Xunit;
using Xunit.Abstractions;
namespace TestDemo
{
public class ValueTests
{
public HttpClient _client { get; }
public ITestOutputHelper Output { get; }
/// <summary>
/// 構(gòu)造方法
/// </summary>
public ValueTests(ITestOutputHelper outputHelper)
{
var server = new TestServer(WebHost.CreateDefaultBuilder()
.UseStartup<Startup>());
_client = server.CreateClient();
Output = outputHelper;
}
[Fact]
public async Task GetById_ShouldBe_Ok()
{
// 1、Arrange
var id = 1;
// 2、Act
// 調(diào)用異步的Get方法
var response = await _client.GetAsync($"/api/value/{id}");
// 3、Assert
// 模擬測試失敗
//Assert.Equal(HttpStatusCode.BadRequest, response.StatusCode);
// 輸出返回信息
// Output
var responseText = await response.Content.ReadAsStringAsync();
Output.WriteLine(responseText);
// 3、Assert
Assert.Equal(HttpStatusCode.OK, response.StatusCode);
}
}
}這里我們在構(gòu)造函數(shù)中添加了 ITestOutputHelper 參數(shù),xUnit 會將一個實現(xiàn)此接口的實例注入進(jìn)來。拿到這個實例后,我們就可以用它來輸出日志了。運行(注意不是 Debug)此方法,運行結(jié)束后在測試資源管理器里面查看:

點擊就可以看到輸出的日志了:

在上面的例子中,我們是使用的簡單的Value控制器進(jìn)行測試,控制器里面沒有其他依賴關(guān)系,如果控制器里面有依賴關(guān)系該如何測試呢?方法還是一樣的,我們新建一個Student控制器,里面依賴IStudentRepository接口,代碼如下:
using System.Collections.Generic;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Mvc;
using UnitTest.IRepository;
using UnitTest.Model;
namespace UnitTestDemo.Controllers
{
[Route("api/student")]
[ApiController]
public class StudentController : ControllerBase
{
private readonly IStudentRepository _repository;
/// <summary>
/// 通過構(gòu)造函數(shù)注入
/// </summary>
/// <param name="repository"></param>
public StudentController(IStudentRepository repository)
{
_repository = repository;
}
/// <summary>
/// get方法
/// </summary>
/// <returns></returns>
[HttpGet]
public async Task<ActionResult<List<Student>>> Get()
{
return await _repository.GetList();
}
}
}然后在Startup類的ConfigureServices方法中注入:
public void ConfigureServices(IServiceCollection services)
{
// 添加數(shù)據(jù)庫連接字符串
services.AddDbContext<AppDbContext>(options =>
{
options.UseSqlServer(Configuration.GetSection("ConnectionString").GetSection("DbConnection").Value);
});
// 添加依賴注入到容器中
services.AddScoped<IStudentRepository, StudentRepository>();
services.AddControllers();
}在單元測試項目中添加StudentTest類:
using Microsoft.AspNetCore;
using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.TestHost;
using Newtonsoft.Json;
using System.Collections.Generic;
using System.Net.Http;
using System.Threading.Tasks;
using UnitTest.Model;
using UnitTestDemo;
using Xunit;
using Xunit.Abstractions;
namespace TestDemo
{
public class StudentTest
{
public HttpClient Client { get; }
public ITestOutputHelper Output { get; }
public StudentTest(ITestOutputHelper outputHelper)
{
var server = new TestServer(WebHost.CreateDefaultBuilder()
.UseStartup<Startup>());
Client = server.CreateClient();
Output = outputHelper;
}
[Fact]
public async Task Get_ShouldBe_Ok()
{
// 2、Act
var response = await Client.GetAsync($"api/student");
// Output
string context = await response.Content.ReadAsStringAsync();
Output.WriteLine(context);
List<Student> list = JsonConvert.DeserializeObject<List<Student>>(context);
// Assert
Assert.Equal(3, list.Count);
}
}
}然后運行單元測試:

可以看到,控制器里面如果有依賴關(guān)系,也是可以使用這種方式進(jìn)行測試的。
Post方法也可以使用同樣的方式進(jìn)行測試,修改控制器,添加Post方法:
using System.Collections.Generic;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Mvc;
using UnitTest.IRepository;
using UnitTest.Model;
namespace UnitTestDemo.Controllers
{
[Route("api/student")]
[ApiController]
public class StudentController : ControllerBase
{
private readonly IStudentRepository _repository;
/// <summary>
/// 通過構(gòu)造函數(shù)注入
/// </summary>
/// <param name="repository"></param>
public StudentController(IStudentRepository repository)
{
_repository = repository;
}
/// <summary>
/// get方法
/// </summary>
/// <returns></returns>
[HttpGet]
public async Task<ActionResult<List<Student>>> Get()
{
return await _repository.GetList();
}
/// <summary>
/// Post方法
/// </summary>
/// <param name="entity"></param>
/// <returns></returns>
[HttpPost]
public async Task<bool> Post([FromBody]Student entity)
{
int? result = await _repository.Add(entity);
if(result==null)
{
return false;
}
else
{
return result > 0 ? true : false;
}
}
}
}在增加一個Post的測試方法:
using Microsoft.AspNetCore;
using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.TestHost;
using Newtonsoft.Json;
using System.Collections.Generic;
using System.Net.Http;
using System.Threading.Tasks;
using UnitTest.Model;
using UnitTestDemo;
using Xunit;
using Xunit.Abstractions;
namespace TestDemo
{
public class StudentTest
{
public HttpClient Client { get; }
public ITestOutputHelper Output { get; }
public StudentTest(ITestOutputHelper outputHelper)
{
var server = new TestServer(WebHost.CreateDefaultBuilder()
.UseStartup<Startup>());
Client = server.CreateClient();
Output = outputHelper;
}
[Fact]
public async Task Get_ShouldBe_Ok()
{
// 2、Act
var response = await Client.GetAsync($"api/student");
// Output
string context = await response.Content.ReadAsStringAsync();
Output.WriteLine(context);
List<Student> list = JsonConvert.DeserializeObject<List<Student>>(context);
// Assert
Assert.Equal(3, list.Count);
}
[Fact]
public async Task Post_ShouldBe_Ok()
{
// 1、Arrange
Student entity = new Student()
{
Name="測試9",
Age=25,
Gender="男"
};
var str = JsonConvert.SerializeObject(entity);
HttpContent content = new StringContent(str);
// 2、Act
content.Headers.ContentType = new System.Net.Http.Headers.MediaTypeHeaderValue("application/json");
HttpResponseMessage response = await Client.PostAsync("api/student", content);
string responseBody = await response.Content.ReadAsStringAsync();
Output.WriteLine(responseBody);
// 3、Assert
Assert.Equal("true", responseBody);
}
}
}運行測試用例:

這樣一個簡單的單元測試就完成了。
我們觀察上面的兩個測試類,發(fā)現(xiàn)這兩個類都有一個共同的特點:都是在構(gòu)造函數(shù)里面創(chuàng)建一個HttpClient對象,我們可以把創(chuàng)建HttpClient對象抽離到一個共同的基類里面,所有的類都繼承自基類。該基類代碼如下:
using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.TestHost;
using System.IO;
using System.Net.Http;
using UnitTestDemo;
namespace TestDemo
{
/// <summary>
/// 基類
/// </summary>
public class ApiControllerTestBase
{
/// <summary>
/// 返回HttpClient對象
/// </summary>
/// <returns></returns>
protected HttpClient GetClient()
{
var builder = new WebHostBuilder()
// 指定使用當(dāng)前目錄
.UseContentRoot(Directory.GetCurrentDirectory())
// 使用Startup類作為啟動類
.UseStartup<Startup>()
// 設(shè)置使用測試環(huán)境
.UseEnvironment("Testing");
var server = new TestServer(builder);
// 創(chuàng)建HttpClient
HttpClient client = server.CreateClient();
return client;
}
}
}然后修改StudentTest類,使該類繼承自上面創(chuàng)建的基類:
using Newtonsoft.Json;
using System.Collections.Generic;
using System.Net.Http;
using System.Threading.Tasks;
using UnitTest.Model;
using Xunit;
using Xunit.Abstractions;
namespace TestDemo
{
public class StudentTest: ApiControllerTestBase
{
public HttpClient Client { get; }
public ITestOutputHelper Output { get; }
public StudentTest(ITestOutputHelper outputHelper)
{
// var server = new TestServer(WebHost.CreateDefaultBuilder()
//.UseStartup<Startup>());
// Client = server.CreateClient();
// 從父類里面獲取HttpClient對象
Client = base.GetClient();
Output = outputHelper;
}
[Fact]
public async Task Get_ShouldBe_Ok()
{
// 2、Act
var response = await Client.GetAsync($"api/student");
// Output
string context = await response.Content.ReadAsStringAsync();
Output.WriteLine(context);
List<Student> list = JsonConvert.DeserializeObject<List<Student>>(context);
// Assert
Assert.Equal(3, list.Count);
}
[Fact]
public async Task Post_ShouldBe_Ok()
{
// 1、Arrange
Student entity = new Student()
{
Name="測試9",
Age=25,
Gender="男"
};
var str = JsonConvert.SerializeObject(entity);
HttpContent content = new StringContent(str);
// 2、Act
content.Headers.ContentType = new System.Net.Http.Headers.MediaTypeHeaderValue("application/json");
HttpResponseMessage response = await Client.PostAsync("api/student", content);
string responseBody = await response.Content.ReadAsStringAsync();
Output.WriteLine(responseBody);
// 3、Assert
Assert.Equal("true", responseBody);
}
}
}文章中的示例代碼地址:https://github.com/jxl1024/UnitTest
到此這篇關(guān)于ASP.NET Core項目使用xUnit進(jìn)行單元測試的文章就介紹到這了。希望對大家的學(xué)習(xí)有所幫助,也希望大家多多支持腳本之家。
相關(guān)文章
.NET使用StackTrace獲取方法調(diào)用信息的代碼演示
StackTrace, 位于 System.Diagnostics 命名空間下,名字很直觀,它代表一個方法調(diào)用的跟蹤堆棧,里面存放著按順序排列的棧幀對象(StackFrame),每當(dāng)發(fā)生一次調(diào)用,就會壓入一個棧幀,這篇文章主要介紹了.NET使用StackTrace獲取方法調(diào)用信息,需要的朋友可以參考下2022-09-09
XslTransform.Transform將結(jié)果輸出到字符串里的方法
XslTransform.Transform將結(jié)果輸出到字符串里的方法...2007-04-04
asp.net動態(tài)添加js文件調(diào)用到網(wǎng)頁的方法
這篇文章主要介紹了asp.net動態(tài)添加js文件調(diào)用到網(wǎng)頁的方法,涉及asp.net動態(tài)添加js的相關(guān)技巧,具有一定參考借鑒價值,需要的朋友可以參考下2015-04-04
asp.net中關(guān)于dropdwonlist無法獲得值問題
用dropdwonlist綁定了一個數(shù)據(jù)源做選擇,但是當(dāng)提交時,用控件屬性無法獲得相應(yīng)的值,打印出來每次都是顯示的第一個值2011-11-11

